HTTP 的重定向和跳转


楔子

在最开始的时候我们说过,为了实现在互联网上构建超链接文档系统的设想,蒂姆·伯纳斯 - 李发明了万维网,使用 HTTP 协议传输超文本,让全世界的人都能够自由地共享信息。超文本里含有超链接,可以从一个超文本跳跃到另一个超文本,对线性结构的传统文档是一个根本性的变革。能够使用超链接在网络上任意地跳转也是万维网的一个关键特性,它把分散在世界各地的文档连接在一起,形成了复杂的网状结构,用户可以在查看时随意点击链接、转换页面。再加上浏览器又提供了前进、后退、书签等辅助功能,让用户在文档间跳转时更加方便,有了更多的主动性和交互性。

那么,点击页面链接时的跳转是怎样的呢?具体一点,比如在 Nginx 的主页上点了一下 download 链接,会发生什么呢?结合上面的内容,稍微思考一下就能得到答案:浏览器首先要解析链接文字里的 URI,再用这个 URI 发起一个新的 HTTP 请求,获取响应报文后就会切换显示内容,渲染出新 URI 指向的页面。

这样的跳转动作是由浏览器的使用者主动发起的,可以称为 主动跳转,但还有一类跳转是由服务器来发起的,浏览器使用者无法控制,相对地就可以称为 被动跳转,这在 HTTP 协议里有个专门的名词,叫做 重定向(Redirection)。

重定向的过程

其实之前我们就已经见过重定向了,在介绍 3×× 状态码时说过:301 是永久重定向,302 是临时重定向,浏览器收到这两个状态码就会跳转到新的 URI。也就是说一次重定向实际上发送了两次 HTTP 请求,但如果不用开发者工具的话,你是完全看不到这个跳转过程的,也就是说,重定向是用户无感知的。

如果发生了重定向,那么响应头中会多出一个 Location 字段,它就是 301/302 重定向跳转的秘密所在。Location 字段属于响应字段,必须出现在响应报文里,但只有配合 301/302 状态码才有意义,因为它标记了服务器要求重定向的 URI。浏览器收到 301/302 报文,会检查响应头里有没有 Location。如果有,就从字段值里提取出 URI,发出新的 HTTP 请求,相当于自动替我们点击了这个链接。

而在 Location 里的 URI 既可以使用绝对 URI,也可以使用相对 URI。所谓绝对 URI,就是完整形式的 URI,包括 scheme、host:port、path 等。所谓相对 URI,就是省略了 scheme 和 host:port,只有 path 和 query 部分,是不完整的,但可以从请求上下文里计算得到,比如这个报文是 https://www.baidu.com 返回的,那么就会将 https://www.baidu.com 和 相对 URI 拼接起来进行跳转。所以在重定向时如果只是在站内跳转,你可以放心地使用相对 URI;但如果要跳转到站外,就必须用绝对 URI。

重定向状态码

最常见的重定向状态码就是 301 和 302,另外还有几个不太常见的,例如 303、307、308 等。它们最终的效果都差不多,让浏览器跳转到新的 URI,但语义上有一些细微的差别,使用的时候要特别注意。

301 俗称永久重定向(Moved Permanently),意思是原 URI 已经永久性地废弃,今后的所有请求都必须改用新的 URI。浏览器看到 301,就知道原来的 URI 过时了,就会做适当的优化。比如历史记录、更新书签,下次可能就会直接用新的 URI 访问,省去了再次跳转的成本。搜索引擎的爬虫看到 301,也会更新索引库,不再使用老的 URI。

302俗称临时重定向(Moved Temporarily),意思是原 URI 处于临时维护状态,新的 URI 是起顶包作用的临时工。浏览器或者爬虫看到 302,会认为原来的 URI 仍然有效,但暂时不可用,所以只会执行简单的跳转页面,不记录新的 URI,也不会有其他的多余动作,下次访问还是用原 URI。

301/302 是最常用的重定向状态码,在 3×× 里剩下的几个还有:

  • 303 See Other:类似 302,但要求重定向后的请求改为 GET 方法,访问一个结果页面,避免 POST/PUT 重复操作
  • 307 Temporary Redirect:类似 302,但重定向后请求里的方法和实体不允许变动,含义比 302 更明确
  • 308 Permanent Redirect:类似 307,不允许重定向后的请求变动,但它是 301永久重定向 的含义

