PHPのheader関数のLocationを使ったリダイレクトについて

採用はこちら

PHPのheader()関数を使うと、ブラウザに対してHTTPヘッダーを送信できます。

その中でも、Locationヘッダーを指定すると、ユーザーを別のURLへリダイレクトできます。

<?php
header('Location: /thanks.php');
exit;

このコードでは、現在のページにアクセスしたユーザーを/thanks.phpへ移動させます。

header('Location: ...')は、フォーム送信後の完了ページへの移動、ログイン後のマイページ遷移、未ログインユーザーのログインページへの誘導など、さまざまな場面で使われます。

ただし、使い方を間違えるとリダイレクトが動かなかったり、意図しない処理が実行されたり、セキュリティ上の問題につながったりすることがあります。

そのため、header()によるリダイレクトでは、特に次の3点が重要です。

・header()は出力前に実行する
・header('Location: ...')の後にはexitを書く
・目的に応じてステータスコードを指定する
目次

header関数でLocationリダイレクトする基本形

基本的な書き方

PHPでリダイレクトする基本形は次のとおりです。

<?php
header('Location: /target-page.php');
exit;

Location:の後に移動先のURLやパスを書きます。

たとえば、同じサイト内の完了ページへ移動させたい場合は、次のように書きます。

<?php
header('Location: /contact/thanks.php');
exit;

このようにすると、ユーザーは/contact/thanks.phpへ転送されます。

exitを書く理由

header('Location: ...')を書いたあとには、基本的にexit;を書きます。

理由は、header()でリダイレクト指示を送っても、PHPの処理自体は自動的に停止しないためです。

たとえば、次のコードは危険です。

<?php
if (!$is_login) {
    header('Location: /login.php');
}

delete_user_data();

この場合、ログインしていないユーザーをログインページへ移動させるつもりでも、exit;がないため、その後のdelete_user_data();が実行される可能性があります。

正しくは、次のように書きます。

<?php
if (!$is_login) {
    header('Location: /login.php');
    exit;
}

delete_user_data();

リダイレクト後に処理を止めることで、意図しない処理の実行を防げます。

ステータスコードを明示する書き方

header('Location: ...')だけでも、多くの場合は一時的なリダイレクトである302として扱われます。

ただし、実務では意図を明確にするため、ステータスコードを指定する書き方がおすすめです。

<?php
header('Location: /target-page.php', true, 302);
exit;

header()の第3引数に、HTTPステータスコードを指定できます。

たとえば、フォーム送信後の完了ページへ移動させる場合は、303を使うと意味が明確です。

<?php
header('Location: /contact/thanks.php', true, 303);
exit;

通常の一時的な転送なら302、恒久的なURL変更なら301、フォーム送信後の完了ページ遷移なら303が候補になります。

header関数のLocationが使われる主な場面

フォーム送信後に完了ページへ移動する

お問い合わせフォームや申し込みフォームでは、送信処理が終わったあとに完了ページへリダイレクトすることがよくあります。

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 入力チェック
    // データベース保存
    // メール送信

    header('Location: /contact/thanks.php', true, 303);
    exit;
}

フォーム送信後にリダイレクトすることで、ブラウザの再読み込みによる二重送信を防ぎやすくなります。

このような流れは、一般的にPRGパターンと呼ばれます。

Post → Redirect → Get

ユーザーがフォームをPOST送信したあと、サーバー側で処理を行い、完了ページへリダイレクトします

完了ページはGETで表示されるため、再読み込みしてもフォームが再送信されにくくなります。

ログイン後にマイページへ移動する

ログイン処理でも、header('Location: ...')はよく使われます。

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $email = $_POST['email'] ?? '';
    $password = $_POST['password'] ?? '';

    // 実際にはDBからユーザー情報を取得して認証する
    if ($login_success) {
        session_regenerate_id(true);
        $_SESSION['user_id'] = $user_id;

        header('Location: /mypage/', true, 302);
        exit;
    }

    $error = 'メールアドレスまたはパスワードが違います。';
}

ログインに成功した場合は、セッションにユーザー情報を保存し、マイページへリダイレクトします。

なお、ログイン成功後にはsession_regenerate_id(true);を実行すると、セッション固定攻撃への対策としてより安全です。

