[L1] HTTP 301、302、307 重定向状态码有什么语义差异?如何选择?
一句话结论
301 永久重定向(可缓存、允许方法变更),302 临时重定向(历史实现允许方法变更),307 严格临时重定向(强制保持原请求方法),选型依据是"是否永久"和"是否需要保持 POST 方法"。
体系讲解
核心对比
| 状态码 | 语义 | 是否可被缓存 | 方法是否可变 | 典型场景 |
|---|---|---|---|---|
| 301 Moved Permanently | 永久迁移 | ✅ 可缓存(浏览器/CDN 长期记住) | ⚠️ 允许变为 GET | HTTP → HTTPS、域名变更、旧 URL 规范化 |
| 302 Found | 临时重定向 | ❌ 默认不缓存 | ⚠️ 允许变为 GET(历史行为) | 登录后跳转、A/B 测试、临时维护页 |
| 307 Temporary Redirect | 临时重定向(严格) | ❌ 默认不缓存 | ✅ 必须保持原方法 | API 临时迁移(POST 表单、REST API) |
方法变更的历史问题
HTTP/1.0 规范要求:对 301/302 响应,若原请求为 POST,重定向后应继续使用 POST。但早期浏览器实现将 POST 重定向后改为 GET,这一错误行为后来被事实标准化。307 正是为了弥补这一缺口,在 HTTP/1.1 中明确语义:无论原方法是什么,307 必须保持原方法重放请求。
308 Permanent Redirect
308 是 301 的严格版本:永久迁移 + 强制保持原方法,对应 307 与 302 的关系。
临时 vs 永久 允许方法变更 强制保持方法
302 302 307
301 301 308SEO 与缓存影响
- 301:搜索引擎将链接权重(PageRank)转移到新 URL,浏览器长期缓存;使用后如需撤销,缓存清除困难。
- 302/307:搜索引擎保留原 URL 的权重,适合临时跳转。
考察意图
- 考察候选人是否理解 HTTP 重定向状态码的语义差异
- 验证是否了解 POST 重定向场景下 302 与 307 的行为差异
- 检验是否能根据"永久/临时"、"方法保持"两个维度进行正确选型
追问链
用户提交表单(POST)后,服务器希望跳转到结果页,应该用哪个状态码?
简答:应用 303 See Other(POST/Redirect/GET 模式)。303 明确指示客户端用 GET 方法访问目标 URL,是 Web 表单防止"刷新重复提交"的标准做法。302 虽然实践中行为相同,但语义上 303 更精确。
如果将 HTTP 全站重定向到 HTTPS,用 301 还是 302?为什么?
简答:用 301。HTTP → HTTPS 是永久迁移,301 告知浏览器和搜索引擎永久记住新地址,浏览器后续直接访问 HTTPS,减少一次 HTTP 请求;302 每次都会发起 HTTP 请求再跳转,不利于安全(首次请求仍为明文)。配合 HSTS 效果更佳。
301 重定向的缓存有什么潜在风险?
简答:301 默认被浏览器永久缓存(无需服务器介入),一旦发出后若需要撤销,用户必须手动清除缓存才能看到变更。因此对于不确定是否永久的跳转,应优先用 302/307,待确认后再改为 301。
易错点
用 302 重定向 POST 请求却期望保持 POST:浏览器遇到 302 会将 POST 改为 GET 重放(历史行为),若 API 迁移需要保持 POST 方法,必须使用 307。
混淆 302 与 303 的表单重定向:表单提交后的重定向应使用 303(POST/Redirect/GET 模式),而非 302。虽然浏览器对二者行为相同,但 303 语义更清晰,且明确禁止重放 POST,是规范用法。
随意使用 301 导致难以回滚:301 会被浏览器长期缓存,若业务变动需要撤销跳转,已缓存的客户端无法感知。在正式切换前应先用 302 验证跳转逻辑,再改为 301。
代码示例
<?php
// 301:永久重定向(HTTP → HTTPS 全站迁移)
function redirectPermanent(string $url): never
{
header('Location: ' . $url, true, 301);
exit;
}
// 302:临时重定向(登录跳转、A/B 测试)
function redirectTemporary(string $url): never
{
header('Location: ' . $url, true, 302);
exit;
}
// 307:临时重定向,保持原请求方法(API 临时迁移)
function redirectTemporaryStrict(string $url): never
{
header('Location: ' . $url, true, 307);
exit;
}
// 303:POST/Redirect/GET 模式(表单提交后跳转结果页)
function redirectAfterPost(string $url): never
{
header('Location: ' . $url, true, 303);
exit;
}
// 使用示例
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET' && !isset($_SERVER['HTTPS'])) {
// HTTP 全站跳 HTTPS → 301
redirectPermanent('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
}
if ($method === 'POST' && isset($_POST['form_data'])) {
// 表单处理完毕 → 303 跳转结果页
redirectAfterPost('/result?id=123');
}