SSR 中重定向和 HTTP 状态码应该如何设计?
简化版
SSR 中重定向不能只靠客户端 location.href,因为首屏请求本身就是一次 HTTP 响应。更规范的做法是在服务端根据场景返回合适状态码:临时跳转用 302 或 307,永久迁移用 301 或 308;页面不存在返回 404;服务端异常返回 500;无权限可以返回 401、403 或跳登录页。状态码会影响浏览器行为、搜索引擎收录和 CDN 缓存。
详细版
SSR 页面既是前端页面,也是服务端 HTTP 响应。状态码设计不清楚,会导致 SEO 错误、缓存错误、监控误报和用户体验问题。
例如商品下架后如果仍返回 200,只在页面里写“商品不存在”,搜索引擎和监控都会认为页面正常。登录态失效如果只在客户端跳转,用户会先看到错误页面再跳走,还会造成首屏闪烁。
面试里要把重定向分成业务跳转、权限跳转、资源迁移和异常降级,不同场景选择不同状态码。
完整版教学
一、SSR 为什么要关注状态码
CSR 应用常常返回一个固定的 index.html。
SSR 则可以针对每个 URL 返回不同 HTML 和状态码。
这让 SSR 更适合 SEO,但也要求前端理解 HTTP 语义。
状态码不是后端独有问题,SSR 页面负责人也要参与设计。
二、常见状态码选择
| 场景 | 建议状态码 | 说明 |
|---|---|---|
| 页面永久换地址 | 301 / 308 | 有 SEO 迁移含义 |
| 登录后临时跳转 | 302 / 307 | 不代表资源永久迁移 |
| 内容不存在 | 404 | 搜索引擎应知道不存在 |
| 无权限访问 | 401 / 403 / 302 | 取决于产品策略 |
| 服务端异常 | 500 | 便于监控识别 |
| 服务降级可展示 | 200 + 降级内容 | 页面仍可正常服务 |
三、301、302、307、308 的区别
301 和 308 表示永久重定向。
302 和 307 表示临时重定向。
301、302 在历史实现里可能改变请求方法。
307、308 更强调保持原请求方法和请求体。
页面 GET 请求里常用 301、302 已经足够,但接口和表单跳转要更谨慎。
四、代码示例
export async function handleProductPage(params) {
const product = await getProduct(params.id);
if (!product) {
return renderNotFound({ status: 404 });
}
if (product.canonicalPath !== params.path) {
return redirect(product.canonicalPath, 301);
}
return renderProduct(product, { status: 200 });
}
这里的重点是状态码跟业务语义一致,而不是全部返回 200。
五、SEO 相关影响
永久迁移适合用 301 或 308,把权重传递到新 URL。
临时活动页跳转不要误用 301,否则搜索引擎可能长期记住旧规则。
不存在的内容要返回 404 或 410,而不是软 404。
规范 URL 可以配合 canonical 标签,但它不能替代正确的 HTTP 状态码。
SEO 场景里,HTML 文案和 HTTP 状态码都重要;只改页面内容是不完整的。
六、权限跳转的取舍
登录页跳转常用 302。
API 未登录常用 401。
已登录但无权限常用 403。
对于需要 SEO 的公开页,未登录也可以返回 200,只隐藏个性化区域。
对于私有页面,服务端直接重定向可以减少首屏闪烁。
七、误区和追问
- 误区:SSR 页面统一返回 200 最简单。 简单但会破坏 SEO、监控和缓存判断。
- 误区:客户端跳转可以替代服务端重定向。 客户端跳转发生得更晚,搜索引擎和首屏体验都受影响。
- 误区:所有跳转都用 301。 301 有永久含义,活动页和登录跳转不适合乱用。
- 追问:商品下架返回 404 还是 200? 如果资源不可访问且没有替代内容,倾向 404;如果是可解释的下架页,可结合业务和 SEO 策略决定。
- 追问:无权限返回 403 还是跳登录? 未登录可 302 到登录页,已登录无权限更适合 403 或权限说明页。
- 追问:状态码会影响 CDN 吗? 会,不同状态码可能被不同策略缓存,错误缓存会放大事故。
八、面试收束
回答可以落在“HTTP 语义、用户体验、SEO、缓存、监控”五个维度。
这样能体现你不是只会写页面,也理解 SSR 的服务端响应责任。