未ログインユーザーをログインページへ移動する

会員専用ページに未ログインユーザーがアクセスした場合、ログインページへリダイレクトさせることがあります。

<?php
session_start();

if (empty($_SESSION['user_id'])) {
    header('Location: /login.php', true, 302);
    exit;
}

このようにすると、ログインしていないユーザーが会員専用ページへ直接アクセスしても、ログインページへ誘導できます。

ログアウト後にログインページへ移動する

ログアウト処理後に、ログインページやトップページへ移動させることもあります。

<?php
session_start();

$_SESSION = [];

if (ini_get('session.use_cookies')) {
    $params = session_get_cookie_params();

    setcookie(
        session_name(),
        '',
        time() - 42000,
        $params['path'],
        $params['domain'],
        $params['secure'],
        $params['httponly']
    );
}

session_destroy();

header('Location: /login.php', true, 302);
exit;

セッション情報を削除したあと、ログインページへリダイレクトします。

header関数を使うときの重要な注意点

header関数の前に出力してはいけない

header()を使うときに最も注意すべき点は、header()より前に出力してはいけないということです。

HTTPヘッダーは、HTML本文より前に送信される必要があります。

そのため、次のようなコードは問題になる可能性があります。

<?php
echo 'こんにちは';

header('Location: /thanks.php');
exit;

echoで文字を出力したあとにheader()を実行しているため、ヘッダーを変更できない場合があります。

このとき、次のようなエラーが表示されることがあります。

Warning: Cannot modify header information - headers already sent

これは、「すでに出力が始まっているため、ヘッダー情報を変更できません」という意味です。

HTMLを先に書くとリダイレクトできないことがある

次のように、PHPの前にHTMLを書いている場合も注意が必要です。

<!DOCTYPE html>
<html lang="ja">
<body>
<?php
header('Location: /thanks.php');
exit;
?>
</body>
</html>

この場合、<!DOCTYPE html><html>がすでに出力されているため、header()によるリダイレクトが失敗する可能性があります。

リダイレクト処理は、HTMLを出力する前に行う必要があります。

PHPタグの外の空白や改行にも注意する

PHPファイルの先頭に空白や改行があるだけでも、環境によっては出力として扱われることがあります。

悪い例です。

 
<?php
header('Location: /thanks.php');
exit;

一見わかりにくいですが、<?phpの前に空白や改行があると、すでに出力が始まったと判断されることがあります。

また、UTF-8 BOM付きで保存されたファイルでも、BOMが出力として扱われる場合があります。

PHPファイルは、基本的に「UTF-8 BOMなし」で保存するのがおすすめです。

includeしたファイルの出力にも注意する

共通ヘッダーなどをincludeしたあとにリダイレクトしようとすると、すでに出力が始まっている可能性があります。

悪い例です。

<?php
include 'header.php';

if ($is_success) {
    header('Location: /thanks.php');
    exit;
}

header.phpの中でHTMLや空白が出力されていると、その後のheader()が失敗することがあります。

正しくは、リダイレクト判定を先に行い、リダイレクトしない場合だけHTMLを出力します。

<?php
if ($is_success) {
    header('Location: /thanks.php');
    exit;
}

include 'header.php';

このように、処理の順番を意識することが大切です。

Locationに指定するURLの書き方

相対パスを指定する

Locationには、相対パスを指定できます。

<?php
header('Location: thanks.php');
exit;

この場合、現在のURLを基準にしてthanks.phpへ移動します。

たとえば、現在のURLが次の場合、

https://example.com/contact/form.php

リダイレクト先は次のようになります。

https://example.com/contact/thanks.php

同じディレクトリ内のページへ移動したい場合は、この書き方でも問題ありません。

ルート相対パスを指定する

実務では、ルート相対パスを使うことがよくあります。

<?php
header('Location: /contact/thanks.php');
exit;

先頭に/を付けると、サイトのルートを基準にしたパスになります。

現在のページの階層に影響されにくいため、同一サイト内のリダイレクトでは扱いやすい書き方です。

絶対URLを指定する

外部サイトへ移動させる場合や、URLを明確に指定したい場合は、絶対URLを使います。

<?php
header('Location: https://example.com/contact/thanks.php');
exit;

