Nginxでキャッシュをする際の、Cookieに関する注意点

こんにちは。エキサイト株式会社の三浦です。

皆さんは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 ヘッダがある場合もキャッシュを利用する際は、

  1. セキュリティの観点から、レスポンスから Set-Cookie は外したほうが良い
  2. 結果として、サーバからブラウザに対してCookieの保存処理をすることができなくなる

ことに注意する必要があります。

このことから、Nginxのキャッシュを利用する場合、画像やCSS、JavaScriptなどの静的ファイル、あるいはCookieなどの状態を持たない非常にシンプルなWebページを対象とするのがよく、状態を持ちうるWebページを対象とするのには不向きであると言えます。 そういったページに対しては、Redis等を使ったコンテンツキャッシュを使うことをまず考え、どうしようもない時のみNginxのキャッシュを使うことを考えるのが良いでしょう。

参考

Nginx プロキシキャッシュでクッキーがついていてもキャッシュするには