提醒:本页面将不再更新、维护或者支持,文章、评论所叙述内容存在时效性,涉及技术细节或者软件使用方面不保证能够完全有效可操作,请谨慎参考!

今天研究起WordPress的评论发布页 wp-comments-post.php ,假如我们提交一个不是期望的评论,比如我们什么都不写就点击提交评论,然后WordPress将交由 wp-comments-post.php 处理,当然这次处理将以失败告终,WordPress将显示一条错误消息,比如:“错误:请填写必填项目(姓名和电子邮件地址)。”。好了,这时如果我们右击查看源代码会找到一段奇怪的字符串:

WordPress wp-comments-post.php源文件
<!-- Ticket #11289, IE bug fix: always pad the error page with enough characters
such that it is greater than 512 bytes, even after gzip compression
abcdefghijklmnopqrstuvwxyz1234567890aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuu
vvwwxxyyzz11223344556677889900abacbcbdcdcededfefegfgfhghgihihjijikjkjlklkmlmln
mnmononpopoqpqprqrqsrsrtstsubcbcdcdedefefgfabcadefbghicjkldmnoepqrfstugvwxhyz
1i234j567k890laabmbccnddeoeffpgghqhiirjjksklltmmnunoovppqwqrrxsstytuuzvvw0wxx
1yyz2z113223434455666777889890091abc2def3ghi4jkl5mno6pqr7stu8vwx9yz11aab2bcc3
dd4ee5ff6gg7hh8ii9j0jk1kl2lmm3nnoo4p5pq6qrr7ss8tt9uuvv0wwx1x2yyzz13aba4cbcb5
dcdc6dedfef8egf9gfh0ghg1ihi2hji3jik4jkj5lkl6kml7mln8mnm9ono -->

看似杂乱无章的字符串,有什么实际作用呢?好在我看到了这么一句话 Ticket #11289, IE bug fix: always pad the error page with enough characters such that it is greater than 512 bytes, even after gzip compression ,貌似和IE什么Bug有关系,立即Google之,找到了WordPress原先的Bug处理页 《Internet Explorer, 512-byte error page fix doesn't work. Neglects gzip compression.》