現在の多くのブラウザでは相対パスも使えますが、互換性や明確さを重視する場合は、絶対URLまたはルート相対パスを使うと安心です。

リダイレクトのステータスコード

301リダイレクト

301は、恒久的なリダイレクトを意味します。

<?php
header('Location: https://example.com/new-page/', true, 301);
exit;

旧URLから新URLへ完全に移転した場合などに使います。

たとえば、サイトリニューアルでURLが変わった場合や、SEO上の評価を新しいURLへ引き継ぎたい場合に使われます。

ただし、301はブラウザや検索エンジンにキャッシュされやすいため、テスト段階で安易に使うのは避けた方がよいです。

テスト中は302を使い、恒久的な移転が確定してから301を使うのが安全です。

302リダイレクト

302は、一時的なリダイレクトを意味します。

<?php
header('Location: /temporary-page.php', true, 302);
exit;

一時的に別ページへ移動させたい場合に使います。

ログイン後のマイページ遷移、条件分岐による転送、キャンペーンページへの一時的な誘導などで使われることがあります。

header('Location: /path/')のようにステータスコードを省略した場合、多くの場合は302として扱われます。

ただし、意図を明確にするためには、次のように302を明示するのがおすすめです。

<?php
header('Location: /path/', true, 302);
exit;

303リダイレクト

303 See Otherは、フォーム送信後の完了ページ遷移に向いているステータスコードです。

<?php
header('Location: /thanks.php', true, 303);
exit;

303は、「処理結果は別のURLをGETで取得してください」という意味を持ちます。

そのため、POST送信後にサンクスページへ移動させたい場合に適しています。

お問い合わせフォームや申し込みフォームの完了ページでは、次のような流れになります。

1. ユーザーがフォームをPOST送信する
2. サーバー側で入力チェック、保存、メール送信などを行う
3. 303で完了ページへリダイレクトする
4. ユーザーはGETで完了ページを表示する

この形にすると、再読み込みによる二重送信を防ぎやすくなります。

307リダイレクト

307は、一時的なリダイレクトです。

ただし、302と異なり、元のHTTPメソッドを維持します。

たとえば、POSTで送信されたリクエストは、リダイレクト先にもPOSTのまま送られます。

<?php
header('Location: /temporary-endpoint.php', true, 307);
exit;

POSTメソッドを維持したまま一時的に別のURLへ転送したい場合に使います。

308リダイレクト

308は、恒久的なリダイレクトです。

301と似ていますが、元のHTTPメソッドを維持する点が特徴です。

<?php
header('Location: /new-endpoint.php', true, 308);
exit;

恒久的にURLを変更しつつ、POSTなどのメソッドを維持したい場合に使われます。

ただし、一般的なWebページのURL変更では、301が使われることが多いです。

301・302とメソッド変更の注意点

301302は、歴史的な経緯により、POSTリクエスト後のリダイレクトでブラウザがGETに変えて再リクエストすることがあります。

そのため、フォーム送信後の完了ページ遷移では、302よりも303を使った方が意味として明確です。

一方で、リダイレクト先でもPOSTを維持したい場合は、307308を検討します。

フォーム送信後の実用例

お問い合わせフォームのリダイレクト例

お問い合わせフォームでは、POST送信後に入力チェックやメール送信を行い、成功したら完了ページへ移動させます。

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name = $_POST['name'] ?? '';
    $email = $_POST['email'] ?? '';
    $message = $_POST['message'] ?? '';

    if ($name === '' || $email === '' || $message === '') {
        $error = '未入力の項目があります。';
    } else {
        // DB保存やメール送信などの処理

        header('Location: /contact/thanks.php', true, 303);
        exit;
    }
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>お問い合わせ</title>
</head>
<body>

<?php if (!empty($error)): ?>
    <p><?= htmlspecialchars($error, ENT_QUOTES, 'UTF-8') ?></p>
<?php endif; ?>

<form method="post">
    <input type="text" name="name">
    <input type="email" name="email">
    <textarea name="message"></textarea>
    <button type="submit">送信</button>
</form>

</body>
</html>

このコードでは、HTMLを出力する前にPOST処理とリダイレクト処理を行っています。

