301 是永久重定向。它会被浏览器和搜索引擎长期记住,所以用错了很难改回来。
2XX 都结束了,3XX 才开始干活:客户端还要再走一步。这一节是 3XX 里最危险的一个——因为「永久」。
一、永久意味着什么
301 的语义是「这个资源以后都在新地址了」。 客户端可以缓存这个结论,搜索引擎会把收录地址换掉,书签和外部链接的价值也会转移过去。 这正是它的用途:站点改域名、HTTP 迁 HTTPS、URL 结构调整——都该用 301, 因为你要的就是「以后别再来老地址」。 但反过来,误用 301 的后果是灾难性的:浏览器可能把跳转缓存几个月, 你改回来之后老用户仍然被送到错误的地方,而且清不掉。
二、代价是多一个往返
Demo 里客户端请求 /old,服务端回 301 加 Location: /new,
客户端再来一次才拿到内容——总共两次请求。
跨城按 40 毫秒算,这一次跳转就多花 40 毫秒。
所以重定向链越长越慢,实践中要求「一跳到位」,别 A→B→C。
能不能省掉?能:服务端直接在内部转发(rewrite),不发 301。
但那样地址栏不变,用户看到的还是老 URL——SEO 上也拿不到好处。
三、301 与 308 的关系
308 Permanent Redirect 是 301 的「保住方法」版本:301 在历史上允许客户端把 POST 改成 GET,
308 明确禁止。
所以做 API 的永久迁移要用 308,做页面迁移用 301。
这个区分是 2014 年之后才补上的(RFC 7538),书里没有。
还有一条现实约束:301 的响应正文通常为空,但标准允许带一段说明文字,老客户端会显示它。
现代客户端基本忽略。
四、动手 Demo:跟着 301 走一跳
服务端对 /old 回 301 并带 Cache-Control: max-age=31536000,
客户端读到 Location 后再请求一次。
if path == u'/old':
return (u'HTTP/1.1 301 Moved Permanently',
[(u'Location', u'/new'),
(u'Cache-Control', u'max-age=31536000')], b'')
target = hd.get(u'location')
s.sendall(_req(target=target))
实跑输出:
--------------------------------------------------------------------
301 Moved Permanently:永久搬家,下次别再问
--------------------------------------------------------------------
第 1 跳 /old → HTTP/1.1 301 Moved Permanently(Location: /new)
第 2 跳 /new → HTTP/1.1 200 OK
客户端请求次数 2 次
服务端 /old 被访问 1 次
/new 被访问 1 次
301 的语义 永久。可以被缓存、可以被搜索引擎改收录
301 的代价 多一个往返(跨城 40 毫秒就是 40 毫秒)
301 最危险的地方是「永久」:一旦浏览器或 CDN 缓存住,
你想改回来都难——用户那边的缓存可能保留几个月。
所以「临时跳转」一定用 302/307,别图省事用 301。
第 1 跳 /old → 301(Location: /new)、第 2 跳 /new → 200 OK。
两跳才拿到内容,这就是重定向的成本。
服务端 /old 被访问 1 次、/new 被访问 1 次——这次是一比一,因为客户端没缓存。
真机上第二次访问可能一次都不来(缓存里直接跳到 /new)。
那个 max-age=31536000 是一年。
这就是「改不回来」的来源——浏览器会在一年内记住这个跳转,你删掉服务端配置都没用。
30 秒实验
- 把
Cache-Control改成no-store,想想第二次访问的行为会怎么变。 - 让
/new也回一个 301 指向/new2,看客户端会跟几跳(浏览器通常限 20 跳)。 - 把 301 换成 302,重跑,对比两次访问的差别(本例看不出,因为没缓存)。
常见误区
- 「301 只是跳转」——它是永久承诺,会被缓存一年甚至更久。
- 「临时维护用 301 省事」——必须用 302/307,否则用户会被永久困在维护页。
- 「301 能保住 POST」——历史上不一定,要保住方法得用 308。
自测三问(点开对答案)
1. 301 的「永久」会导致什么后果? —— 客户端和搜索引擎会长期记住它,配错之后很难撤销。 2. 页面迁移和 API 迁移分别该用哪个码? —— 页面用 301,API 用 308(保住方法与正文)。 3. 为什么重定向链要尽量短? —— 每一跳都要多一个往返,跨城 40 毫秒时三跳就是 120 毫秒。301 太永久了。下一节看「只是临时搬一下」的那个码。
- ☐ (本节完,下一节 4.3.2 302 Found)