搞懂 HTTP 重定向 - 如何优雅地使用 301
高峰 高级前端进阶 1周前最近一段时间,连续遇到了两次跟重定向相关的问题,本着知己知彼百战百胜的态度,我决定深入了解一下,顺便跟大家分享一下。作为前端开发,大家对重定向一定不陌生,不就是永久重定向和临时重定向嘛,谁还不知道呢 😂。那么大家是否知道永久重定向和临时重定向的区别呢?如果不小心设置了永久重定向该如何取消呢?如何优雅地使用重定向呢?接下来就让我们来一探究竟吧。URL 重定向,能够将多个 URL 指向同一个页面,这一技术有着多种用途。在 HTTP 中有一个专门的响应,叫做 HTTP 重定向,也就是所有 3 开头的响应(这个相信大家都背过)。除了 HTTP 重定向,还有其他方式能够进行重定向,本文也会介绍。内容较长,我们先看一下本文的内容架构:HTTP 重定向详解其他类型的重定向方式重定向的使用场景如何优雅地使用 3011. HTTP 重定向在 HTTP 中,服务器可以通过返回一个重定向响应来进行重定向。这个重定向响应有一个以 3 开头的状态码 ,并且有一个 Location 头字段 表示要重定向到的位置。浏览器接收到这个重定向之后,会立即加载 Location 中指定的 URL。通常这一过程耗时极端,用户基本注意不到这个过程。重定向过程如下图所示:重定向过程1.1 重定向状态码及含义前面提到,重定向相关的状态码都是以 3 开头的,主要有以下 9 种状态码:状态码状态短语状态含义300Multiple Choices当请求的 URL 对应有多个资源时(如同一个 HTML 的不同语言的版本),返回这个代码时,可以返回一个可选列表,这样用户可以自行选择。通过 Location 头字段可以自定首选内容。301Moved Permanetly当前请求的资源已被移除时使用,响应的 Location 头字段会提供资源现在的 URL。直接使用 GET 方法发起新情求。302Found与 301 类似,但客户端只应该将 Location 返回的 URL 当做临时资源来使用,将来请求时,还是用老的 URL。直接使用 GET 方法发起新情求。303See Other用于在 PUT 或者 POST 请求之后进行重定向,这样在结果页就不会再次触发重定向了。304Not Modified资源未修改,表示本地缓存仍然可用。305Use Proxy用来表示必须通过一个代理来访问资源,代理的位置有 Location 头字段给出306Switch Proxy在最新版的规范中,306 状态码已经不再被使用。最初是指“后续请求应使用指定的代理”。307Temporary Redirect与 302 类似,但是使用原请求方法发起新情求。308Permanent Redirect与 301 类似,但是使用原请求方法发起新情求。总共有 9 个与重定向相关的状态码,其中 301/302/304 都比较常见,305/306 使用较少,本文不做介绍(其实我也不懂,也没用过 😂)。这 9 种状态码可以分成 3 大类,分别是:永久重定向、临时重定向以及特殊重定向。1.2 永久重定向类301 和 308 都属于永久重定向。永久重定向意味着原始 URL 不再可用,替换成了一个新的内容。所以搜索引擎、聚合内容阅读器以及其他爬虫识别这两个状态码时,会更新旧 URL 的资源。划重点:这个就是永久重定向和临时重定向的区别。规范中,301 本来不允许改变请求方法,但是已有的浏览器厂商都使用了 GET 方法进行新的请求。所以创建了 308 用来处理需要使用非 GET 进行重定向的场景。1.3 临时重定向类302/303/307 都属于临时重定向。有时,当原有资源因为一些不可预测的原因而临时无法访问时,可以通过临时重定向的方式将请求转移到另一个地方。搜索引擎和爬虫不应该记住这个临时的连接。此外,临时重定向还可以用来在创建、修改和删除时展示临时的进度页,这里通常使用 303。302 和 307 的关系类似于 301 和 308,参见上文。1.4 特殊重定向类除此之外,300/304/305/306 可以归属到特殊重定向类。这里重点说一下 304,304 是 HTTP 缓存中的一个重要内容,表示资源未修改,相当于将资源重定向到本地缓存。关于 HTTP 缓存的详细内容,可以查看这篇文章:浏览器缓存策略之扫盲篇2. 其他类型的重定向方式HTTP 是最简易使用的重定向方式,但是有些时候我们并不能够操作服务端。好在,除了 HTTP 重定向外,还有两种方式:通过<meta>进行 HTML 重定向和通过 DOM 的 JS 重定向。2.1 HTML 重定向如下代码所示,我们可以通过在<meta>元素上设置http-equiv="Refresh可以实现页面的重定向。<head><meta http-equiv="Refresh" content="0; URL=https://example.com/"></head>复制代码content属性需要以数字开头,表示多少秒之后重定向到指定页面。通常我们设置为 0。注意,这一方式只适用于 HTML2.2 JavaScript 重定向这个大家都用过,使用window.location可以重定向页面。这个方法很常见,不过多做介绍。当然,这一方式只在 JavaScript 的客户端执行环境有效。上述所介绍的三种重定向方式中,按照优先级顺序如下:HTTP > HTML > JavaScript。这和我们所知道的文件的请求处理顺序一致,不过多解释。3. 重定向的使用场景不同类别的重定向有不同的使用场景,大致可以分为以下几类:网站别名:通常情况下,对于一个资源,我们只有一个 URL,但有些特殊情况下,资源会存在多个 URL,这个时候就需要用到重定向。提高网站的可达率:比如 www.example.com 和 example.com都可以访问到指定网站。迁移到新的站点:因为某些原因旧站点被废弃,但仍然希望之前已经存在的连接和收藏书签能够生效,这是可以使用重定向。强制跳转 HTTPS:当我们的网站支持 HTTPS 时,通常会强制使用 HTTPS,所以访问 HTTP 时需要做重定向跳转。保证已有链接可用:站点的维护是一个长时间的过程,有时,我们在进行重构时,会对一些链接或路由进行调整,这时候我们内部的 URL 可以修改,但是对于已在被外部引用了的链接却无法修改。为了保证这部分的链接可用,我们通常需要设置重定向。对于危险操作进行重定向:类似编辑删除等危险操作,为了避免用户刷新时重复触发危险操作,我们可以将其重定向到临时的进度展示页,比如使用 303。对于耗时较长的请求也可以这么处理。4. 如何优雅地使用 301有些时候,我们对于永久重定向的理解并不够,在仓促之中使用了 301 永久重定向时就会遇到这样的一个坑,那就是不管我们怎么重新设置,(有些)浏览器都仍然使用最开始设置的 301 永久重定向。这时,我们的用户甚至是我们自己的状态大概是这样的:网站:忍法 - 永久重定向之术 用户&我们:我是谁?我在哪?我该怎么回去?往往在错误配置了 301 之后,我们需要面临的问题就是取消最初的 301?然而,很不幸的是,似乎并没有好的办法能够快速的清除用户端已经使用过的错误 301 重定向。如果用户足够聪明的话,还可以让用户按照我们的说明进行处理。所以最好的做法是能够搞懂并优雅地使用 301,这样才能避免这一问题。下面,我们先来复现问题,然后再解释问题。4.1 准备:使用 Nginx 配置 301 永久重定向在 Nginx 中,我们可以创建一个 server 块来指定所有内容都进行重定向:server {listen 80;server_name example.com;return 301 $scheme://www.example.com$request_uri;}复制代码也可以通过rewrite指令指定目录和页面进行重定向:rewrite ^/images/(.*)$ https://images.example.com/$1 redirect;rewrite ^/images/(.*)$ https://images.example.com/$1 permanent;复制代码对于其他 web 服务器,可以参考 MDN 或者网上的其他教程进行配置。比如我准备了下面这样一个 nginx.conf 文件。server {listen 80;server_name redirect.example.com;root /your-path/web-redirect;location /original-page {# 下面是两种重定向方式,为了测试使用,启用一个即可# 永久重定向rewrite ^/original-page http://redirect.example.com/301 permanent;# # 临时重定向# rewrite ^/original-page http://redirect.example.com/302 redirect;}location /301 {try_files $uri /301.html$is_args$args;}location /302 {try_files $uri /302.html$is_args$args;}}复制代码301.html/302.html 自行准备即可,内容自己能区分出来就行。现在我们假设不小心将初始页面永久重定向到了 301 页面,现在想取消这一行为,临时重定向到 302 页面。先开启永久重定向的规则,在浏览器访问http://redirect.example.com/original-page,会发现重定向到了 301。关闭永久重定向规则,开启临时重定向,再次访问初始页面,看看是否重定向到了 302 页面。至此,我们会发现,301 之后,浏览器会记住第一次的 301,忽略之后的其他重定向。那这样到底是为什么呢?4.2 浏览器会缓存“301”永久重定向这所以会这样,这是因为浏览器会缓存“301”永久重定向。经不完全测试,各浏览器的缓存情况如下:是否缓存重启是否清除时间改为 1 年后是否失效5 年后Chrome是未清除未失效未失效FireFox是未清除未失效未失效Safari是清除失效失效IE--(没测)------可以看出除了 Safari 重启/修改时间之后,能够使用新的重定向,Chrome/Firefox 都会无限期的缓存 301 重定向。在 FireFox 中我们也可以简单验证下,输入about:cache,在磁盘缓存中可以找到相关的缓存项。如下:FireFox中的301缓存内容浏览器为什么会缓存 301 重定向呢?其实,HTTP RFC 中规定 301 是一个可缓存的响应,所以浏览器会根据响应中的 HTTP 缓存头进行缓存。如果我们没有提供明确的缓存头,浏览器就会默认永久缓存 301 响应,因为 301 是永久重定向的意思。这里笔者偷懒没有测试 IE,但是鉴于有浏览器(Chrome/Firefox)会无限期缓存 301 重定向,那么我们就需要试着去解决这一问题 —— 如何清除 301 重定向缓存。4.3 如何清除 301 重定向缓存内心戏:不是说没法清除吗?这怎么介绍了。我:别急,先看完。既然是缓存行为,那么我们就可以通过常规的缓存清理方式来处理,包括但不限于以下几种方式:控制台禁用缓存清除历史记录Network 面板清除缓存这里大家可以自行尝试以下,如果不行的话,记得多试 1-2 遍就行(至于为什么要多试,我也很奇怪,有的时候就是清两遍就好了)。如果大家验证了上面的几种清除方式,就会发现确实是行之有效的。那为什么我会说没有很好地方式去清除呢?大家细想,当我们将错误的 301 请求发布到线上环境了,并且影响了数以万计的用户时,我们要怎么通知并教会用户按照我们的方式去清除缓存呢?当然,清除历史记录算是最便捷的方式了,如果真的不行遇到了这种情况,那就通知用户这么清除吧 😂。4.4 优雅地使用 301为了避免上面需要清除的情况,最好的做法是优雅地使用 301。前面解释浏览器为什么会缓存 301 重定向时,已经隐晦地提到了这一方法。既然浏览器认为这是一个可以缓存的资源,并且我们可以通过缓存头来控制。那么在使用 301 时,我们将其设置为不缓存就可以了。比如设置 Cache-Control: no-store 或 Cache-Control: no-cache。location /original-page {+ add_header Cache-Control no-store;# 永久重定向rewrite ^/original-page http://redirect.example.com/301 permanent;# 临时重定向# rewrite ^/original-page http://redirect.example.com/302 redirect;}复制代码这样设置之后,如果我们再将重定向切换成 302,会发现浏览器不会缓存 301 了,新的重定向可以立即生效了。总结以上就是重定向相关的内容。301 使用需谨慎,一定要设缓存头 😂。The End如果你觉得这篇内容对你挺有启发,我想请你帮我三个小忙:1、点个 「在看」,让更多的人也能看到这篇内容2、关注官网 https://muyiy.cn,让我们成为长期关系3、关注公众号「高级前端进阶」,公众号后台回复 「加群」 ,加入我们一起学习并送你精心整理的高级前端面试题。
高级前端进阶接下来让我带你走进高级前端的世界,在进阶的路上,共勉!48篇原创内容公众号》》面试官都在用的题库,快来看看《《“在看”吗?在看就点一下吧
阅读原文喜欢此内容的人还喜欢VsCode 何时能一统江湖?VsCode 何时能一统江湖?...高级前端进阶不喜欢不看的原因确定内容质量低不看此公众号用中间件思想解决实际前端问题用中间件思想解决实际前端问题...前端试炼不喜欢不看的原因确定内容质量低不看此公众号呵,你会51单片机的精确延时吗?呵,你会51单片机的精确延时吗?...嵌入式ARM不喜欢不看的原因确定内容质量低不看此公众号