未入力がある場合はエラーを表示し、送信に成功した場合だけ完了ページへリダイレクトします。

二重送信を防ぎやすくする

フォーム送信後にそのまま完了メッセージを表示すると、ユーザーがブラウザを再読み込みしたときに、POSTデータが再送信される可能性があります。

しかし、送信処理後にリダイレクトして完了ページをGETで表示すれば、再読み込みしてもフォームが再送信されにくくなります。

そのため、お問い合わせフォームや申し込みフォームでは、次のような書き方が実務上よく使われます。

<?php
// POST処理成功後
header('Location: /thanks.php', true, 303);
exit;

フォーム処理では、303を使うと「POST後にGETで完了ページを表示する」という意図が明確になります。

クエリパラメータ付きでリダイレクトする方法

URLにパラメータを付ける

リダイレクト先にクエリパラメータを付けることもできます。

<?php
header('Location: /search.php?keyword=php');
exit;

この場合、/search.php?keyword=phpへリダイレクトします。

検索結果ページやエラーメッセージの受け渡しなどで使われることがあります。

http_build_queryを使う

動的にパラメータを作る場合は、http_build_query()を使うと安全です。

<?php
$params = [
    'keyword' => 'PHP header 関数',
    'page' => 1,
];

header('Location: /search.php?' . http_build_query($params));
exit;

生成されるURLは、次のようになります。

/search.php?keyword=PHP+header+%E9%96%A2%E6%95%B0&page=1

日本語やスペースを含む値をそのままURLに入れると不具合の原因になることがあります。

そのため、クエリパラメータを組み立てる場合は、http_build_query()を使うのがおすすめです。

セキュリティ上の注意点

ユーザー入力をそのままLocationに使わない

リダイレクト先をユーザー入力から受け取る場合は注意が必要です。

悪い例です。

<?php
$url = $_GET['url'] ?? '';

header('Location: ' . $url);
exit;

このようなコードでは、攻撃者が任意のURLへユーザーを誘導できる可能性があります。

たとえば、次のようなURLが作られるかもしれません。

https://example.com/redirect.php?url=https://evil.example/

ユーザーは自社サイトのURLだと思ってアクセスしても、最終的に悪意ある外部サイトへ移動してしまう可能性があります。

このような問題を、オープンリダイレクトと呼びます。

許可リスト方式で制御する

外部URLへリダイレクトさせる場合は、許可するURLをあらかじめ決めておく方法が安全です。

<?php
$allowedUrls = [
    'https://example.com/',
    'https://example.com/campaign/',
];

$url = $_GET['url'] ?? '';

if (in_array($url, $allowedUrls, true)) {
    header('Location: ' . $url);
    exit;
}

header('Location: /');
exit;

このようにすれば、許可されたURLにだけリダイレクトできます。

IDでリダイレクト先を管理する

さらに安全にするなら、ユーザーにURLを直接指定させず、サーバー側でリダイレクト先を管理する方法があります。

<?php
$redirectMap = [
    'mypage' => '/mypage/',
    'profile' => '/mypage/profile.php',
    'orders' => '/mypage/orders.php',
];

$key = $_GET['redirect'] ?? 'mypage';

$redirect = $redirectMap[$key] ?? '/mypage/';

header('Location: ' . $redirect);
exit;

この方法では、ユーザーはmypageprofileといったキーだけを指定します。

実際のURLはサーバー側の配列で管理するため、外部サイトへ勝手に飛ばされるリスクを減らせます。

サイト内パスだけを許可する

ログイン後に元のページへ戻す場合など、サイト内のパスだけを許可したいケースもあります。

PHP 8以降であれば、str_starts_with()を使って次のように書けます。

<?php
$redirect = $_GET['redirect'] ?? '/mypage/';

if (!str_starts_with($redirect, '/')) {
    $redirect = '/mypage/';
}

if (str_starts_with($redirect, '//')) {
    $redirect = '/mypage/';
}

header('Location: ' . $redirect);
exit;

/mypage/のようなサイト内パスだけを許可し、https://evil.example///evil.example/のような外部URLを防ぎます。

ただし、str_starts_with()はPHP 8.0以降の関数です。

PHP 7系にも対応する必要がある場合は、次のようにstrpos()を使います。

