301、302、307 我一开始分不清

Nuxt 与服务端渲染

改 URL 的时候要考虑旧地址怎么跳,我就用过 "redirect": '/new' 这种写法,然后以为它是永久重定向。

区别在两点:能不能被缓存,以及权重转不转移。

301 永久。浏览器会记住,之后再访问旧地址直接跳,不再问服务器。搜索引擎会把旧地址的权重转移到新地址,并逐步把旧地址从索引里去掉。用在"这个页面搬走了,不会再回来"。

302 临时。每次都问服务器,搜索引擎会保留旧地址、同时收录新地址,长期看就是两个地址并存。用在 A/B、临时活动页、登录后回跳。

307 和 308 分别是 302 和 301 的"严格版本",区别是明确要求请求方法不变。302 在实践中很多浏览器会把 POST 变成 GET,307 不允许这么干。所以只有涉及非 GET 方法的重定向时,307/308 才是对的选择。

我用错的地方在这:框架的 redirect 配置默认给的是 307,不是我以为的 301。看它的归一化代码:

// 传字符串时只会补上 to,statusCode 用默认值
{ to: '/', statusCode: 307, ...你传的内容 }

要 301 必须显式写成对象:

'/Old': { redirect: { to: '/new', statusCode: 301 } }

后果不是报错,是搜索端长期保留两个地址、权重分散。这种问题不会当天暴露,是几个月后看收录数据才会发现——这也是为什么我认为永久跳转必须写对:它是那种"错了也没人立刻知道"的东西。

还有一件更重要的事,是我在旧地址跳转上犯的另一个错,性质完全不同:那条规则被框架注册成了客户端路由,而路由匹配不区分大小写,于是它自己跳自己,把页面主线程打死。这个 bug 的表现是整页卡死,跟"状态码不对"完全是两个量级的问题。所以后来凡是涉及大小写的旧地址跳转,我都放到服务端中间件里做,不进 routeRules。

判断标准我最后简化成一句:这个 URL 是不是永远不再回到旧内容。是,就 301;不是,就 302。307 只在需要保方法的时候用,普通页面跳转用不上。

验证方式是一条命令,同时看状态码和 Location:

curl -sI -o /dev/null -w '%{http_code} %{redirect_url}\n' https://example.com/Old

只看 -I 的输出容易漏:301 和 307 在响应头形状上只差一个字段名,肉眼看会看错。

相关阅读