apacheの mod_cache の設定を復習した

apacheの日本語のdocumentはversionが古くて、いくつかのdirectiveの説明が無い。他には Caching Guide のoverview "What Can be Cached?" の項目を読むと良い。

1. Caching must be enabled for this URL. See the CacheEnable and CacheDisable directives.
2. The response must have a HTTP status code of 200, 203, 300, 301 or 410.
3. The request must be a HTTP GET request.
4. If the request contains an "Authorization:" header, the response will not be cached.
5. If the response contains an "Authorization:" header, it must also contain an "s-maxage", "must-revalidate" or "public" option in the "Cache-Control:" header.
6. If the URL included a query string (e.g. from a HTML form GET method) it will not be cached unless the response specifies an explicit expiration by including an "Expires:" header or the max-age or s-maxage directive of the "Cache-Control:" header, as per RFC2616 sections 13.9 and 13.2.1.
7. If the response has a status of 200 (OK), the response must also include at least one of the "Etag", "Last-Modified" or the "Expires" headers, or the max-age or s-maxage directive of the "Cache-Control:" header, unless the CacheIgnoreNoLastMod directive has been used to require otherwise.
8. If the response includes the "private" option in a "Cache-Control:" header, it will not be stored unless the CacheStorePrivate has been used to require otherwise.
9. Likewise, if the response includes the "no-store" option in a "Cache-Control:" header, it will not be stored unless the CacheStoreNoStore has been used.
10. A response will not be stored if it includes a "Vary:" header containing the match-all "*".

mod_cacheがcacheをするための条件

↑の"What Can be Cached?"をざっくりとまとめると。

  • requestにAuthorization headerを含む場合(認証が必要なpage)はcacheしない
  • URLにqueryを含む場合はExpiresかCache-Control headerが必要

また、http status が 200 OKの場合はresponse headerに以下のいずれかのheaderが必要 (cache のhint となるもの)

  1. Last-Modified
  2. Etag
  3. Expires
  4. Cache-Control

このあたりはLoglevelをdebug にしてerrorlogを確認するとわかる。

[debug] mod_cache.c(589): cache: /foo/bar not cached. Reason: No Last-Modified, Etag, Expires, Cache-Control:max-age or Cache-Control:s-maxage headers

cache の期限

Studying HTTP - HTTP Header Fields も参考に。
Cache-ControlはExpiresやPragmaより優先される。ただし、HTTP/1.0ではCache-Controlを実装してないので、古い環境では有効にならないかもしれない。

CacheDefaultExpire

expires, last-modifiedなどでdocumentに期限が設定されていないときのcache期限。

CacheDefaultExpire ディレクティブは、ドキュメントに 有効期限 (expiry) や最終修正時刻 (last-modified) が指定されていない場合の デフォルトの時間を指定します。CacheMaxExpire ディレクティブで指定された値はこの設定を上書きしません。

"ドキュメントに 有効期限 (expiry) や最終修正時刻 (last-modified) が指定されていない場合" にcacheされるのはどんなときか。例えば CacheIgnoreNoLastMod を使うと、Last-Modifiedがなくてもcacheするようになる。このときのcache 有効期限はCacheDefaultExpireになる。

CacheMaxExpire

cacheの最大期限、と説明にあるが実はそうではない。はず。

CacheMaxExpire ディレクティブ
説明: ドキュメントをキャッシュする最大時間を秒数で表したもの
CacheMaxExpire ディレクティブは、 キャッシュする HTTP ドキュメントを、元のサーバに問い合わせないまま最大何秒 保持してもよいかを指定します。
つまり、ドキュメントは最大でこの秒数間ぶん古く なることになります。この最大値は、(訳注:レスポンス中で)ドキュメントと共に ドキュメントの期日が提供されている場合でも適用されます。

と書いているんだけど、確認する限りではcontentsのresponse headerにCache-ControlやExpires headerがある場合はCacheMaxExpireより、それらの値のほうが優先されている。
contentsのresponse headerにCache-Controlは含まれていないけど、Last-Modified/Etag が含まれる場合: CacheMaxExpire で最大cache期限を設定できる?

その他のdirective

いずれも設定を間違えるとcacheして欲しくないものをcacheしてしまうので注意。

CacheIgnoreHeaders

指定されたヘッダをキャッシュしない。
コンテンツにもよるが、Set-Cookieをキャッシュするとセッションが共有されてしまい
大抵の場合セキュリティ的によろしくないので、
デフォルトに加えてSet-Cookieも除外すると良い。

CacheIgnoreHeaders Set-Cookie
CacheIgnoreNoLastMod

上でも書いたけど、CacheIgnoreNoLastMod On にするとLast-Modified headerがなくてもcacheする。
これを使えば、Applicationがcacheに必要なExpires/Cache-Control/Last-Modified などのheaderを吐いていなくてもcacheできるようになるので、apacheの設定変更だけでcacheを操作したい時にお奨め?

CacheIgnoreQueryString

まず、通常はrequestにqueryが含まれているとcacheはしない、がCache-Controlなどのheaderがあれば、それをhintにしてcache可能になる。

6. If the URL included a query string (e.g. from a HTML form GET method) it will not be cached unless the response specifies an explicit expiration by including an "Expires:" header or the max-age or s-maxage directive of the "Cache-Control:" header, as per RFC2616 sections 13.9 and 13.2.1.

apacheのerrorlog (loglevel debug)

[debug] mod_cache.c(589): cache: /?foo=bar not cached. Reason: Query string present but no explicit expiration time

この場合でも、queryが違えば異なるcacheして扱われる。ex) http://example.com/ にExpiresヘッダが含まれているとして、以下の2つのURLはそれぞれ違うcacheになる。(responseのAge header を見ればわかる)

queryの違いを無視して同じcacheを使いたい場合はCacheIgnoreQueryString On にすると良い。

  1. client が http://example.com/?foo=bar にアクセス
  2. apachehttp://example.com/?foo=bar の内容を応答、キャッシュする
  3. client が http://example.com/?hoge=fuga にアクセス
  4. apache は 2番でキャッシュしたhttp://example.com/?foo=bar の内容を応答する
CacheIgnoreURLSessionIdentifiers

CacheIgnoreQueryString と少し似ている。

/someapplication/image.gif;jsessionid=123456789
/someapplication/image.gif?PHPSESSIONID=12345678

のようにURLにsessionが埋めこまれていると、sessionごとにURLが変わるのでcacheが効かなくなる。sessionに関係なく、同じcacheを使いたい場合には

CacheIgnoreURLSessionIdentifiers PHPSESSIONID jsessionid

などと設定すると、URL中のPHPSESSIONID, jsessionidの違いを無視する。

CacheIgnoreCacheControl

通常はclientがCache-Control: no-cache、 Pragma: no-store headerを送ると、apacheはcacheを利用しない。が、CacheIgnoreCacheControl On にするとclientのrequestに関わらずcacheを使うように試みる。