<?php
$redirect = $_GET['redirect'] ?? '/mypage/';

if (strpos($redirect, '/') !== 0 || strpos($redirect, '//') === 0) {
    $redirect = '/mypage/';
}

header('Location: ' . $redirect);
exit;

header関数が効かない主な原因

すでに出力している

header()が効かない原因で最も多いのは、すでに出力が始まっていることです。

<?php
echo 'test';

header('Location: /');
exit;

このようなコードでは、echoによって本文が出力されたあとにheader()を呼び出しているため、リダイレクトできない可能性があります。

確認すべきポイントは次のとおりです。

・header()より前にechoやprintがないか
・PHPより前にHTMLを書いていないか
・ファイル先頭に空白や改行がないか
・include先のファイルで出力していないか
・UTF-8 BOM付きで保存されていないか
・WarningやNoticeなどのエラーが表示されていないか

エラー表示も出力として扱われる場合があります。

そのため、開発環境でエラーが表示されている場合も、header()が失敗する原因になります。

exitを書いていない

header('Location: ...')の後にexit;を書いていないと、リダイレクト後もPHPの処理が続いてしまいます。

悪い例です。

<?php
header('Location: /login.php');

// 下の処理が実行される可能性がある
update_data();

正しくは次のようにします。

<?php
header('Location: /login.php');
exit;

リダイレクト処理の直後には、基本的にexit;を書くと覚えておくとよいです。

URLの指定が間違っている

相対パスの指定ミスも、リダイレクトがうまくいかない原因になります。

<?php
header('Location: thanks.php');
exit;

この書き方では、現在のURLの階層によって移動先が変わります。

たとえば、現在のURLが次の場合、

https://example.com/contact/form.php

リダイレクト先は次のようになります。

https://example.com/contact/thanks.php

サイトルートからのパスを指定したい場合は、次のように先頭に/を付けます。

<?php
header('Location: /thanks.php');
exit;

リダイレクトループが起きている

リダイレクト先のページで、さらに同じリダイレクト処理が実行されると、リダイレクトループが発生します。

たとえば、/login.phpの中に無条件で次のコードを書いている場合です。

<?php
header('Location: /login.php');
exit;

この場合、/login.phpにアクセスしても、また/login.phpへリダイレクトされ続けます。

ブラウザでは「リダイレクトが繰り返し行われました」といったエラーが表示されることがあります。

リダイレクト処理を書くときは、現在のページやログイン状態を確認したうえで、必要な場合だけ実行することが大切です。

AjaxでLocationリダイレクトを使う場合の注意点

fetchでは画面全体が遷移しないことがある

通常のフォーム送信であれば、PHP側で次のように書くとブラウザ全体が移動します。

<?php
header('Location: /thanks.php');
exit;

しかし、JavaScriptのfetch()XMLHttpRequestでPHPを呼び出している場合、PHP側でLocationヘッダーを返しても、ブラウザの画面全体が期待どおりに移動しないことがあります。

通信としてはリダイレクトが処理されても、現在表示しているページ自体はそのままになることがあります。

AjaxではJavaScript側で遷移させる

Ajax処理では、PHP側でJSONを返し、JavaScript側で画面遷移させる方法がわかりやすいです。

PHP側の例です。

<?php
header('Content-Type: application/json; charset=UTF-8');

echo json_encode([
    'redirect' => '/thanks.php',
]);
exit;

JavaScript側の例です。

fetch('/contact/submit.php', {
    method: 'POST',
    body: formData
})
.then(response => response.json())
.then(data => {
    if (data.redirect) {
        window.location.href = data.redirect;
    }
});

通常のフォーム送信ならPHPのheader('Location: ...')で問題ありません。

一方、Ajax処理では、JavaScript側でwindow.location.hrefを使って移動させる方が制御しやすいです。

HTTPSやwww統一のリダイレクト

PHPでもHTTPSリダイレクトはできる

HTTPでアクセスされた場合に、HTTPSへリダイレクトすることもできます。

<?php
if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') {
    header('Location: https://example.com' . $_SERVER['REQUEST_URI'], true, 301);
    exit;
}

このコードでは、HTTPSでないアクセスをhttps://example.comへ転送しています。

