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

404 表示「服务器收到了请求,但找不到这个资源」。 它是个正常响应,不是错误——和 500 完全不是一类。

📝 本节和前面几节的关系

上一节 403 是「有但不能看」,这一节 404 是「没有」。 这一对经常被故意混用,背后是安全与可用性的取舍。

一、404 与 410

410 Gone 表示「曾经有,永久没了」。 它比 404 更明确,搜索引擎看到 410 会更快删掉收录,而不是反复来试探。 Demo 里三个路径分别回 200、404、410,正文长度各不相同——其中 404 的正文 24 字节(一句中文提示), 410 的 12 字节。 什么时候用 410?下架商品、删除的文章、废弃的接口。 但要注意:一旦说「永久没了」,就别让它再回来,否则搜索引擎会困惑。

二、两个常见误用

误用一:权限不足回 404。这是安全模糊化,没错,但要和 403 的使用保持一致。 同一个系统里有的地方 403、有的地方 404,攻击者反而能从差异里推断。 误用二:接口出错全回 404。 这会把「路由没匹配」「业务对象没找到」「上游服务挂了」三种完全不同的情况混成一个码, 排查时无从下手。 正确做法:路由没匹配用 404,业务对象没找到也用 404,但正文里要区分(或者用不同错误码); 上游挂了用 502/504。

三、404 页面的价值

从产品角度看,404 页面是唯一一个用户不想要但一定会看到的页面。 好的 404 页会做三件事:承认找不到、给出可能的去处(首页、搜索、热门链接)、保持品牌一致性。 从工程角度看,404 的日志价值更高: 大量 404 往往意味着链接腐烂、改版没做重定向、或者有人在扫描。 按路径聚合看,前十个 404 路径基本就能定位问题。 还有一个细节:默认的服务器 404 页会暴露服务器类型和版本,自定义 404 页是第一道信息隐藏。

四、动手 Demo:200、404、410 三个响应对照

服务端按路径返回三种状态,分别打状态行和正文长度。

if path == u'/gone':
    return (u'HTTP/1.1 410 Gone', [(u'Content-Type', u'text/plain')],
            b'gone forever')
if path == u'/exists':
    return (u'HTTP/1.1 200 OK', [(u'Content-Type', u'text/plain')], b'here')
return (u'HTTP/1.1 404 Not Found',
        [(u'Content-Type', u'text/html; charset=utf-8')],
        u'<h1>页面不存在</h1>'.encode())

实跑输出:

--------------------------------------------------------------------
404 Not Found:没这个东西
--------------------------------------------------------------------
  /exists        HTTP/1.1 200 OK,正文 4 字节
  /nothing       HTTP/1.1 404 Not Found,正文 24 字节
  /gone          HTTP/1.1 410 Gone,正文 12 字节

  404 的含义                 服务器收到了请求,但找不到这个资源
  404 不是错                 它是正常响应,跟 500 完全不是一类
  410 与 404                 410 表示「曾经有,永久没了」,比 404 更明确
  为什么要区分               搜索引擎看到 410 会更快删掉收录
  两个常见误用:
  ① 权限不足时回 404 而不是 403——这叫「安全模糊化」,
     目的是不让攻击者探测哪些资源存在,很多系统故意这么做。
  ② 接口出错全回 404——那是把「路由没匹配」和「业务没找到」混在一起了。

/exists 200 OK,正文 4 字节、/nothing 404 Not Found,正文 24 字节、 /gone 410 Gone,正文 12 字节——三个码都有正文,404 不是「没有响应」。 这正是新手常犯的错:以为 404 就是「空响应」。 实际上它可以有完整的 HTML 页面、甚至可以是几十 KB 的品牌页。 410 与 404 410 表示「曾经有,永久没了」,比 404 更明确——搜索引擎对这两个码的处理不同, 这是它存在的全部理由。

30 秒实验

  1. 把 404 的正文换成很大的页面,看客户端会不会正常渲染(会)。
  2. 让 404 响应带 Cache-Control: max-age=3600,想想合不合理(不合理,资源回来了用户也看不到)。
  3. 用 curl -I 请求一个不存在的路径,看响应头里暴露了什么信息。

常见误区

  1. 「404 是错误」——它是正常响应,属于 4XX 但不算故障。
  2. 「404 表示服务器挂了」——服务器好好的,只是没有那个资源。
  3. 「所有找不到都该 404」——废弃资源用 410 更准确。
自测三问(点开对答案) 1. 404 与 410 的区别? —— 404 是「找不到」,410 是「曾经有、永久没了」。搜索引擎处理不同。 2. 为什么有的系统把无权限回成 404? —— 安全模糊化,避免攻击者通过状态码差异判断资源是否存在。 3. 大量 404 日志说明了什么? —— 链接腐烂、改版没做重定向,或者有人在扫描探测。

4XX 讲完了。最后一节进 5XX:服务端自己的问题。

相关笔记