はじめに
Apache 2.4のサーバーから304 (Not Modified) のレスポンスが返るべき状況で、常に200 (OK) が返るという問題に遭遇した。その解決方法について調べたので備忘録を残す。
問題
パフォーマンス対策の一環として、Cache-Control
にno-cache
を指定した。対象はHTMLとJavaScriptのファイル。この場合ファイルに変更が無い場合は、サーバーは304を返すはずである。しかし対象のサーバーでは、コンテンツに変更が無いにも関わらず、常に200を返してきた。
原因
調べてみるとこの現象はApacheのバグとして報告されている。最初の報告は2008年なので「本当に今でも未解決なのか?」と驚いたが … 経緯が色々とあるらしい。
参照: Bug 45023 – DEFLATE preventing 304 NOT MODIFIED response
ApacheのディレクティブでDEFLATE
が指定されていると、サーバーからWebブラウザにコンテンツを圧縮して送り出す。この時Etag
に-gzip
が追加される。このEtag
は次回のアクセス時に、WebブラウザからIf-None-Match
で送り返される。サーバーはこのEtag
の値を現在のEtag
の値と比較して、変更の有無を判断するが、Etag
に-gzip
が含まれていると正しく比較できず、コンテンツに変更があったと誤って判断する(と一応理解した)。
筆者の環境はレンタルサーバーのため、DEFLATE
の設定については確認できていない。しかしレスポンスにContent-Encoding: gzip
が含まれており、Etag
の末尾に-gzip
も追加されていることから、上記の説明に該当するものと判断した。
解決策
対象のファイルを置いてあるディレクトリに、.httaccess
ファイルを作成し、Bug 45023のComment #26に基づいて次を記述した。
<IfModule mod_headers.c>
RequestHeader edit "If-None-Match" '^"((.*)-(gzip|br))"$' '"$1", "$2"'
</IfModule>
Webブラウザからのリクエストヘッダーに含まれているETag
から-gzip
を削除している。
-br
も指定されているが、これは別の圧縮方式で追加されるものである。-gzip
と同様の問題を引き起こすものと思われるが、筆者の環境では未確認。
対象をより限定する場合は、Directory
, Location
, Filter
などを併用する必要がある。
なおApache 2.5では、DeflateAlterETag
Directiveを使うことで、この問題を回避できるとある。そこで2.5のドキュメントを見てみると、このディレクティブは2.4.42以降で使えるとある。しかし2.4のドキュメントには記載がない。実際に使えるかどうか筆者は未確認。
キャッシュコントール
Cache-Control
は、サーバーからのレスポンスヘッダーのひとつで、Webブラウザのキャッシュを制御することができる。キャッシュコントロールは、目的に応じて複数の方法から指定可能となっている。
Cache-Control
にno-cache
を指定すると、Webブラウザーは毎回サーバーからコンテンツの取得を試みる。サーバーはコンテンツに変更があれば、200と新しいコンテンツを返す。変更が無い場合は、304のみを返しコンテンツの再送は行わない。Webブラウザは、304を受け取ると、自身のキャッシュ内のコンテンツを使ってレンダリングを行う。
キャッシュを全くしないと誤認されている場合があるので要注意である。
変更履歴
日付 | 内容 |
2023/05/14 | 初版リリース |