PHPでブラウザのキャッシュを利用させる検証 | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

純粋なHTMLファイルで、余計なヘッダーやMETAタグを付けていなければ一定期間、ブラウザ上にあるキャッシュを見させると言うことは出来たりします。

これって、PHPででも出来るのかなとふと思い検証してみました。

(特にそれで困ったことがあったわけではないのですが)


で、結論から言えばApacheの設定やヘッダーによる制御で、PHPを意図的にローカル(ブラウザ)のキャッシュを利用させるということは出来ませんでした。

キャッシュされているか否かでいえばキャッシュされているようなんですが、要は(あえてHTTステータスコードを返すようなことを除いて)ヘッダーやMETAタグだけの制御により、サーバーから304レスポンス返してブラウザにあるキャッシュを意図的に見せたい、ってことができない。



キャッシュ制御に関するおさらい


純粋にクライアント-サーバーのシステム構成で、間にプロキシサーバーなど余計なものを介在しなければ、キャッシュが働く場所は、クライアント(ブラウザ)とアプリケーション(Web)サーバーになります。

今回、アプリケーションサーバー上にはキャッシュの機構は特に用意していません。


ブラウザ上のキャッシュを”利用させたくない”場合、HTTPヘッダーやMETAタグに下記のように記載しておきます。


// PHPの場合
header("Cache-Control: no-cache");

// METAタグに入れる場合
<META HTTP-EQUIV="Cache-control" CONTENT="no-cache" />

// PHPの場合
header("Pragma: no-cache");

// METAタグに入れる場合
<META HTTP-EQUIV="Pragma" CONTENT="no-cache" />

※ HTTP/1.0用のため古い制御の仕方


// PHPの場合
header("Expires: -1");

// METAタグに入れる場合
<META HTTP-EQUIV="Expires" CONTENT="-1" />

ただ、純粋なHTMLファイルの場合、METAタグを正しく入れてもブラウザのキャッシュが利用されたりします。


一方で、キャッシュを”させたい”場合というのは、上記のヘッダーやMETAタグを入れないか、唯一制御できるCache-Controlを使用します。


<META HTTP-EQUIV="Cache-control" CONTENT="private, max-age=3600" />

その他にも、ApacheでExpiresを設定し強制的にキャッシュさせるようにすることも可能です。

詳細は、Apacheのドキュメント を参照。


ExpiresActive On
ExpiresByType text/html "access plus 1 hours"


PHPに対してローカルキャッシュを利用させる試行錯誤


と言うことで本題。

まず、PHPとHTMLとで中身としては同意なファイルを作り、それぞれに2度アクセスしてみます。


<html>
<head>
<META HTTP-EQUIV="Cache-control" CONTENT="private, max-age=3600" />
</head>
<body>
hello
</body>
</html>

<?php

echo "
<html>
<head>
<META HTTP-EQUIV='Cache-control' CONTENT='private, max-age=3600' />
</head>
<body>
hello
</body>
</html>
";

上記のファイルへのアクセスした際のHTTPヘッダをLive HTTP Headers で見てみます。


http://www.example.com/hoge.html

GET /hoge.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
If-Modified-Since: Wed, 09 Jun 2010 15:21:04 GMT
Cache-Control: max-age=0

HTTP/1.1 200 OK
Date: Wed, 09 Jun 2010 15:34:26 GMT
Server: Apache/2.2.3 (CentOS)
Last-Modified: Wed, 09 Jun 2010 15:29:59 GMT
Accept-Ranges: bytes
Content-Length: 119
Connection: close
Content-Type: text/html
----------------------------------------------------------
http://www.example.com/hoge.html

GET /hoge.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
If-Modified-Since: Wed, 09 Jun 2010 15:29:59 GMT
Cache-Control: max-age=0

HTTP/1.1 304 Not Modified
Date: Wed, 09 Jun 2010 15:34:31 GMT
Server: Apache/2.2.3 (CentOS)
Connection: close
User-Agent


