作弊条:在 Nginx 中正确配置自定义 404 页面
目录
最近给blog增加了一个自定义的404错误页面。像Hugo这样的静态网站生成器在构建时,默认会把渲染好的 404 页面直接输出在网站根目录下,即 /404.html。
针对这类页面,我们通常希望:
- 当访客访问不存在的网址时,服务器呈现该页面的内容,并且HTTP状态码必须是
404。 - 当有人(或爬虫)直接在浏览器中访问
/404.html时,同样呈现其内容,但返回的HTTP状态码依然必须是404,而不是200 OK。
第2点的意义在于, /404.html 本身是一个错误提示页面,而不是正常的文章内容。如果直接访问时返回 200 OK,从HTTP语义上看,它就成了一个「普通的」正常页面;搜索引擎则可能将这种情况判定为soft 404,甚至在无法正确识别时造成无意义的内容收录。
查阅Nginx官方文档中关于 error_page 的说明,最基础的配置方式如下:
error_page 404 /404.html;当客户端请求一个不存在的路径(例如 /non-existent)时,Nginx在常规流程中找不到对应资源,产生404错误,进而触发 error_page 进行内部重定向到 /404.html,并将404状态码和页面内容一并返回给客户端。
但这无法解决直接访问 /404.html 的情况。因为对于Nginx而言,根目录下确实存在一个名为 404.html
的物理静态文件。如果客户端直接请求 /404.html,Nginx就会将其当成普通静态资源处理,返回 200 OK。
解
想要解决这个问题,可以在配置中增加一个匹配 /404.html 的
location,并使用 internal 指令:
| |
工作原理
这其中比较重要的是 location 语句块。此处使用了 = 修饰符进行精确匹配。当请求的URI恰好是 /404.html 时,Nginx便会直接选中这个
location 块,不再继续寻找其他匹配。
接下来的 internal 则告诉Nginx这种请求只能由内部发起,如果是来自客户端的外部请求直接尝试访问该 location,Nginx会直接拒绝并返回 404 Not Found。而由 error_page、try_files 等产生的内部重定向,以及经过 rewrite 改写的请求,则属于Nginx所定义的「内部请求」,可以进入这个 location。
这样一来,普通的不存在页面仍按常规流程处理:Nginx产生 404 后,通过 error_page 内部重定向到 /404.html。由于这是内部请求,internal 不会阻止它,因此最终返回 404.html 的内容,同时保持404状态码。
比较复杂的是直接访问 /404.html 的情况。这个请求会首先命中
location = /404.html,但因为它来自客户端,会被 internal 拒绝而产生 404。随后 error_page 又把这个 404 内部重定向到 /404.html。第二次进入该 location 时已经是内部请求,因此可以正常读取文件,最后客户端看到的仍然是自定义页面和 404 状态码。
验证
配置生效后,可以分别通过浏览器和 curl 进行验证:
- 浏览器访问:直接在浏览器中访问一个不存在的网址(如
/non-existent/),以及直接访问/404.html,确认两种情况下都能看到自定义的404页面。(那边说开Developer Tools也能看到的同学请坐,如果页面比较复杂的话会产生大量结果,虽然还是能过滤但是不如curl来的直接) - 检查HTTP状态码:浏览器界面通常不会直接显示响应状态码。可以通过
curl检查直接访问两种情况时返回的HTTP状态码,确认返回的是404而非200:
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.net/non-existent
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.net/404.html
# 输出均应为:404