【Nginx】proxy_cache_validで変数が使えない理由と対処法

サーバー

問題の概要

拡張子によってキャッシュ時間を変えるために map ディレクティブで $cache_duration 変数を定義し、proxy_cache_valid に渡そうとしたところ、意図通りに動作しなかった。

# httpブロックで定義
map $uri $cache_duration {
    ~*\.(css|js|jpe?g|gif|png|svg|...)$  "2m";
    default                               "1m";
}

# serverブロック内で使用しようとした
proxy_cache_valid 200 $cache_duration;  ← これが効かない

問題点
proxy_cache_valid の時間指定部分は変数を受け付けない nginx の仕様のため、$cache_duration を渡しても無視される。

原因

proxy_cache_valid の仕様制限

nginx の proxy_cache_valid ディレクティブは時間の指定に変数を使えない。キャッシュ時間はコンフィグ読み込み時に静的に決定される必要がある。

# NG — 変数は使えない
proxy_cache_valid 200 $cache_duration;

# OK — 固定値のみ有効
proxy_cache_valid 200 2m;

map ディレクティブの制約

map ディレクティブは http ブロックにしか記述できない。server ブロック内でインクルードする .inc ファイルには記述できないという制約もある。つまり $cache_duration を定義することはできても、proxy_cache_valid には渡せない。

解決方法

静的ファイル専用のインクルードファイル proxy_cache_static.inc を新たに作成した。拡張子にマッチする location ブロックごと固定値で proxy_cache_valid 200 2m; を定義し、proxy_cache_full.inc(デフォルト1分)と2行セットで使用する構成にした。

proxy_cache_static.inc(新規作成)

# =============================================================================
# /etc/nginx/conf.d/proxy_cache_static.inc
# 静的ファイル用キャッシュ設定(2分)
#
# proxy_cache_full.inc とセットで使用する。
# server { } ブロック内、location { } の外側で include する。
#
# キャッシュOFF: proxy_cache_full.inc とセットでコメントアウト
#   #include /etc/nginx/conf.d/proxy_cache_full.inc;
#   #include /etc/nginx/conf.d/proxy_cache_static.inc;
# =============================================================================
location ~* \.(css|js|jpe?g|gif|png|svg|svgz|wbmp|webp|ico|jng|bmp|
               3gpp|3gp|ts|mp4|mpe?g|mov|webm|flv|m4v|mng|asx|asf|wmv|avi|
               mid|midi|kar|mp3|ogg|m4a|ra|woff2?|ttf|otf|eot)$ {
    proxy_pass             http://127.0.0.1:8080;
    proxy_set_header       Host $host;
    proxy_set_header       X-Real-IP $remote_addr;
    proxy_set_header       X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header       X-Forwarded-Proto $scheme;
    proxy_cache            CACHE;
    proxy_cache_key        "$scheme$host$request_uri";
    proxy_cache_valid      200 2m;  ← 固定値で2分
    proxy_cache_valid      404 1m;
    proxy_cache_valid      any 0s;
    proxy_no_cache         $no_cache $upstream_http_set_cookie;
    proxy_cache_bypass     $no_cache $upstream_http_set_cookie;
    proxy_cache_use_stale  error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_lock       on;
    add_header             X-Cache-Status $upstream_cache_status always;
}

virtual.conf での使い方

server ブロック内、location の外側で2行セットでインクルードする。キャッシュをOFFにする場合は2行セットでコメントアウトするだけ。

server {
    listen 80;
    server_name example.com;

    # キャッシュON(2行セット)
    include /etc/nginx/conf.d/proxy_cache_full.inc;    # デフォルト1分
    include /etc/nginx/conf.d/proxy_cache_static.inc;  # 静的ファイル2分

    # キャッシュOFF(2行セットでコメントアウト)
    #include /etc/nginx/conf.d/proxy_cache_full.inc;
    #include /etc/nginx/conf.d/proxy_cache_static.inc;

    location / {
        proxy_pass http://127.0.0.1:8080;
        ...
    }
}

ポイント
proxy_cache_static.inc の location ブロックが静的ファイルにマッチし proxy_cache_valid 200 2m; が適用される。それ以外は proxy_cache_full.inc のデフォルト(1分)が使われる。2行セットでコメントアウトするだけでON/OFFを切り替えられるので、Perlスクリプトからの制御も容易。

map ディレクティブはどうなったか

map $uri $cache_duration の定義は proxy_cache_valid には使えないため不要になった。ただし map 自体は他の用途(キャッシュ除外フラグの制御など)には引き続き利用できる。

map の正しい用途
map で作成した変数は proxy_no_cache、proxy_cache_bypass、add_header などには使える。時間指定が必要な proxy_cache_valid だけが変数非対応。

まとめ

  • proxy_cache_valid の時間指定には変数が使えない(nginx の仕様)
  • map で $cache_duration を定義しても proxy_cache_valid には渡せない
  • 解決策は静的ファイル専用の proxy_cache_static.inc を作成し、location ブロックごと固定値で定義すること
  • proxy_cache_full.inc(1分)と proxy_cache_static.inc(2分)を2行セットでインクルードし、コメントアウトでON/OFFを切り替える