2度目のアクセス時には、304ステータスコードが返ってきてローカルキャッシュが使われていることがわかります。

次に、PHPの場合


http://www.example.com/hoge.php

GET /hoge.php HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cache-Control: max-age=0

HTTP/1.1 200 OK
Date: Wed, 09 Jun 2010 15:38:21 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
Content-Length: 120
Connection: close
Content-Type: text/html
----------------------------------------------------------
http://www.example.com/hoge.php

GET /hoge.php HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cache-Control: max-age=0

HTTP/1.1 200 OK
Date: Wed, 09 Jun 2010 15:38:23 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
Content-Length: 120
Connection: close
Content-Type: text/html


2度目も200のステータスコードが返され、キャッシュを利用していません。

header()を使ってCache-controlを指定してもダメです。


HTTP/1.1 200 OK
Date: Wed, 09 Jun 2010 15:48:36 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
Cache-Control: private,max-age=3600
Content-Length: 120
Connection: close
Content-Type: text/html


レスポンスヘッダ内にしっかりCache-Controlの指定がされているのですが、何回リロードしてもキャッシュを利用してくれません。

他にも、Apacheの設定ファイル内で


# ETagを使用しない
FileETag None

# Expiresを指定
ExpiresByType text/html "access plus 1 hours"

みたいな事もやってみましたが効果なし。

結局、全て200ステータスコードを返してきます。

ちなみに、304のステータスコードを強制的に返してやるとキャッシュを使ってくれます。


HTTP/1.1 304 Not Modified
Date: Wed, 09 Jun 2010 15:58:41 GMT
Server: Apache/2.2.3 (CentOS)
Connection: close
Expires: Wed, 09 Jun 2010 16:58:41 GMT
Cache-Control: max-age=3600

PHP側でレスポンスヘッダを制御する方法は、下記のエントリが参考になります。


304 Not Modifiedをhttpレスポンスヘッダで制御する方法(If-Modified-Since偏) @ pblo


このことからもPHPのファイルとはいえ、ブラウザ内にキャッシュはされていると言うことがわかります。

実際、PHPから出力しているHTMLの一部を変更してリロードしても古い情報が引き続き表示されたりします。

予断ですが、PHP側で304を常に返すようにしておいて、ブラウザのキャッシュをクリアしたらどうなるかって実験をしたら、きちんと最新のページを読んできました。

これは、矛盾していることではあるんですがApacheのログを見ても


192.168.0.100 - [10/Jun/2010:01:05:24 +0900] "GET /hoge.php HTTP/1.1" 304 305 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)"

とか出ていて、ステータスコードとかって当てに出来ないんだなとか思ったり。

最後に、PHPを複数の拡張子で動作できるようにしている場合があったりしますが、それについてもキャッシュが有効に働いてくれないようです。


AddType application/x-httpd-php .php .htm

上記のような場合、.htmファイルでもPHPが動くようになります。

ただ、純粋なHTMLファイルとしても動くわけなのですが、PHPの処理を一切書いていない、下記のようなファイルであってもキャッシュが働いてくれませんでした。


- hoge.htm(PHPは動作可能だが、処理を一切書かずにHTMLのみにしたファイル)

<html>
<head>
<META HTTP-EQUIV='Cache-control' CONTENT='private, max-age=3600' />
</head>
<body>
hello
</body>
</html>


http://www.example.com/hoge.htm

GET /hoge.htm HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cache-Control: max-age=0

HTTP/1.1 200 OK
Date: Wed, 09 Jun 2010 16:33:15 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
Content-Length: 119
Connection: close
Content-Type: text/html

こう考えると、わざわざPHPにする必要の無いほぼHTMLのようなファイルや、PHPでも動作可能な拡張子のファイルってローカルキャッシュが上手く働いてくれずに無駄な処理が行われてしまうのかな。