📘 第四章 返回结果的HTTP状态码
📄 一句话概括

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 秒实验

  1. 把 Cache-Control 改成 no-store,想想第二次访问的行为会怎么变。
  2. 让 /new 也回一个 301 指向 /new2,看客户端会跟几跳(浏览器通常限 20 跳)。
  3. 把 301 换成 302,重跑,对比两次访问的差别(本例看不出,因为没缓存)。

常见误区

  1. 「301 只是跳转」——它是永久承诺,会被缓存一年甚至更久。
  2. 「临时维护用 301 省事」——必须用 302/307,否则用户会被永久困在维护页。
  3. 「301 能保住 POST」——历史上不一定,要保住方法得用 308。
自测三问(点开对答案) 1. 301 的「永久」会导致什么后果? —— 客户端和搜索引擎会长期记住它,配错之后很难撤销。 2. 页面迁移和 API 迁移分别该用哪个码? —— 页面用 301,API 用 308(保住方法与正文)。 3. 为什么重定向链要尽量短? —— 每一跳都要多一个往返,跨城 40 毫秒时三跳就是 120 毫秒。

301 太永久了。下一节看「只是临时搬一下」的那个码。

相关笔记