こんにちは。エキサイト株式会社の三浦です。
皆さんはNginxでキャッシュを使ったことがありますか? レスポンスごとキャッシュをしてくれるので、使いようによっては非常に有効な機能です。
一方で、レスポンスごとキャッシュすることに起因する注意点もあります。
今回は、私がハマったNginxキャッシュとCookieに関連する、とある問題について説明していきます。
Nginxキャッシュとは
Nginxのキャッシュは、Nginxが返すレスポンスをそのままキャッシュしてくれます。 キャッシュがヒットすればアプリケーションコードにアクセスが行かず、Nginxだけで処理が完結するため、サービスのスピードや負荷対策に大きな効果をもたらします。
proxy_cache_path /var/cache/nginx_sample1 levels=1:2 keys_zone=sample1:1m max_size=10m inactive=3m; proxy_temp_path /var/cache/nginx_tmp; server { listen 80; server_name sample; location / { proxy_http_version 1.1; proxy_cache_valid 200 301 302 1m; proxy_cache sample1; root /var/sample/; } }
ただしこの設定だと、Cookieに関連するとある事項から想定通り挙動しないことがあります。
NginxキャッシュとCookie
Nginxは、 Set-Cookie
ヘッダがレスポンスに含まれる場合、デフォルトではそのレスポンスをキャッシュしてくれません。
このような時、 proxy_ignore_headers
を使えば、 Set-Cookie
ヘッダがあってもキャッシュしてくれます。
proxy_cache_path /var/cache/nginx_sample1 levels=1:2 keys_zone=sample1:1m max_size=10m inactive=3m; proxy_temp_path /var/cache/nginx_tmp; server { listen 80; server_name sample; location / { proxy_http_version 1.1; # この設定で、Set-Cookieがあってもキャッシュするようにする proxy_ignore_headers Set-Cookie; proxy_cache_valid 200 301 302 1m; proxy_cache sample1; root /var/sample/; } }
これで Set-Cookie
ヘッダがあってもキャッシュしてくれるようになるわけですが、この設定によって問題が発生することがあります。
Nginxのキャッシュではレスポンスをそのまま返します。
それは、ヘッダにある Set-Cookie
も同様です。
すなわち、Cookieの設定自体もキャッシュします。
Cookieで機密性のあるデータを取り扱っていなければ問題ありませんが、セッション情報などを載せて通信している場合、 Set-Cookie
ごとキャッシュすると本来のユーザ以外にもセッション情報を送ってしまうことになり、サービスとしての動作はもちろんセキュリティ的にも問題が発生してしまいます。
上記のような問題を避けるため、キャッシュを使う際は proxy_hide_header
を使って Set-Header
をレスポンスに含めないようにすると良いです。
proxy_cache_path /var/cache/nginx_sample1 levels=1:2 keys_zone=sample1:1m max_size=10m inactive=3m; proxy_temp_path /var/cache/nginx_tmp; server { listen 80; server_name sample; location / { proxy_http_version 1.1; proxy_ignore_headers Set-Cookie; # この設定で、Set-Cookieをレスポンスから外す proxy_hide_header Set-Cookie; proxy_cache_valid 200 301 302 1m; proxy_cache sample1; root /var/sample/; } }
これによってCookieに関するセキュリティのリスクが回避できるのですが、それによってさらに別の問題が発生します。
NginxキャッシュとCookieは実質併用できない
サーバからブラウザにCookieを保存する際は、 Set-Cookie
ヘッダを通して設定します。
しかし、上記のように Set-Cookie
ヘッダをレスポンスに含めないように設定すると、サーバからブラウザに対してCookie設定をすることができません。
その結果、アプリケーションコード上ではCookie設定をしているはずなのに、ブラウザのCookieを見てみると正しくCookieが設定されていない、という状況が発生します。
とはいえセキュリティの観点からキャッシュ中は Set-Cookie
ヘッダは返すわけには行かないので、Set-Cookie
ヘッダがある場合もキャッシュしたい場合のキャッシュ使用中は、実質Cookieを保存することはできないと言っていいでしょう。
まとめ
Nginxで Set-Cookie
ヘッダがある場合もキャッシュを利用する際は、
- セキュリティの観点から、レスポンスから
Set-Cookie
は外したほうが良い - 結果として、サーバからブラウザに対してCookieの保存処理をすることができなくなる
ことに注意する必要があります。
このことから、Nginxのキャッシュを利用する場合、画像やCSS、JavaScriptなどの静的ファイル、あるいはCookieなどの状態を持たない非常にシンプルなWebページを対象とするのがよく、状態を持ちうるWebページを対象とするのには不向きであると言えます。 そういったページに対しては、Redis等を使ったコンテンツキャッシュを使うことをまず考え、どうしようもない時のみNginxのキャッシュを使うことを考えるのが良いでしょう。