Apache2で動的コンテンツをキャッシュさせたい

ウェブサービスでフロントエンドとしてApache2を使っています。役目はフロントキャッシュと、負荷分散。こないだ入れたシステムで、Zope複数のZopeインスタンス間でデータをAPIをつかって取り出すような仕組みになりました。

動的とはいえ内容の更新頻度は低いので、Apacheを噛ませてキャッシュきかせられたらいいなぁと。


単純に書いてみる

単純にこんな設定をいれてみましたが、

    <IfModule mod_cache.c>
        CacheDefaultExpire      36000
        CacheEnable             disk    /
        CacheIgnoreNoLastMod    On
        CacheStoreNoStore       On
        CacheStorePrivate       On
        CacheMaxExpire          86400

        <IfModule mod_disk_cache.c>
            CacheRoot           /usr/local/apache2/cache
            CacheDirLevels      2
            CacheDirLength      3
            CacheMaxFileSize    2000000
            CacheMinFileSize    1
        </IfModule>
    </IfModule>

これだとまったくキャッシュしてくれません。キャッシュに関するRFCで、クエリ文字列(?とか&とか)があるURLはキャッシュしちゃいかんよってのがあるらしいです。

無視してくれないかな

きっとApacheくらい気の利いたやつだと無視してくれる設定があるんだろう、とIgnore Queryなどと適当にGoogle先生に聞いたところ、ありましたよ。こんなのを入れて再起動。

        CacheIgnoreQueryString  On

ばっちりキャッシュしてくれる。。。と思いきや、クエリ文字から下をちょん切ってキャッシュを取り出してくる。つまり、
http://www.hogehoge.com/getitem?country=cn&trade=01
をキャッシュすると、
http://www.hogehoge.com/getitem?country=jp&trade=02
をリクエストされると上記のキャッシュをかえす。そりゃ意味がないな。

ソースをのぞくと

ソースではどんな処理になってるのかなとチェック。mod_cache.cの1309行目あたり(Apache2.2.11)にあります。

    else if (!conf->ignorequerystring && r->parsed_uri.query && exps == NULL &&
             !ap_cache_liststr(NULL, cc_out, "max-age", NULL)) {
        /* if a query string is present but no explicit expiration time,
         * don't cache it (RFC 2616/13.9 & 13.2.1)
         */
        reason = "Query string present but no explicit expiration time";
    }

えーとですね。クエリ文字列がURLにあって、CacheIgnoreQueryStringがOFFになってて、Expiresヘッダがついてない場合はキャッシュしないそうです。はい、そうですか。

じゃあ

ってなことでこんな風に改造。

    else if (!conf->ignorequerystring && r->parsed_uri.query  &&
             !ap_cache_liststr(NULL, cc_out, "max-age", NULL)) {
        /* if a query string is present but no explicit expiration time,
         * don't cache it (RFC 2616/13.9 & 13.2.1)
         */
        reason = "Query string present but no explicit expiration time";
    }

Expiresヘッダのところの条件を削除。これで再コンパイル、再起動してキャッシュしてくれることを確認。

でもなぁ

本来は後段のZopeがExpiresヘッダをつけて返してくれるのが一番いい。Plone/Zopeでヘッダコントロールってできるのかな。