首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP URL 跳轉實現大全:從基礎 Header 重定向到安全防範的最佳實踐

     編輯:PHP 相關知識

核心機制:HTTP Location Header

PHP 重定向本質是發送 Location 響應頭,瀏覽器收到 302 (Found) 或 301 (Moved Permanently) 狀態碼後自動跳轉。核心代碼:

<?php
header('Location: https://example.com/target', true, 302);
exit; // 關鍵:阻止後續代碼執行

基礎實現:動態目標跳轉

<?php
// redirect.php
$url = $_GET['url'] ?? '';
if ($url) {
    // 安全:補全協議,防止開放重定向
    if (!preg_match('~^https?://~i', $url)) {
        $url = 'https://' . $url;
    }
    header('Location: ' . $url, true, 302);
    exit;
}
// 顯示輸入表單
?>
<form method="get">
    <input name="url" placeholder="example.com" required>
    <button type="submit">跳轉</button>
</form>

安全陷阱:開放重定向漏洞

風險:攻擊者構造 redirect.php?url=evil.com/phishing,利用你的域名信任度誘騙用戶點擊,繞過郵件/IM 安全檢查,實施釣魚、OAuth 劫持、CSRF 配合攻擊。

防禦策略:白名單 + 域名校驗

<?php
function safeRedirect(string $target, array $allowedHosts = []): void {
    $parsed = parse_url($target);
    if (!$parsed || !isset($parsed['host'])) {
        // 無效 URL,跳轉首頁或報錯
        header('Location: /', true, 302);
        exit;
    }
    
    $host = strtolower($parsed['host']);
    
    // 1. 絕對白名單
    if (!empty($allowedHosts) && !in_array($host, $allowedHosts, true)) {
        // 不在白名單,記錄日誌、跳轉安全頁
        error_log("Blocked open redirect to: $host");
        header('Location: /security/blocked', true, 302);
        exit;
    }
    
    // 2. 同域/子域放行 (可選)
    $currentHost = strtolower($_SERVER['HTTP_HOST'] ?? '');
    if ($host !== $currentHost && !endsWith($host, '.' . $currentHost)) {
        // 跨域跳轉,要求用戶確認或記錄
        // header('Location: /confirm-redirect?url=' . urlencode($target));
        // exit;
    }
    
    // 3. 協議限制:僅允許 HTTPS
    if (($parsed['scheme'] ?? '') !== 'https') {
        $target = 'https://' . $host . ($parsed['path'] ?? '') . ($parsed['query'] ? '?' . $parsed['query'] : '');
    }
    
    header('Location: ' . $target, true, 302);
    exit;
}

function endsWith(string $haystack, string $needle): bool {
    return $needle === '' || substr($haystack, -strlen($needle)) === $needle;
}

進階場景與模式

1. POST-Redirect-GET (PRG) 模式:防止表單重複提交

<?php
// submit.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 處理業務:保存數據、發送郵件...
    $orderId = createOrder($_POST);
    
    // 重定向到 GET 頁面,瀏覽器歷史記錄替換為 GET
    header('Location: /order/detail?id=' . $orderId, true, 303); // 303 See Other
    exit;
}
// GET 顯示頁面...
?>

2. 延遲跳轉:倒計時 + 用戶可取消

<?php
// delay_redirect.php
$target = $_GET['url'] ?? '/';
$delay = 5; // 秒
?>
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>正在跳轉...</title></head>
<body>
    <p><span id="count">5</span> 秒後自動跳轉至 <a id="target" href="<?= htmlspecialchars($target) ?>"><?= htmlspecialchars($target) ?></a></p>
    <button onclick="cancel()">取消跳轉</button>
    <script>
        var t = 5;
        var timer = setInterval(function() {
            t--; document.getElementById('count').textContent = t;
            if (t <= 0) window.location.href = document.getElementById('target').href;
        }, 1000);
        function cancel() { clearInterval(timer); document.body.innerHTML = '已取消跳轉。'; }
    </script>
</body></html>

3. JavaScript 客戶端跳轉 (AJAX 後/特殊場景)

<?php
// 返回 JSON 指示前端跳轉
echo json_encode(['redirect' => 'https://example.com/dashboard']);
exit;

// 前端
fetch('/api/login', {method:'POST', body:formData})
    .then(r=>r.json())
    .then(d=>{ if(d.redirect) window.location.href = d.redirect; });

4. Meta Refresh / HTTP Refresh (最後手段)

<?php
// 僅在無法發送 Header 時使用 (已有輸出)
header('Refresh: 3; url=https://example.com'); // HTTP Header 方式
// 或 HTML Meta
echo '<meta http-equiv="refresh" content="3;url=https://example.com">';

狀態碼選型指南

| 狀態碼 | 含義 | 適用場景 | SEO 影響 | |--------|------|----------|----------| | **301** | Moved Permanently | 域名遷移、HTTPS 強制、永久 URL 變更 | 傳遞權重,搜索引擎更新索引 | | **302** | Found (临時移動) | **默認推薦**:登錄後跳轉、表單提交後、臨時活動頁 | 不傳遞權重,保留原 URL 索引 | | **303** | See Other | **PRG 模式**:POST 請求後重定向到 GET | 明確語義,瀏覽器強制 GET | | **307** | Temporary Redirect | 保持請求方法 (POST→POST) | 極少用,需保持請求體 | | **308** | Permanent Redirect | 永久重定向且保持方法 | 極少用 |

避坑檢查表

  • [ ] Header 之前無任何輸出 (echo、空白、BOM、錯誤信息) → headers_sent() 檢查或 ob_start() 緩衝。
  • [ ] Header 後立即 exit,防止後續邏輯執行/輸出干擾。
  • [ ] URL 經過 htmlspecialchars 輸出到頁面 (延遲跳轉頁),防 XSS。
  • [ ] 開放重定向防禦:白名單、域名校驗、協議強制 HTTPS。
  • [ ] 狀態碼語義正確:永久用 301,臨時/POST 後用 302/303。
  • [ ] 相對路徑轉絕對:header('Location: ' . $request->getSchemeAndHttpHost() . '/path')。

框架封裝參考

<?php
// 通用 RedirectResponse 類
class RedirectResponse {
    public static function to(string $url, int $status = 302, array $allowedHosts = []): never {
        // 安全校驗...
        header('Location: ' . $url, true, $status);
        exit;
    }
    public static function back(int $status = 302): never {
        $referer = $_SERVER['HTTP_REFERER'] ?? '/';
        self::to($referer, $status);
    }
    public static function route(string $name, array $params = [], int $status = 302): never {
        $url = Router::generate($name, $params); // 假設路由器
        self::to($url, $status);
    }
}

總結

PHP URL 跳轉看似簡單,實則涉及 HTTP 協議語義、安全防禦、用戶體驗、SEO 策略 多個維度。核心原則:

  1. 安全第一:嚴防開放重定向,白名單校驗目標域名。
  2. 語義正確:301 永久、302 臨時、303 PRG、307/308 保持方法。
  3. 執行徹底:Header 前無輸出、Header 後即 exit。
  4. 體驗友好:必要時提供倒計時、取消選項、清晰提示。
「重定向是 Web 應用的交通指揮棒——指揮錯誤,用戶迷路;指揮失當,攻擊者闖入。」—— 記住:每一次 header('Location: ...') 都要經過安全審視。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved