【解決】nginx で gzip 圧縮がされない問題

f:id:kizashi1122:20201222093127p:plain

id:kizashi1122 です。 2020年ももう終わりですね。今年最後のエントリです。今年も色々ありました。

その色々あった中で nginx がらみで面白いことがあったのでシェアします。

弊社サービス Re:lation では Ruby on Rails を採用しており、AWS 上で稼働させています。 フロントには ALB をおいて、nginx をはさみ、unicorn が動いています。nginx は静的ファイルへのアクセスやHTTPのリダイレクト処理、固定ヘッダの追加など静的な処理を任せています。

nginx に任せている処理の中に gzip 圧縮があります。これは動的生成コンテンツも圧縮することができ、特にレスポンスがテキストの場合などは圧縮率が高くなり転送サイズはぐっと小さくなります。

現在はほとんどのサービスで gzip 圧縮が使われてると思います。

HTTP リクエストヘッダ

ブラウザがサポートしている圧縮アルゴリズムがあればリクエストヘッダに以下のように設定されます。 br というのは、Brotli というアルゴリズムで圧縮率が高いようです。

Accept-Encoding: gzip, deflate, br

HTTP レスポンスヘッダ

そして、圧縮をサポートしているならば、サーバー側は圧縮してもよいので圧縮して送ります。

content-encoding: gzip

圧縮されたレスポンスのヘッダはこんな感じになります。

nginx の設定で gzip 圧縮を有効にしたい場合は設定ファイルにこのような記述をします(最低限)。

  gzip              on;
  gzip_types        application/json text/plain text/css application/x-javascript text/xml application/xml application/rss+xml text/javascript application/javascript image/x-icon;

text/html は指定しなくてもデフォで圧縮がかかるようです。

さて

とある環境からどうも遅く感じるという報告があり、パフォーマンスのボトルネックを探ってたときにある状況を発見しました。 (そのとある環境からの問題はまだ解決してないのですが)

レスポンスによっては gzip 圧縮が効いてないようなのです。

そのリクエストは POST でした。ははーん、 POST だと効かないのか? いやそうではないようだ。ちゃんと POST のレスポンスでも圧縮されている。

なにが違うのか?

調べていった結果、HTTPのステータスコードが 201 (Created) の場合のみ圧縮が効いてないのです。 nginx のソースをみてみます。

https://github.com/nginx/nginx/blob/master/src/http/modules/ngx_http_gzip_filter_module.c#L228-L240

ここに gzip 圧縮フィルタの関数があります。 if 文をみると HTTP_OK (200) でも NGX_HTTP_FORBIDDEN (403) でも NGX_HTTP_NOT_FOUND (404) でもない場合は処理を抜けて次のフィルタ適用の処理に進んでしまうようです。

微妙だ。

解決策としては、ステータスコードを 201 から 200 に変えるだけ。 これで圧縮が効くようになりました。

めでたしめでたし。