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;
}
<?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 顯示頁面...
?>
<?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>
<?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; });
<?php
// 僅在無法發送 Header 時使用 (已有輸出)
header('Refresh: 3; url=https://example.com'); // HTTP Header 方式
// 或 HTML Meta
echo '<meta http-equiv="refresh" content="3;url=https://example.com">';
headers_sent() 檢查或 ob_start() 緩衝。exit,防止後續邏輯執行/輸出干擾。htmlspecialchars 輸出到頁面 (延遲跳轉頁),防 XSS。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 策略 多個維度。核心原則:
「重定向是 Web 應用的交通指揮棒——指揮錯誤,用戶迷路;指揮失當,攻擊者闖入。」—— 記住:每一次 header('Location: ...') 都要經過安全審視。