乐者为王

Do one thing, and do it well.

构建一个更友好的404页面

用户访问网站时偶尔会遇到404错误。404是一个HTTP状态码,告诉用户请求的网页不存在或链接错误。大多数网站托管和Web服务器会提供自己的默认错误页面,但如果开发者有提供定制的404页面,就可以增强用户的体验,对搜索引擎也更友好。定制的404页面非常非常有用,它不仅提供有关错误信息给用户,也会提供即时反馈给开发者,以便问题能被尽快修复;还可以告知搜索引擎链接已经失效,不要再索引。

这里是我对定制404页面的一些思考。

问题的分析

为了提供有用且特定的信息给用户,需要界定404错误可能的原因。按照页面访问的来源,错误大致可以分为以下4种:

  1. 用户拼错URL地址或者使用过期的书签。
  2. 站内的失效链接。
  3. 其它网站返回的失效链接。
  4. 搜索引擎返回的失效链接。

其中后面3种都是由于链接被修改、页面被删除或被移到其它地方所致。

问题的解决

为了确定404错误的来源,我们的代码需要访问HTTP Referer,它会告诉我们用户是从哪个页面链接过来的。以下是大概的代码执行步骤:

  1. 检测HTTP Referer以确定404错误的来源;
  2. 显示适当的消息给用户;
  3. 如果是特定的错误,发送邮件给开发者。

问题1:用户拼错URL地址或者使用过期的书签

在这种情况下,HTTP Referer通常为空,所以我们需要使用下面的代码做检查:

1
if request.referer.blank?

然后是定制404页面上的显示消息,告诉用户问题是什么:

1
2
3
4
5
很抱歉,你试图访问的页面 http://example.com/no-such-page.html 不存在。

看起来你似乎拼错了URL地址或者使用了过期的书签。

你或许应该试试搜索这个网站或者使用我们的站点地图去找到你想要的东西。

因为这是用户导致的错误,且没有问题要被修复,所以这里就不需要发送邮件给开发者。

问题2:站内的失效链接

当HTTP Referer不为空时,要检查它是指向本站、其它网站还是搜索引擎。如果包含本站域名,那么就可以知道用户是从站内其它页面过来的。这里使用以下的代码检测:

1
unless request.referer.index(request.host).nil?

然后,我们就可以给用户显示消息,告诉他该页面的链接失效了。

1
2
3
很抱歉,你试图访问的页面 http://example.com/no-such-page.html 不存在。

很明显,我们的页面上出现了失效链接。已经给开发者发送了邮件,问题将会很快被修复。你无需做任何事情。

并且,在显示消息给用户的同时发送包含所有必要信息的邮件给开发者。邮件的标题要清晰地指出哪个域名站点上有失效链接,邮件的内容要告诉开发者用户所在页面以及请求页面的URL地址。

1
2
3
4
5
6
7
8
9
From: example.com 404 error

Subject: 在站点 example.com 上有失效链接

Message: 站内的失效链接

在 http://example.com/badlink.html 页面上似乎存在失效链接。
有人正试图通过这个页面去访问 http://example.com/no-such-page.html 页面。
你应该去检查下该页面是否有什么问题。

问题3:其它网站返回的失效链接

如果404错误是由其它网站上的失效链接造成,那么可以显示以下的信息给用户:

1
2
3
4
5
很抱歉,你试图访问的页面 http://example.com/no-such-page.html 不存在。

很明显,在你过来的页面上存在失效链接。我们已经注意到了这个情况,并将试图去联系该页面的所有者修复它。

你或许应该试试搜索这个网站或者使用我们的站点地图去找到你想要的东西。

同时发送邮件给开发者,让开发者访问有失效链接的页面,如果页面上有所有者的联系信息,那么就可以通知他们修复这个问题。

1
2
3
4
5
6
7
8
9
From: example.com 404 error

Subject: 在站点 domain.com 上有失效链接

Message: 其它网站返回的失效链接

在 http://domain.com/badlink.html 页面上似乎存在失效链接。
有人正试图通过这个页面去访问 http://example.com/no-such-page.html 页面。
你应该联系页面的所有者去检查下该页面有什么问题。

问题4:搜索引擎返回的失效链接

为了确定用户是来自搜索引擎的结果页面,需要用一个搜索引擎URL列表来检测HTTP Referer,这个列表是一个排序的文本文件,这样我们可以在任何时候更新列表而不需要修改代码。这里是做检查的代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
from_searchengine = false

IO.readlines('searchengines.txt') do |searchengine|
  unless request.referer.index(searchengine).nil?
    from_searchengine = true
    break
  end
end

if from_searchengine
  # do something
end

在这种情况下,我们要让用户知道搜索引擎返回的是旧链接:

1
2
3
4
5
很抱歉,你试图访问的页面 http://example.com/no-such-page.html 不存在。

看起来似乎是搜索引擎返回了旧页面的链接,等到搜索引擎更新索引后旧链接就会被移除。

你或许应该试试搜索这个网站或者使用我们的站点地图去找到你想要的东西。

因为没有什么我们可以做的,所以不需要发送邮件给开发者。

Comments