開発ブログ

WWWクリエイターズが送る、Git、CSS、HTML、コマンドライン、Macの便利機能など、開発に関する役立ち情報発信します。気まぐれに更新。

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

最終更新:2017-08-13 by Joe

PHP関数によるでHTTPリダイレクト(転送)処理に関してです。

PHPのリダイレクト関数紹介は、他の記事でもよく見かけますが、HTTPステータスコードに関する言及が以外に少ないようです。SEOの観点では、HTTPステータスコードをケアしたリダイレクトが重要です。

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

PHPでのリダイレクト処理の記述例

まず、PHPでのリダイレクト(転送)の記述例からです。

<?php

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

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

これに関して、3点のポイントがあります。

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

下記に詳しく補足します。

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

SEOの視点で、「リダイレクトの目的」に関する情報をヘッダーに追加すべきです。

正しくステータスコードをリダイレクト時に適応することは、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は、長期的に、など、シグナルの意味に合わせて最適な振る舞いが実装が行われているようですが、この仕様はブラウザ依存のようです。

残りのステータスコードは?

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

一方で、ウェブページの一時転送において最も一般的なコードは、302 (Moved permanently) とされていますので、使うのは301か、302のどちらかと考えて問題無いはずです。

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

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

詰まるところは、Google自身が301シグナルの利用を公式に謳っており、特に、URL全体が変更になる「サイトの引っ越し」などには、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; でもどちらでも構いません。

以上、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へのアクセス数をカウントしてくれるなどの付加的な機能もあります。リダイレクトの管理が煩雑になりそうであれば、一度試してみる価値はあるでしょう。

閉じる