アルファサード株式会社 代表取締役 野田 純生のブログ


HTTPレスポンスヘッダとかステータスコードとかキャッシュとか。


公開日 : 2007-10-23 20:04:12


某所でまたHTTPステータスコードの話が盛り上がってるみたいだけどちゃんと見解というか言ってなかったな。というか今日あげたエントリーがBlogのサーバー側のキャッシュの話だったので (検索結果のフィードも吐けるようにしてあるし) このブログのMTのダイナミックパブリッシングのHTTPヘッダをチェックしてみた。

あらあら、404だわ。mod_rewriteが無効だからNotFound扱いで動いてるのね。このままじゃ怒られちゃう(謎)から修正。

一応MTが吐く.htaccessはこんな風になってる。


## %%%%%%% Movable Type generated this part; don't remove this line! %%%%%%%
# Disable fancy indexes, so mtview.php gets a chance...
Options -Indexes +SymLinksIfOwnerMatch
<IfModule mod_rewrite.c>
# 中略, あとコメント部とかも略
  RewriteEngine on
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^(.*)$ /online/mtview.php [L,QSA]
</IfModule>

<IfModule !mod_rewrite.c>
# mod_rewriteが無効なのでこっちが有効になってる
  ErrorDocument 404 /online/mtview.php
  ErrorDocument 403 /online/mtview.php
</IfModule>
## ******* Movable Type generated this part; don't remove this line! *******

ということで、まずはmod_rewriteを有効に(サーバーはUbuntu)。


sudo a2enmod rewrite
sudo /etc/init.d/apache2 restart

Ubuntu簡単。これでステータスは200になったけど、mtview.phpも修正。Last-Modified ヘッダを付ける (MTがちゃんとしてないというわけでなくて、クエリーの付いていないリクエストであれば $mt->caching = true; $mt->conditional = true; で済む)。

mtview.php


<?php
    $server_cache = 86400; //サーバー側のキャッシュの有効期間=1日
    $cache_dir = '/path/to/cache/';
    include('<$MTCGIServerPath$>/php/mt.php');
    $mt = new MT(<$MTBlogID$>, '<$MTConfigFile$>');
    $pattern = '/(search¥.html|feed¥.xml)/';
    if (preg_match($pattern, $_SERVER['REQUEST_URI'])) {
        $search = true;
        //check own cache
        $path = getenv('REQUEST_URI');
        $path = str_replace('/', '%2F', $path);
        $path = str_replace('..', '', $path);
        $cache = $cache_dir . $path;
        if (file_exists($cache)) {
            $mtime = filemtime($cache);
            if ((time() - $mtime) > $server_cache) {
                unlink($cache);
                $match = 0;
            } else {
                header( "Last-Modified: " . gmdate( "D, d M Y H:i:s", $mtime ) . ' GMT' );
                //キャッシュの更新日を Last-Modified ヘッダに付ける
                $match = 1;
            }
        } else {
            $match = 0;
        }
        if ($match) {
            if (!($fcache = fopen($cache, 'r'))) {
                $match = 0;
            } else {
                $rec = file($cache);
                foreach ($rec as $line) {
                    echo "$line¥n";
                }
                return;
            }
        }
    } else {
        $mt->caching = true;
    }
    $mt->conditional = true;
    ob_start();
        $mt->view();
        $output = ob_get_contents();
    ob_end_clean();
    $ctime = time();
    header( "Last-Modified: " . gmdate( "D, d M Y H:i:s", $ctime ) . ' GMT' );
    //キャッシュがなければ現在の時刻を Last-Modified ヘッダに付ける
    echo $output;
    if ($search) {
        //save own cache
        if (!($match)) {
            if (!($fh = fopen($cache, 'w'))) {
                return;
            }
            fwrite($fh, $output, 128000);
            fclose($fh);
            touch ($cache, $ctime); //矛盾がないようにtouchしてやる
        }
    }
?>

確認!


$ telnet junnama.alfasado.net 80
Trying 61.205.62.101...
Connected to junnama.alfasado.net.
Escape character is '^]'.
HEAD /online/feed.xml?query=Movable%20Type&offset=1&limit=20&blog_id=1&tag=1 HTTP/1.0

HTTP/1.1 200 OK
Date: Tue, 23 Oct 2007 11:01:56 GMT
Server: Apache/2.0.55 (Ubuntu) mod_fastcgi/2.4.2 PHP/5.1.2
X-Powered-By: PHP/5.1.2
Last-Modified: Tue, 23 Oct 2007 10:57:43 GMT
Connection: close
Content-Type: text/html

Connection closed by foreign host.

200 OK, Last-Modified:ヘッダもちゃんと返って来てるね。

RSSリーダーがHEADリクエスト送って来てタイムスタンプチェックしてるかとか、If-Modified-Sinceヘッダ送って来てるかとか知らんけど、更新されてなかったら304 Not Modifiedを返すと尚良いだろうね。

If-Modified-Sinceヘッダをチェックして、日付フォーマット解析して(Perlだったっらモジュールで一発だけど、PHPはどうなんでしょうね)、キャッシュのタイムスタンプと比較して更新されてなければ304 Not Modified を返すということ。

まぁ、これは週末にでもやるとしよう。あと、検索結果が該当しなかった時は...リクエストが不正なわけじゃないんだけどfeedの場合は400が良いのか? 検索結果だから後々有効になる可能性もあるし200で良いわな。何らかのメッセージは返すのが良いのだろう。

そろそろamebloのフィードの件について...

あ、一応書いておく(amebloのRSSの件ね)。200とか以前に、Last-Modifiedヘッダも吐いてないし、JSPってか動的に吐いてるのだろうか?

GET /staff/rss20.xml


...
Set-Cookie: JSESSIONID=XXXXXXXXXXXXXXXXXXXXXXXXX; Path=/
Connection: close
Transfer-Encoding: chunked
...

CGIほどのオーバーヘッドはないにしても...。静的なファイルを都度生成でなくとも少なくともサーバーサイドにキャッシュくらいしてるよね。HEADリクエスト送っても更新日情報返ってこないからクライアントはIf-Modified-Since付けないでGETリクエスト送るしかないし、サーバーも304も返しようがない。ヘッダ自前でコントロールするの面倒ならApacheに任せちゃえばいいんじゃないか? (ファイル作っときゃいいし)

この調子だからコンテンツも動的なんだろうね、と思ったらURLは~.htmlだ。

GET /staff/entry-10051436729.html


...
Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Expires: Mon, 23 Oct 2006 10:49:21 JST
...

no-cacheって...これもおそらく動的生成なんだろうね。.htmlって、あ、SEOかSEOそんなに(というより、負荷対策よりも)大事なのか?

物理的にサーバー増設とか変更するにしてもリバースプロキシとかロードバランサとかでURL変更しない手は無いのか。それ以前にキャッシュ (現金じゃなくて!) という言葉をご存知か?(サーバー側でどう処理してるかは知らんけど少なくともクライアントキャッシュは使われないわけだし) ステータスコード云々よりも負荷対策きちんとやったりURL変更しなくて良い手を考えるのが先、ってもう遅いか。

カテゴリ
タグ


このブログを書いている人
野田純生の写真
野田 純生 (のだ すみお)

大阪府出身。ウェブアクセシビリティエバンジェリスト。 アルファサード株式会社の創業者であり、現役のプログラマ。経営理念は「テクノロジーによって顧客とパートナーに寄り添い、ウェブを良くする」。 プロフィール詳細へ