通过上述描述得知当自定义HTTP错误页(Custom HTTP Error Page)体积过小,小于一个临界阈值(thresholds)时,IE浏览器将自动以内部错误页面(Microsoft-stylin' error page)来取代你的自定义错误页。

什么意思呢?首先我们要理解什么是自定义HTTP错误页。当然要理解HTTP状态码(HTTP Status Code),比如说我们找不到页面时服务器将返回一个404状态码,表示找不到页面,当我们进行301或者302重定向时也是通过这个状态码来进行,当然还要有一些附加信息,比如状态,这些信息是写到HTTP相应头(HTTP Response Header)里的,只有浏览器等客户端可以“看到”并理解这些信息,然后翻译传达给我们,这里给个找不到页面的status header:

HTTP/1.1 404 Not Found
Server: nginx
Date: Mon, 05 Mar 2012 08:20:41 GMT
Content-Type: text/html
Content-Length: 564
Connection: keep-alive
Keep-Alive: timeout=5

其中 HTTP/1.1 404 Not Found 这个就是我们要关注的,HTTP表示协议,1.1表示版本,404表示状态码,Not Found表示状态,那么这个Header等于告诉浏览器找不到页面,我刚才说了这个是由服务器发送的,当然也是我故意访问了一个不存在的页面,服务器才发送这个404消息,学过任何一种Web编程语言的同学知道,其实这个也是可以自定义的,比如在PHP中,我们这样写可以实现同样的功能,即使这个页面是存在的:

<?php header("HTTP/1.1 404 Not Found"); ?>

好了,这里我为了便于演示,换一种状态代码500,表示服务器错误:

<?php header('HTTP/1.1 500 Internal Server Error'); ?>

现在访问这个页面表明我们自定义了一个500内部服务器错误的HTTP错误页,而且浏览器等客户端也可以理解这种错误页,当然我们需要提供一些其他信息来告知我们的访问者,当前页面出错了:

<?php header('HTTP/1.1 500 Internal Server Error'); ?>
<html>
<head>
  <title>Error</title>
</head>
<body bgcolor="white">
  <center><h1>当前页面出错咯</h1></center>
</body>
</html>

小提示:header必须在任何HTML或者文本输出前发送,否则无效。

好啦,把上面的代码写入到500.php文件里并上传服务器,然后再通过浏览器浏览,Chrome下显示正常为“当前页面出错咯”我们设定的文本,IE下却显示其内部内置的“HTTP 500 内部服务器错误,无法显示网页,您要访问的网页有问题,无法显示。”,如下图所示:

这个页面很熟悉?是的,搞ASP开发的同学肯定有印象,而且需要修改“Internet 选项”,高级选项卡,去掉“显示友好 HTTP 错误信息”的钩才能将我们自定义的错误文本显示出来。

Internet 选项 - 显示友好 HTTP 错误信息

让我们的客户去改浏览器设置?呵呵,这当然是不现实的,可能有同学要问:为什么有些网站的自定义HTTP错误的自定义文本能够正常显示,比如一些站漂亮的404页面。说了这么多,这个就是我们今天要研究的问题。

让我们回到本文一开始那个IE Bug,其实这个Bug也反映了这个问题,当服务器响应的内容长度小于一定的临界阈值(thresholds)时,IE浏览器将自动忽略服务器在响应头发送完后的自定义文本,也就是我们设定的文字被IE忽略了,取而代之的是IE自家的内部错误页面(Microsoft-stylin' error page)。我们看下刚才那个页面的size:

对于500错误的阈值IE默认设置为512 bytes(更多的阈值稍后介绍),很明显刚才我们构造的页面不足512 bytes,所以IE没有显示我们的页面,好了,大家肯定在想,如果大于这个阈值(thresholds),那么我们的页面不就能够正常显示了?恭喜你,回答正确!比如我们将页面人为的填充垃圾值,使其大于512字节,当然为了不影响整体页面,我们将其放入到HTML注释中:

<?php header('HTTP/1.1 500 Internal Server Error'); ?>
<html>
<head>
  <title>Error</title>
</head>
<body bgcolor="white">
  <center><h1>当前页面出错咯</h1></center>
<!-- <?php echo str_repeat('*', 256); ?> -->
</body>
</html>

很明显现在页面size大于512这个阈值了,假如我们现在用IE访问这个页面,即使不关闭显示友好 HTTP 错误信息,也会显示我们自己的消息文本了。

500 Internal Server Error

当然这里需要注意的还有Gzip,如果说这里页面本来就大于512个字节,但是gzip压缩输出后页面却小于了512个字节,那么IE依旧会用自己的错误页取代我们的错误也,所以这里填充的垃圾值要考虑到这个问题,由于压缩的特性,重复字符压缩效率高,我们为了扩充体积不希望看到这个效果,所以需要使用不重复的乱字符填充,恐怕这也是为什么一开始WP的杂乱字符填充的原因吧。

竟然这个阈值有这样神奇的效果,那我们如何获取不同错误页面的IE默认临界阈值呢?下面列出一张表供大家参考:

代码 描述 文件大小(阈值)
400 Bad Request > 512 bytes
403 Forbidden > 256 bytes
404 Not Found > 512 bytes
405 Method Not Allowed > 256 bytes
406 Not Acceptable > 512 bytes
408 Request Time-out > 512 bytes
409 Conflict > 512 bytes
410 Gone > 256 bytes
500 Internal Server Error > 512 bytes
501 Not Implemented > 512 bytes
505 HTTP Version Not Supported > 512 bytes

当然上面那张表不是空穴来风,是根据IE配置来的,这个配置保存在注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main\ErrorThresholds ,你可以根据需要编辑修改这个值:

注册表修改IE错误页显示阈值(thresholds)

刚才看到Nginx的默认错误页,比如404错误,也应用了相应的技巧来避免这个问题:

Nginx 404 错误页源文件
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->

不过看字面意思,貌似Chrome也有这种特性?而且试了一下,貌似只有IE或者Chrome访问才会有这么几行HTML注释Padding。

另外某些浏览器外壳,可能会捕获相应的HTTP错误头,以便于显示其内置的页面,如果这样的话,这里的技巧就不起作用了,还是建议大家使用主流浏览器。

参考文档

  1. 10 Status Code Definitions
  2. List of HTTP status codes
  3. Why Won’t Internet Explorer Display Your WordPress Error Page (Or Any Custom HTTP Error Page, For That Matter)? Because It’s Too Small.
  4. Important Note for Your Custom Error Pages