HOWTO · PHP

PHP で前のページに戻る

このチュートリアルでは、PHP で前のページに戻る方法を紹介します

このページの内容

フォームがあるページに訪問者を戻すには、$_SERVER から任意項目の HTTP_REFERER を読み取り、許可したローカルの遷移先だけを受け入れ、レスポンスを出力する前に header() で Location ヘッダーを送信します。リファラーが常に存在するとは限らないため、安全なローカルの代替先も用意してください。

HTTP_REFERER リクエストヘッダーを使って PHP で前のページに戻る

index.php のフォームは、home.php などの処理用スクリプトに送信できます。ブラウザーはフォームがあるページの URL を HTTP_REFERER リクエストヘッダーに含めて送信する場合があり、PHP では $_SERVER['HTTP_REFERER'] から取得できます。ブラウザーやプライバシー設定によってこのヘッダーが省略されることがあり、クライアントが任意の値を送ることもできるため、検証せずにその値へリダイレクトしてはいけません。このヘッダーはリクエストに付随するヒントであり、ブラウザー履歴を確実に示すものではありません。ヘッダーがない場合もアプリケーションが動作するようにしてください。

index.php では、フォームの送信とともにメッセージを渡せます。

<form action="home.php" method="post"><input type="hidden" name="message" value="Saved."><button type="submit">Submit</button></form>

次の home.php の完全なハンドラーは、ローカルの /index.php パスだけを受け入れます。この例では str_contains() を使用するため、PHP 8.0 以降が必要です。/index.php に既存のクエリパラメーターがあれば保持し、送信されたメッセージを追加します。リファラーがない場合や別の場所を指している場合は、/index.php に戻します。コマンドラインで実行すると、サンプルのリクエストを使ってリダイレクト先を表示するため、ローカルでも成功する流れを確認できます。Web リクエストでは、303 リダイレクトを送信して処理を終了します。

<?php
// home.php
$isCli = PHP_SAPI === 'cli';

// Provide a sample request when running this file directly with PHP CLI.
if ($isCli) {
    $_SERVER['REQUEST_METHOD'] = 'POST';
    $_SERVER['HTTP_REFERER'] = 'https://example.test/index.php?tab=profile';
    $_POST['message'] = 'Saved.';
}

if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
    http_response_code(405);
    exit;
}

$message = $_POST['message'] ?? 'Your form was submitted.';
if (!is_string($message)) {
    $message = 'Your form was submitted.';
}

$destination = '/index.php';
$referrer = $_SERVER['HTTP_REFERER'] ?? '';
$parts = parse_url($referrer);
if (is_array($parts) && ($parts['path'] ?? '') === '/index.php') {
    $query = [];
    parse_str($parts['query'] ?? '', $query);
    $destination = '/index.php';
    if ($query !== []) {
        $destination .= '?' . http_build_query($query);
    }
}

$separator = str_contains($destination, '?') ? '&' : '?';
$target = $destination . $separator . http_build_query(['message' => $message]);

if ($isCli) {
    echo $target, PHP_EOL;
    exit;
}

header('Location: ' . $target, true, 303);
exit;

サンプルのリクエストでは、ハンドラーは次のリダイレクト先を表示します。

/index.php?tab=profile&message=Saved.

戻り先のページでは、メッセージを HTML に挿入する前にエスケープしてください。クエリパラメーターは配列として届くこともあるため、まず値が文字列であることを確認します。例: htmlspecialchars(is_string($_GET['message'] ?? null) ? $_GET['message'] : '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')。ハンドラーでの URL エンコードはクエリパラメーターを保護し、表示時の HTML エスケープはページを保護します。文字列だけを受け取るエスケープ関数にほかの値を渡す場合も、同じように文字列かどうか確認してください。

ハンドラーはリファラーのホストを使わず、許可済みのローカルパスから遷移先を組み立てるため、外部 URL がリダイレクト先になることはありません。パスの許可リストは意図的に限定しています。アプリケーションに複数のフォームページがある場合は、受け入れるローカルパスをそれぞれ明示的に対応付け、リストにない値には安全なローカルの代替先を用意してください。この例では、受け入れたリファラーのクエリパラメーターを保持したうえで、http_build_query() を使って message パラメーターを追加または置換します。これにより URL エンコードを手作業で組み立てずに済み、リダイレクトパスとクエリ文字列を分けて扱えます。

header() は HTML、空白、その他のレスポンスを出力する前に実行する必要があります。リダイレクト後の exit によって、リクエストが通常どおり続いたかのように後続のハンドラーコードが実行されるのを防げます。303 ステータスは、フォームの POST の後にブラウザーが GET リクエストでリダイレクト先を開くよう指示します。CLI 分岐は動作を示すための補助にすぎません。Web リクエストでは、PHP は遷移先をページ内容として表示するのではなく、レスポンスヘッダーを送信します。ホスティング環境の PHP が古い場合は、str_contains($destination, '?') を strpos($destination, '?') !== false のような互換性のあるチェックに置き換え、アプリケーションのほかの部分もそのバージョンに対応していることを確認してください。