nginxのgzip、location単位で設定変えると優先されるのは一番具体的なパス

  • nginx
  • Infrastructure

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

nginxのgzip設定はhttp・server・location・ifのどのコンテキストにも書ける。だから特定のパスだけ圧縮の閾値を変えたい、みたいなことができる。

location単位でgzip設定を分ける

例えば/api/以下は全部圧縮したいけど、/api/foo/barだけは閾値を変えたい、みたいなケース。

server {
listen 80;
location /api/foo/bar {
gzip on;
gzip_min_length 512;
}
location /api/ {
gzip on;
gzip_min_length 256;
}
}

正規表現を使わない通常のlocation同士なら、リクエストパスと一番長く一致する(=一番具体的な)locationが優先される。なので/api/foo/barへのアクセスは上のlocation /api/foo/barが勝ってgzip_min_length 512が適用される。それ以外の/api/配下はgzip_min_length 256。

flowchart TD
    A[リクエスト: /api/foo/bar] --> B{最長一致のlocationを探す}
    B --> C[location /api/foo/bar]
    B --> D[location /api/]
    C -->|一致文字数が長い方が優先| E[gzip_min_length 512 が適用される]

圧縮されてるかはContent-Encodingヘッダーで見る

レスポンスにContent-Encoding: gzipが付いてれば圧縮されてる。付いてない場合は大体以下のどれか。

  • クライアントがAccept-Encoding: gzipを送ってない
  • gzip_min_length未満でスキップされてる
  • gzip_typesに該当MIMEタイプが入ってない(text/htmlはデフォルトで含まれてるので指定不要)

確認はcurlで十分。

ターミナル
curl -H "Accept-Encoding: gzip" -I https://example.com/api/foo/bar

Content-Encoding: gzipが出てれば圧縮成功。中身まで見たいなら--compressedオプションを付ける。

gzip_proxied anyは便利だけど無条件じゃない

gzip_proxied anyにするとプロキシ経由のレスポンスも問答無用で圧縮する。楽だけど、トークンとか個人情報を含むレスポンスまで圧縮対象になる。

TLS配下で秘密情報を含むレスポンスを圧縮すると、BREACHという攻撃手法でその内容を推測される可能性があるらしい(実機での検証はしてない)。gzip_proxied no-cache no-store private expired authのようにCache-Controlの値を見て判断する書き方にすれば、公開情報以外は圧縮しない分安全側に倒せる。

APIレスポンスに認証情報や個人データが混ざる可能性があるエンドポイントは、anyじゃなくてこっちの書き方を検討した方が良さそう。