ただし、$_SERVER['HTTP_HOST']をそのまま使うと、Hostヘッダー由来の値を使うことになるため、環境によっては注意が必要です。

PHPでドメインを指定する場合は、可能であれば固定ドメインを使う方が安全です。

Webサーバー側で設定する方が一般的

HTTPS化やwwwあり・なしの統一は、PHPよりもWebサーバー側で行う方が一般的です。

たとえば、Apacheの.htaccessやNginxの設定でリダイレクトする方法があります。

PHPでリダイレクトする場合、PHPが実行されるリクエストにしか対応できません。

一方、Webサーバー側で設定すれば、静的ファイルやPHP以外のリクエストも含めて統一的に処理できます。

そのため、サイト全体のURL正規化やHTTPS化は、基本的にはWebサーバー側で設定するのがおすすめです。

WordPressでリダイレクトする場合

wp_redirectを使う

WordPressでは、PHPのheader('Location: ...')を直接使うより、wp_redirect()を使うことが多いです。

<?php
wp_redirect(home_url('/thanks/'));
exit;

WordPressには独自の関数やフックの仕組みがあるため、テーマやプラグイン内ではWordPress用の関数を使う方が自然です。

wp_safe_redirectを使う

安全性を重視する場合は、wp_safe_redirect()を使います。

<?php
wp_safe_redirect(home_url('/mypage/'));
exit;

wp_safe_redirect()は、許可されていない外部URLへのリダイレクトを防ぐ目的で使われます。

ログイン後の遷移や会員ページへの誘導など、サイト内リダイレクトではwp_safe_redirect()を使うと安心です。

実行タイミングに注意する

WordPressでは、リダイレクト処理を書くタイミングにも注意が必要です。

すでにHTMLが出力されたあとにリダイレクトしようとすると、通常のPHPと同じように失敗する可能性があります。

そのため、テーマやプラグインでリダイレクト処理を行う場合は、適切なフックで早めに実行する必要があります。

たとえば、表示テンプレートが読み込まれる前に処理できるtemplate_redirectフックが使われることがあります。

<?php
add_action('template_redirect', function () {
    if (is_page('old-page')) {
        wp_redirect(home_url('/new-page/'), 301);
        exit;
    }
});

WordPressでは、出力が始まる前のタイミングでリダイレクトすることが重要です。

JavaScriptリダイレクトとの違い

PHPのLocationリダイレクトはサーバー側で行う

PHPのheader('Location: ...')は、サーバー側でリダイレクト指示を返す方法です。

<?php
header('Location: /thanks.php');
exit;

HTMLをブラウザに返す前に、別のURLへ移動させることができます。

ログイン判定、フォーム送信後の完了ページ、アクセス制限など、サーバー側の処理結果に応じて移動させたい場合に向いています。

JavaScriptリダイレクトはブラウザ側で行う

JavaScriptでは、次のようにページを移動できます。

window.location.href = '/thanks.php';

これはブラウザ側で実行されるリダイレクトです。

ページを表示したあとに、ユーザー操作やAjaxの結果に応じて移動させたい場合に使います。

用途に応じて使い分ける

PHPとJavaScriptのリダイレクトは、実行される場所が違います。

PHPのheader Location:サーバー側でリダイレクトする
JavaScriptのwindow.location:ブラウザ側でリダイレクトする

通常のフォーム送信やログイン処理では、PHPのheader('Location: ...')が向いています。

一方、Ajax処理やページ表示後の動的な遷移では、JavaScriptのwindow.location.hrefを使う方が自然です。

出力バッファリングを使う方法

ob_startで出力をバッファにためる

どうしても先に出力が発生してしまう場合、ob_start()で出力バッファリングを使う方法があります。

<?php
ob_start();

echo '何かの出力';

header('Location: /thanks.php');
exit;

ob_start()を使うと、出力をすぐにブラウザへ送らず、一時的にバッファへためることができます。

そのため、場合によってはheader()を後から実行できることがあります。

根本的な解決にはならない

ただし、ob_start()は根本的な解決ではありません。

本来は、リダイレクト判定をHTML出力より前に行う設計にするべきです。

たとえば、次のように処理を先に書きます。

<?php
if ($is_success) {
    header('Location: /thanks.php');
    exit;
}

// ここからHTML出力

