PHPで正しく404エラーページにリダイレクトする方法

CMSで作成したWebサイトやシステムで検索エンジンに悪い評価をもらわないようにするポイントとして、404(Not Found)ページを正しく伝えるというものがあります。

正しく404ページを表示させる方法には、Webサーバーの「ErrorDocument」を指定する方法がありますが、システムの柔軟性を考えるとPHPでエラーページを表示させてなんらかのカスタムメッセージなどを表示させたいもの。

ところが、実際にネット上を検索してみると「PHPで404ページをリダイレクト表示させる」としながらも、実際にはそう作られていない例が多数見受けられました。 そこで、今回は正しくステータスコード404を返しながら、柔軟にエラーページを表示させる方法について書いてみたいと思います。

404ページを表示させているつもりで、実際に異なるステータスコードを返している状態を「ソフト404エラー」と呼びます。 正しい404ステータスコードが返せないと、検索エンジンが「存在するページ」として認識してしまい、サイトの評価を下げられてしまう可能性があります。 これはSEOで上位表示したいサイトにとって良くない傾向です。

404ステータスコードの返し方が間違っている例

一見、合っているようで実際は間違っている例。

$redirectUrl = "http://www.example.com/404.html";
header("HTTP/1.0 404 Not Found");
header("Location: $redirectUrl");
exit;

最初にheader()で「HTTP/1.0 404 Not Found」を設定しているのは良いのですが、次のheader() で「Location」指定をしているのが良くない。 これだと実際はステータスコードとして302(Moved Temporarily)が返されてしまいます。

しかもそのリダイレクト先にはページが存在するため、ステータスコード200(OK)が返されてしまいます。 これでは検索エンジンに「お、URLが一時的にこっちに変わったのね。」と間違って解釈されてしまいます。

よく使われるHTTPステータスコード一覧

コード番号ステータス意味
200OK成功(問題なし)
301Moved Permanentlyリダイレクト先のURLにページが(永久的に)移動した
302Moved Temporarilyリダイレクト先のURLにページが(一時的に)移動した
403Forbiddenページへのアクセスがサーバーによって禁止されている
404Not Foundページが見つからなかった

404ステータスコードの返し方が正しい例

正しい例でもheader() で「HTTP/1.0 404 Not Found」を返しますが、そのままエラーページのHTMLをfile_get_contents() で読み込んで表示させます。

$redirectUrl = "http://www.example.com/404.html";
header("HTTP/1.0 404 Not Found");
print(file_get_contents($redirectUrl));
exit;

正確に言うと「リダイレクト」という呼び方は適切ではなく、404ページを読み込んで表示しているということですね。

一つ注意しないといけないのは、Locationのリダイレクトと違ってURLが変わらないので、エラーページに表示させる画像やスタイルシートなどの外部ファイルを相対パスで指定していると、mod_rewrite の場合と同じように階層がずれてリンク切れになってしまうおそれがあります。

それを防ぐためには、エラーページに表示させる画像やスタイルシートファイル等を絶対パスで指定しておくように変更しましょう。

<img src="../img/header.gif" ... />
          ↓
<img src="/path/to/img/header.gif" ... />

このページをシェアする

1 件のコメント

  • codebydeer より:

    こんにちは、はじめまして。codebydeerといいます。
    私も、ほかの記事を見て「その方法は302になるんだよなぁ」などと思っておりました(笑)

    そして、私もこの方法でやろうと試していたのですが、唯一この方法には欠点があります。
    exit;構文はphpのスクリプトを停止させるだけであり、出力を終了して送信するというわけではありません。
    したがって、このスクリプトの後にphpの閉じタグ、生のHTMLが書かれている場合、出力にはそれらも含まれてしまいます。

    今回コメントするにあたって、phpファイルに生のHTMLを書くのがあまり良くないことは承知していますが、それでも必要になってくる場面などはあると思われます。

    そこでなのですが、実はPHPにもgoto構文がありまして。
    ページの最後に
    <?php
    exit;
    //通常時は最後まで行ってここで処理終了
    err404:
    http_response_code(404);
    echo file_get_content("[ErrorDocument]");
    exit;

    err403:
    //以下省略

    のようにすると、エラーが出たときにgoto分を使って最終行まで飛ばすという荒業を使うことができます。
    ただしこの場合、注意が必要なのが、関数内でのエラー処理です。
    PHPのgotoは、関数の内側から外側への移動を許されていません。
    関数の結果をcatchして、メインコンテキストでgotoをさせる必要があると思われます。

    他にHTMLの出力までスルー出来る方法があれば、ぜひご教授いただきたいです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

2011-05-10