不过这三个状态码的接受程度较低,有的浏览器和服务器可能不支持,开发时应当慎重,测试确认浏览器的实际效果后才能使用。

重定向的应用场景

理解了重定向的工作原理和状态码的含义,我们就可以在服务器端拥有主动权,控制浏览器的行为,不过要怎么利用重定向才好呢?使用重定向跳转,核心是要理解重定向和 永久 / 临时 这两个关键词。先来看什么时候需要重定向:

一个最常见的原因就是 资源不可用,需要用另一个新的 URI 来代替。至于不可用的原因那就很多了,例如域名变更、服务器变更、网站改版、系统维护,这些都会导致原 URI 指向的资源无法访问,为了避免出现 404,就需要用重定向跳转到新的 URI,继续为用户提供服务。

另一个原因就是 避免重复,让多个网址都跳转到一个 URI,增加访问入口的同时还不会增加额外的工作量。

决定要实行重定向后接下来要考虑的就是永久和临时的问题了,也就是选择 301 还是 302。

301 的含义是永久的。如果域名、服务器、网站架构发生了大幅度的改变,比如启用了新域名、服务器切换到了新机房、网站目录层次重构,这些都算是永久性的改变。原来的 URI 已经不能用了,必须用 301 永久重定向,通知浏览器和搜索引擎更新到新地址,这也是搜索引擎优化(SEO)要考虑的因素之一。

302 的含义是临时的。原来的 URI 在将来的某个时间点还会恢复正常,常见的应用场景就是系统维护,把网站重定向到一个通知页面,告诉用户过一会儿再来访问。另一种用法就是服务降级,比如在双十一促销的时候,把订单查询、领积分等不重要的功能入口暂时关闭,保证核心服务能够正常运行。

重定向的相关问题

重定向的用途很多,掌握了重定向,就能够在架设网站时获得更多的灵活性,不过在使用时还需要注意两个问题。

第一个问题是 性能损耗。很明显,重定向的机制决定了一个跳转会有两次请求 - 响应,比正常的访问多了一次。虽然 301/302 报文很小,但大量的跳转对服务器的影响也是不可忽视的。站内重定向还好说,可以长连接复用,站外重定向就要开两个连接,如果网络连接质量差,那成本可就高多了,会严重影响用户的体验。所以重定向应当适度使用,决不能滥用。

第二个问题是 循环跳转。如果重定向的策略设置欠考虑,可能会出现 A=>B=>C=>A 的无限循环,不停地在这个链路里转圈圈,后果可想而知。所以 HTTP 协议特别规定,浏览器必须具有检测循环跳转的能力,在发现这种情况时应当停止发送请求并给出错误提示。

补充

网页的入链接和出链接也是标记网页重要性的关键指标,最著名的就是 Google 发明的 PageRank。

300 Multiple Choices 也是一个特殊的重定向状态码,它会返回一个有多个链接选项的页面,由用户自行选择要跳转的链接,用的较少。

重定向报文里还可以用 Refresh 字段,实现延时重定向,例如 Refresh: 5; url=xxx 告诉浏览器 5 秒钟后再跳转。

与跳转相关的还有一个 Referer 和 Referrer-Policy(前者是个拼写错误,但已经将错就错),表示浏览器跳转的来源(即引用地址),可用于统计分析和防盗链。比如点击页面,如果你在 A 页面点击某个链接进入了 B 页面,那么对于 B 页面而言 Refer 就是 A。之前在做爬虫的时候,总是被反爬,原因就在于没有指定 Referer。因为某些页面需要你点击才能进去,如果你是直接输入一个 URI 的话,那么服务器可能就认为你是爬虫,这个时候通过在 header 中指定 Referer 告诉服务器,我是从 xxx 页面过来的。

301/302 重定向是由浏览器执行的,第一次请求得到 301/302 时,浏览器知道这是个重定向,会找到 Location 字段指定的 URI,然后向该 URI 继续发请求。因此对于服务器而言是外部重定向,相应的也就有内部重定向,内部重定向是在服务器内部跳转 URI,此时则不会发出 HTTP 请求,从而也就没有性能损失。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM