PHPの「正しい」リダイレクト方法と、HTTPステータスコード

最終更新:2018-02-08 by Joe

PHP関数「header()」を使った、HTTPリダイレクト(転送)処理に関してです。

特にウェブサイトのSEOの観点では、HTTPステータスコードをケアしたリダイレクト処理を行うことが重要となります。

この記事では、本当にSEO面で安全なリダイレクト処理について、掘り下げて行きます。

PHPでのリダイレクト処理の書き方

まず、実際のPHPでのリダイレクト(転送)の記述例からです。HTTPのヘッダを出力するためのheader()関数を使います。

<?php

// リダイレクト先のURLへ転送する
$url = 'http://www.example.com/';
header('Location: ' . $url, true, 301);

// すべての出力を終了
exit;

数行のコードでリダイレクトが完了しますので、非常に簡単に処理を追加することができます。ただし、この処理には、いくつか知っておくべきことがあります。

header() 関数によるリダイレクト時の注意点

上記の処理に関して、3点の抑えるべきポイントです。

  1. HTTPステータスコードを加えてリダイレクトする
  2. リダイレクト関数の実行前に何も出力しないようにする
  3. リダイレクト実行後「exit;」を付記する

それでは、それぞれについて下記に詳しく補足します。

1HTTPステータスコード「301」を加えてリダイレクトする

SEOの観点で、意図に合致するHTTPステータスコードをリダイレクト時に発信することは、Google自身も推奨しています。検索エンジンに実装者の「リダイレクトの意図」を明確に伝える手段だからです。

多くの場合、リダイレクト処理が必要になるケースは「URLが変更になったので、旧アドレスへのアクセスを永久に転送したい」という場合のほうが多いと思います。その場合、HTTPヘッダーに含めるべきステータスコードは301(Moved permanently)です。これはheader関数の第四引数で渡すことができます。

リダイレクトに関するステータスコードは「30X」で指定されます。リダイレクト関連のHTTPステータスコードには、下記のようなものがあります。

リダイレクト系の主要なHTTPステータスコード

HTTP Status Code 定義
301 Moved permanently
302 Found
303 See other
307 Moved temporarily

PHPでは、このheader関数に引数を渡さない場合、デフォルトは「302 (Found)」となってしまいます。302は、通常「一時的な転送」を意味するシグナルです。

301と302の違い

永続的な転送であれば、301 (Moved permanently) を発信することで、Googleの検索エンジンが、「永続的な転送」のメッセージを認識します。この場合、GoogleはもともとのURLが持っていたページに対する評価を新しいURLに引き継いでくれます。(302や、ステータスコードがなくても、評価を引き次ぐ可能性があります。でも、きちんと301で明示した方が、間違いなく無難でしょう。)

さらに、一般的にWEBブラウザーは302のシグナルに対して、「一時的な転送」というメッセージを認知しますので、URLに対してページのキャッシュをローカルに永続的には残さないように実装されるようです。ブラウザがローカルキャッシュをいつまでも参照し続けてしまえば、サーバー側の更新により「一時的な転送」が終了したタイミングを判断できないからです。

場合によっては、302に対してはそのセッションの期間だけキャッシュ保持する、301に対しては長期的にキャッシュを保持するなど、シグナルの意味に合わせて最適な振る舞いが実装が行われているようですが、このキャッシュに関する仕様はブラウザ依存となります。

その他のHTTPステータスコードについて

302、303と307は、いずれも「一時的な転送」を意味します。規定の仕様上の詳細な違いありますが、ブラウザ側の実装はそれほど「大きくは」変わりません。(ブラウザ側の解釈・実装次第なので、詳しく追求しません)

一方で、WEBサイトやWEBページの一時転送を伝えるための最も一般的なコードは、302 (Found) とされていますので「Webサイトの引っ越しやURL構造の変更に利用するの301か、302のいずれかである」と考えて問題無いはずです。

詳細の仕様が気になる方は、HTTPの仕様を追いかけてみて下さい。とくに日本語のWiki解説は分かり易いです。

また、Google のJohn Mueller氏が過去にこれについて言及しています。興味があればご一読下さい

詰まるところは、Google自身がページの移動のシグナルとしての301の利用を公式に謳っており、特にURL全体が変更になる「WEB全体サイトの引っ越し」などには、301による転送処理の利用を強く推奨しています

転送の目的に合わせて、正しいシグナルを発信すべきでしょう。

参考リンク:

2リダイレクト関数の実行前に何も出力しないようにする

もう一つの注意点は、出力のタイミングです。例えば、下記のようなPHPリダイレクト処理は、エラーが生じてしまいます。

エラーの例

<!DOCTYPE html>
<?php 
// リダイレクト先のURLへ転送
$url = 'http://www.example.com/';
header('Location: ' . $url, true , 301);

// 出力を終了
exit;

問題なのは、PHPは「文字列の出力が発生した時点でheaderを送信してしまう」という点です。上記は、リダイレクト関数の実行前に、DOCTYPEの記述が発生してしまっています。

header関数によってHTTP headerを送信するためには、header関数より上に、いかなる文字列も出力してはいけません。(改行もだめ、空文字もだめ)特に、複数の人がフィアルを更新している場合、まれにBOM有のUTF-8を使用している場合がありますので、ご留意下さい。目に見える文字列がなくても、出力が発生してしまいます。

参考:

3リダイレクト実行後「exit;」を付記する

実際、このexitはなくても、リダイレクト自体は問題なく動作します。しかし、例えばクローラー(検索エンジンのボット)などのロボットは、HTTPヘッダー情報を無視してページの内のソースの読み込みを進めることがあります

そのようなケースでも、exit; を明記することで、古いURL側のコンテンツを、インターネット上に残さない事を徹底できます

なお「exit();」でも「exit;」でもどちらでも構いません。これに関してはPHPドキュメントに記載をご覧ください。

参考:

以上となります。これら3点を抑えれば、SEOセーフなリダイレクトとなります。

参考リンク

下記、PHPのリダイレクトに関する参考リンクです。PHPマニュアルは情報量が多いです。Google ヘルプもぜひ見ておいて下さい。


【補足】Wordpressテーマ内でリダイレクト関数の利用

おまけで、補足情報ーワードプレスにおけるリダイレクト処理です。

WordPressの応答速度とリダイレクトのタイミングを考える

WordPressテーマでは、初めてのテンプレート出力(例:index.phpの出力)が開始するより先に、プラグインのロード、テーマのロード、URLのパース、クエリの生成、実行・・などなど、多くの処理が行われます。

特に、「Wordpressメインクエリ」は、Wordpressがページを表示を行うために必ず毎回走る処理で、比較的重い処理です。(一概にいえないですが、だいたい1秒〜くらいはかかります)

WordPressでリダイレクトするときは、できるだけ早いタイミングでheaderを応答させることが必要です。さもなくば、すべてのユーザがリダイレクト前に数秒待たされることになります。

.htaccessの利用も検討すべき

もっとも早い応答タイミングで、転送設定が現実的なのは、WEBサーバーレベルの応答ですので、基本的には(Apacheであれば).htaccessでリダイレクトを記述すべきです。

一方で、複雑な設定や、複雑な条件分岐を行うとなると、htaccessの記法の制約で行うよりは、表現力の高いPHPレベルでリダイレクト処理を行うことも、しばしば有効な選択肢になります。

アクションフックの利用

WordPressテーマ内から素早く適切なタイミングで転送を行うため、アクションフックを利用するのがよいでしょう。

テーマから利用できる、一番最初のフックは、「after_setup_theme」です。)これ以前のフックは、テーマファイルが読み込まれていない時点で発生するフックなので、テーマ内部からは、関数を差し込む事ができません。)

実際には、下記をfunctions.phpに書き込むことで、functions.phpがロードされた直後に実行されます。(この時点ではまだ、メインクエリが実行されていません。)これは、functions.php内に記述された関数や変数を利用して分岐処理を追加するが可能なことを意味します。

add_action( 'after_setup_theme', 'my_redirect_url' );

function my_redirect_url() {
  // 転送先URL
  $new_link = 'https://example.com/new-page-url'

  if ( preg_match( "#my_old_page_url#", $_SERVER['REQUEST_URI'] ) ) {
    header('Location: ' . $new_link, true, 301 );
    exit;
  }
}

なお、上記は、ページURLに「my_old_page_url」がふくまれるときだけ転送、という正規表現を利用して条件をつけた例です。 (#をデリミタに使うのはただの好みです。これによりURL文字列内のスラッシュをエスケープしなくて済みます)

実際は、予期せぬリダイレクトが発生しないよう、できるだけ厳しくマッチングを取ったほうが無難でしょう。条件が複雑なほどテストする量が増えますし、予期せぬ検討漏れが発生します。慎重に設定しましょう。

リダイレクト管理プラグインの利用

WordPress環境において、リダイレクト処理の追記が頻繁に発生するような環境では、プラグインの利用も有効な手段です。例えば、「Redirection」は、記述したリダイレクト処理をDBで管理できる、最も一般的なプラグインです。

リダイレクト プラグイン

設定次第で、htaccessレベルでも、PHPレベルでも、好きなようにリダイレクトが設定できます。旧URLへのアクセス数をカウントしてくれるなどの付加的な機能もあります。リダイレクトの管理が煩雑になりそうであれば、一度試してみる価値はあるでしょう。