VarnishはCookieが設定されているとキャッシュしない
Varnish チュートリアルにある Cookie のセクションから
Varnish will, in the default configuration, not cache a object coming from the backend with a Set-Cookie header present. Also, if the client sends a Cookie header, Varnish will bypass the cache and go directly to the backend.
https://www.varnish-cache.org/docs/3.0/tutorial/cookies.html
この挙動を確認
確認方法
現在時刻を返すだけのページを用意し、キャッシュされているなら、同じ時刻を返すし、キャッシュされていなければ、リクエスト時点の現在時刻を返すはず。
クライアントがCookieを送信する場合
現在時刻を返すページ
<?php echo date('Y/m/d H:i:s') . "\n"; ?>
このページを /date.php と /cookie/date.php に用意
Cookieを送信
curl
を使って GET
で Cookie 送信
$ curl --cookie "foo=bar" http://localhost:8080/date.php
VCL
/cookie
以下にアクセスされた時は、クライアントが送信した Cookie を削除し、キャッシュされるようにする。
sub vcl_recv { if (!(req.url ~ "^/cookie/")) { unset req.http.Cookie; } }
実験
$ curl --cookie "foo=bar" http://localhost:8080/cookie/date.php 2013/12/31 07:26:36 $ curl --cookie "foo=bar" http://localhost:8080/cookie/date.php 2013/12/31 07:26:39 $ curl --cookie "foo=bar" http://localhost:8080/date.php 2013/12/31 07:26:49 $ curl --cookie "foo=bar" http://localhost:8080/date.php 2013/12/31 07:26:49
/cookie
以下ではキャッシュされている。
クライアントがCookieを設定する場合
現在時刻を返し、クッキーを設定するページ
<?php setcookie("foo", "bar"); echo date('Y/m/d H:i:s') . "\n"; ?>
このページを /setcookie.php と /cookie/setcookie.php に用意
Cookieを送信
curl
を使って GET
リクエスト。Set-Cookie 確認のため、レスポンスヘッダーをダンプする。
$ curl -D - http://localhost:8080/setcookie.php
VCL
/cookie
以下にアクセスされた時はサーバが送信した Set-Cookie を残しキャッシュされず、それ以外の場合はサーバーが送信した Set-Cookie を削除し、キャッシュされるようにする。
Set Cookie
しているページは Varnish には hit_for_pass
としてマークされ、次回以降はオリジンサーバにアクセスする。
sub vcl_fetch { if (!(req.url ~ "^/cookie/")) { unset beresp.http.set-cookie; } }
実験
$ curl -D - http://localhost:8080/cookie/setcookie.php HTTP/1.1 200 OK Server: nginx/1.1.19 Content-Type: text/html X-Powered-By: PHP/5.3.10-1ubuntu3.9 Set-Cookie: foo=bar Transfer-Encoding: chunked Date: Tue, 31 Dec 2013 07:41:16 GMT X-Varnish: 1563644852 Age: 0 Via: 1.1 varnish Connection: keep-alive 2013/12/31 07:41:16 $ curl -D - http://localhost:8080/cookie/setcookie.php HTTP/1.1 200 OK Server: nginx/1.1.19 Content-Type: text/html X-Powered-By: PHP/5.3.10-1ubuntu3.9 Set-Cookie: foo=bar Content-Length: 20 Accept-Ranges: bytes Date: Tue, 31 Dec 2013 07:41:17 GMT X-Varnish: 1563644853 Age: 0 Via: 1.1 varnish Connection: keep-alive 2013/12/31 07:41:17 $ curl -D - http://localhost:8080/setcookie.php HTTP/1.1 200 OK Server: nginx/1.1.19 Content-Type: text/html X-Powered-By: PHP/5.3.10-1ubuntu3.9 Transfer-Encoding: chunked Date: Tue, 31 Dec 2013 07:41:28 GMT X-Varnish: 1563644854 Age: 0 Via: 1.1 varnish Connection: keep-alive 2013/12/31 07:41:28 $ curl -D - http://localhost:8080/setcookie.php HTTP/1.1 200 OK Server: nginx/1.1.19 Content-Type: text/html X-Powered-By: PHP/5.3.10-1ubuntu3.9 Transfer-Encoding: chunked Date: Tue, 31 Dec 2013 07:41:28 GMT X-Varnish: 1563644855 1563644854 Age: 1 Via: 1.1 varnish Connection: keep-alive 2013/12/31 07:41:28
vmod_cookie を使ってみる
vcl の標準機能だけで Cookie を操作するには、Cookie の文字列に対して正規表現をゴリゴリ書く必要がある。
これでは面倒だしメンテもしづらいので、Varnish Software の中の人がクッキーを操作するユーティリティー関数群を vmod-cookie にまとめてくれている。
https://github.com/lkarsten/libvmod-cookie
Install libvmod-cookie
Ubuntu でのインストール方法は 作者がブログにまとめてくれている
http://lassekarstensen.wordpress.com/2013/07/29/building-a-varnish-vmod-on-debian/
libtool
configure か make のタイミングで “libtoolize not found” というようなエラーが起きたので $ sudo apt-get install libtool
して libtoolize をインストールした。
バージョンの不一致
コンパイル時にはビルド済み Varnish が必要。
3.0.5 の varnish が動いているのに 3.0.4 のコードベースに対してビルドすると、varnish の起動時に行われる VMOD のコンパイルで次のようなエラーが発生した。
Message from VCC-compiler: Could not load module cookie /usr/lib/varnish/vmods/libvmod_cookie.so ABI mismatch, expected <Varnish 3.0.5 1a89b1f>, got <Varnish 3.0.4 9f83e8f> ('input' Line 1 Pos 8) import cookie; -------######- Running VCC-compiler failed, exit 1 VCL compilation failed
ということで、マイナーバージョンも含めてバージョンをあわせましょう。
vmod_cookie で Cookie 操作
vmod_cookie はクライアントが送信する Cookie を vcl_fetch でごにょごにょするのが一番の使いどころ。
VCL ファイルで import cookie;
し、 cookie.使いたい関数
で Cookie を操作すればよい。
リファレンスは次の URL を参照
https://github.com/lkarsten/libvmod-cookie/blob/3.0/README.rst
実験
実験用VCL
- URL に delete が含まれる場合は、Cookie からキー “1” を削除
- URL に filter が含まれる場合は、Cookie からキー “1,2” 以外を削除
- クライアントには削除後の Cookie を設定
するような VCL を用意
import std; import cookie; backend default { .host = "127.0.0.1"; .port = "80"; } sub vcl_recv { cookie.parse(req.http.Cookie); std.log(cookie.get_string()); // Cookie 文字列全体を出力 if (req.url ~ "delete") { cookie.delete("1"); // Cookie からキー "1" を削除 } else if (req.url ~ "filter") { cookie.filter_except("1,2"); // Cookie からキー "1,2" 以外を削除 } std.log("1 value is: " + cookie.get("1")); // Cookie のキー "1" の値を取得 std.log("2 value is: " + cookie.get("2")); std.log("3 value is: " + cookie.get("3")); std.log(cookie.get_string()); set req.http.cookie = cookie.get_string(); // クライアントクッキーを再設定 }
クライアントクッキーをダンプするページ
vmod-cookie
で Cookie がどのように変更されるのか確認する。
<?php print_r($_COOKIE) ?>
このページを /delete.php と /filter.php に用意
実験
クライアントからは Cookie "1=one;2=two;3=three"
を送信
cookie.delete の確認
URL に delete が含まれる場合は、Cookie からキー “1” を削除
$ curl --cookie "1=one;2=two;3=three" http://localhost:8080/delete.php Array ( [2] => two [3] => three )
“1=one” だけが削除されている。
cookie.filter_except の確認
URL に filter が含まれる場合は、Cookie からキー “1,2” 以外を削除
$ curl --cookie "1=one;2=two;3=three" http://localhost:8080/filter.php Array ( [1] => one [2] => two )
確かに “3=three” だけが削除されている。
VCL のログの確認
VCL 内では std.log
でログ出力している。
以下のようにして出力を確認できる
$ sudo varnishlog -i VCL_LOG 0 VCL_Log - libvmod-cookie: parsed 3 cookies. 14 BackendClose - default 14 BackendOpen b default 127.0.0.1 53935 127.0.0.1 80 14 BackendReuse b default 12 SessionOpen c 127.0.0.1 43142 :8080 12 VCL_Log c 1=one; 2=two; 3=three; 12 VCL_Log c 1 value is: one 12 VCL_Log c 2 value is: two 12 VCL_Log c 3 value is: 12 VCL_Log c 1=one; 2=two; 12 ReqEnd c 1825349847 1388499399.437688828 1388499399.441040993 0.000321388 0.003163099 0.000189066