RailsのAssetsをGzipで提供する

# TL;DR

```nginx
server {
  # ...省略
  gzip on;
  gzip_types image/gif image/png image/jpeg; # Railsは画像を圧縮しないので、欲しいであれば自分で圧縮すること

  # ...省略
  location /assets {
    root   /usr/share/rails/app/assets;
    location ~ .*.(js|css|html|svg)+$ {
      gzip_static on;
      # gunzip on; # Railsの`public/assets`に圧縮してないファイルもあるので省略
    }
  }
```

# 目的
最近Googleの[Page Speed Insights](https://developers.google.com/speed/pagespeed/insights/)が遅い遅いとつっこまれたので、調べてみたら、**「テキスト圧縮の有効化」**が効いてないと指摘されて、その対策を探るために調査を行いました。

# 現象
Nginx Gzipで検索したら、たくさん結果が出て、そのほとんどは、下記の構文で、Nginx自分の機能で圧縮を行う手法を提案しています。

```nginx
server {
  # ...省略
  gzip on;
  gzip_types text/css application/javascript application/json;
  gzip_types application/font-woff application/font-tff;
  gzip_types image/gif image/png image/jpeg;
  gzip_types application/octet-stream;
  # ...省略
}
```

この手法にはいくつか欠点があります。まず、アクセスする度に、Nginxを使ってアセットのGzipを行うので、サーバーのCPUを大量に使います。そして、NginxはデフォルトSVGのmime typeを認識しないため、SVGの圧縮を行いたい場合は別途設定が必要でした。

また、`rake assets:precompile`が実行される際のログを見てみると、ちゃんと.gzファイルも出しているのをわかりました。なぜかNginxこれら.gzファイルを認識していなく、圧縮前のファイルだけ提供しています。


# 解決策
そこで見つかったのはNginxの`gzip_static`の設定でした。その設定を`on`にすると、リクエストにgzipしたアセットを優先的に提供していて、もしgzipを明示的に不要とした場合、圧縮前のアセットを提供するしようでした。

ちなみに、もしgzip済のアセットのみ存在する場合、`gunzip on`の設定を追加すると、gzip不要なクライアントに対して、Nginxが一回gzipしたアセットを解凍し、提供することになります。

ただし、Railsの`assets:precompile`は画像ファイルを圧縮しないため、もしどうしても画像ファイルもgzipとして提供したい場合、おとなしく`gzip_types image/gif image/png image/jpeg`を設定しましょう。

また、Nginxはデフォルトでhtmlファイルをgzipしているので、ここでの設定は不要です。

# 余談: [Page Speed Insights](https://developers.google.com/speed/pagespeed/insights/)点数を改善するための開発手法

## 施策まで
施策をする前に、点数は非常に低かったです。
![PageSpeed_Insights_before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87438/f668bf38-799d-a507-bd15-70b733b062e0.png)

Googleさんが指摘した問題点を分析すると、**「JSの読み込みが遅い」**と**「アセットが圧縮されていない」**が大きくペナルティを食らったことがわかりました。

JSの読み込みが遅い問題に対しては、[scriptタグにasyncやdeferを付ける](https://qiita.com/phanect/items/82c85ea4b8f9c373d684)ことで解決できます。ただし、JSの読み込み順が変わってしまうので、細かく動作確認が必要です。

アセットが圧縮されていない問題は上記のように、Nginxでの施策で対処できます。

## 効果確認
ここで効果確認するために、production環境のDockerを作って、NginxのDocker、そしてデータベースのDockerと繋いて、[Ngrok](https://ngrok.com/)などプロキシでフォワーディングして、テストをしました。

![PageSpeed_Insights_ngrok.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87438/b27ed010-23ae-caae-bdfd-330f667325ab.png)

対策した結果、だいぶ点数が上がりました。残りの問題点を調べると、**TTFB**が遅いと指摘されました。それはおそらくローカル環境なので、DBが重いのが原因でした。

## 結果
対策して、本番リリースしたら、より点数がアップしました。やはりTTFBの遅さはローカル環境のせいでした。

![PageSpeed_Insights_after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87438/8f510ac9-ba05-f9f2-4d1b-60849cb42c82.png)

ちなみに、Page Speed Insightsの「モバイル」の方は、回線速度や機器のスペクトルなど、かなりペナルティを課しているそうです。PCだと点数が全く違います。
![PageSpeed_Insights_after_pc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87438/c9d9c872-7752-f44a-9615-eb354dacbb91.png)



# 参考
https://qiita.com/RyoMa_0923/items/55078f6fb57e9d70a37f
https://gist.github.com/equivalent/9352734#file-nginx-on-ruby-on-rails-with-unicorn-and-gzip-assets-L35
https://qiita.com/cubicdaiya/items/2763ba2240476ab1d9dd
https://stackoverflow.com/questions/3972675/how-to-tell-gzip-static-not-to-look-for-image-files
https://qiita.com/yuuna/items/9a2954300a130a9637b8
https://guides.wp-bullet.com/enable-svg-gzip-compression-nginx/