ob_start()に頼るよりも、処理の順番を整理する方が、保守しやすく安全なコードになります。

よくある間違い

Locationのスペルミス

Locationのスペルを間違えると、リダイレクトできません。

悪い例です。

<?php
header('Locaton: /thanks.php');
exit;

正しくは次のとおりです。

<?php
header('Location: /thanks.php');
exit;

Locationのつづりに注意しましょう。

コロンを書き忘れる

Locationヘッダーでは、Location:のようにコロンが必要です。

悪い例です。

<?php
header('Location /thanks.php');
exit;

正しい例です。

<?php
header('Location: /thanks.php');
exit;

headerの前にHTMLを書いている

次のように、HTMLを先に書いてからheader()を実行すると失敗する可能性があります。

<!DOCTYPE html>
<html>
<?php
header('Location: /thanks.php');
exit;

リダイレクト処理は、HTML出力より前に実行します。

301をテストで使ってしまう

恒久的なリダイレクトである301は、ブラウザにキャッシュされることがあります。

テスト中に301を使うと、あとでコードを修正してもブラウザ側に古いリダイレクトが残る場合があります。

テスト段階では302を使い、本番でURL変更が確定してから301に切り替えると安全です。

ユーザー入力をそのままリダイレクト先にする

次のようなコードは避けるべきです。

<?php
header('Location: ' . $_GET['url']);
exit;

ユーザーが任意のURLを指定できるため、悪意ある外部サイトへ誘導される危険があります。

リダイレクト先は、許可リストやサーバー側のマッピングで制御しましょう。

実務でおすすめの書き方

通常の一時リダイレクト

通常の一時的なリダイレクトでは、302を明示します。

<?php
header('Location: /target-page.php', true, 302);
exit;

フォーム送信後のリダイレクト

フォーム送信後の完了ページ遷移では、303を使うと意味が明確です。

<?php
header('Location: /thanks.php', true, 303);
exit;

恒久的なURL変更

URLを恒久的に変更した場合は、301を使います。

<?php
header('Location: https://example.com/new-url/', true, 301);
exit;

ただし、301はキャッシュされやすいため、使用するタイミングには注意が必要です。

安全なリダイレクト先の指定

ユーザー入力を使ってリダイレクトする場合は、URLを直接受け取るのではなく、許可リストやID方式を使いましょう。

<?php
$redirectMap = [
    'home' => '/',
    'mypage' => '/mypage/',
    'contact' => '/contact/',
];

$key = $_GET['redirect'] ?? 'home';

$redirect = $redirectMap[$key] ?? '/';

header('Location: ' . $redirect, true, 302);
exit;

このようにすると、想定外の外部サイトへリダイレクトされるリスクを減らせます。

まとめ

PHPのheader()関数でLocationを指定すると、ユーザーを別のURLへリダイレクトできます。

基本形は次のとおりです。

<?php
header('Location: /target-page.php');
exit;

実務では、次のようにステータスコードを明示すると、意図がわかりやすくなります。

<?php
header('Location: /target-page.php', true, 302);
exit;

特に重要なポイントは、次のとおりです。

・header()はHTMLやechoなどの出力より前に実行する
・header('Location: ...')の後にはexitを書く
・通常の一時リダイレクトは302を使う
・フォーム送信後の完了ページ遷移では303が適している
・恒久的なURL変更では301を使う
・ユーザー入力をそのままリダイレクト先に使わない
・AjaxではPHP側ではなくJavaScript側で画面遷移させることが多い

header('Location: ...')はシンプルで便利な機能ですが、出力の順番、ステータスコード、セキュリティ対策を意識して使う必要があります。

フォーム送信後であれば、次のように書くのがおすすめです。

<?php
header('Location: /thanks.php', true, 303);
exit;

通常の一時的な画面遷移であれば、次のように書きます。

<?php
header('Location: /target-page.php', true, 302);
exit;

URLが恒久的に変更された場合は、次のように301を使います。

<?php
header('Location: https://example.com/new-url/', true, 301);
exit;

目的に応じてステータスコードを使い分け、exit;を忘れずに書くことで、安全でわかりやすいリダイレクト処理を実装できます。

以上、PHPのheader関数のLocationを使ったリダイレクトについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次