搞懂 HTTP 重定向 - 如何优雅地使用 301

403次阅读  |  发布于3年以前

最近一段时间,连续遇到了两次跟重定向相关的问题,本着知己知彼百战百胜的态度,我决定深入了解一下,顺便跟大家分享一下。

作为前端开发,大家对重定向一定不陌生,不就是永久重定向和临时重定向嘛,谁还不知道呢 。

那么大家是否知道永久重定向和临时重定向的区别呢?如果不小心设置了永久重定向该如何取消呢?如何优雅地使用重定向呢?接下来就让我们来一探究竟吧。

URL 重定向,能够将多个 URL 指向同一个页面,这一技术有着多种用途。在 HTTP 中有一个专门的响应,叫做 HTTP 重定向,也就是所有 3 开头的响应(这个相信大家都背过)。

除了 HTTP 重定向,还有其他方式能够进行重定向,本文也会介绍。

内容较长,我们先看一下本文的内容架构:

1 . HTTP 重定向详解 2 . 其他类型的重定向方式 3 . 重定向的使用场景 4 . 如何优雅地使用 301

  1. HTTP 重定向

在 HTTP 中,服务器可以通过返回一个重定向响应来进行重定向。这个重定向响应有一个以 3 开头的状态码 ,并且有一个 Location 头字段 表示要重定向到的位置。

浏览器接收到这个重定向之后,会立即加载 Location 中指定的 URL。通常这一过程耗时极端,用户基本注意不到这个过程。

重定向过程如下图所示:

重定向过程

1.1 重定向状态码及含义

前面提到,重定向相关的状态码都是以 3 开头的,主要有以下 9 种状态码:

状态码 状态短语 状态含义
300 Multiple Choices 当请求的 URL 对应有多个资源时(如同一个 HTML 的不同语言的版本),返回这个代码时,可以返回一个可选列表,这样用户可以自行选择。通过 Location 头字段可以自定首选内容。
301 Moved Permanetly 当前请求的资源已被移除时使用,响应的 Location 头字段会提供资源现在的 URL。直接使用 GET 方法发起新情求。
302 Found 与 301 类似,但客户端只应该将 Location 返回的 URL 当做临时资源来使用,将来请求时,还是用老的 URL。直接使用 GET 方法发起新情求。
303 See Other 用于在 PUT 或者 POST 请求之后进行重定向,这样在结果页就不会再次触发重定向了。
304 Not Modified 资源未修改,表示本地缓存仍然可用。
305 Use Proxy 用来表示必须通过一个代理来访问资源,代理的位置有 Location 头字段给出
306 Switch Proxy 在最新版的规范中,306 状态码已经不再被使用。最初是指“后续请求应使用指定的代理”。
307 Temporary Redirect 与 302 类似,但是使用原请求方法发起新情求。
308 Permanent 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 缓存的详细内容,可以查看这篇文章:浏览器缓存策略之扫盲篇

  1. 其他类型的重定向方式

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。

注意,这一方式只适用于 HTML

2.2 JavaScript 重定向

这个大家都用过,使用window.location可以重定向页面。这个方法很常见,不过多做介绍。

当然,这一方式只在 JavaScript 的客户端执行环境有效。

上述所介绍的三种重定向方式中,按照优先级顺序如下:HTTP > HTML > JavaScript。这和我们所知道的文件的请求处理顺序一致,不过多解释。

  1. 重定向的使用场景

不同类别的重定向有不同的使用场景,大致可以分为以下几类:

  1. 如何优雅地使用 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 页面。

  1. 先开启永久重定向的规则,在浏览器访问http://redirect.example.com/original-page,会发现重定向到了 301。
  2. 关闭永久重定向规则,开启临时重定向,再次访问初始页面,看看是否重定向到了 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 重定向缓存

内心戏:不是说没法清除吗?这怎么介绍了。我:别急,先看完。

既然是缓存行为,那么我们就可以通过常规的缓存清理方式来处理,包括但不限于以下几种方式:

这里大家可以自行尝试以下,如果不行的话,记得多试 1-2 遍就行(至于为什么要多试,我也很奇怪,有的时候就是清两遍就好了)。

如果大家验证了上面的几种清除方式,就会发现确实是行之有效的。那为什么我会说没有很好地方式去清除呢?

大家细想,当我们将错误的 301 请求发布到线上环境了,并且影响了数以万计的用户时,我们要怎么通知并教会用户按照我们的方式去清除缓存呢?当然,清除历史记录算是最便捷的方式了,如果真的不行遇到了这种情况,那就通知用户这么清除吧 。

4.4 优雅地使用 301

为了避免上面需要清除的情况,最好的做法是优雅地使用 301。

前面解释浏览器为什么会缓存 301 重定向时,已经隐晦地提到了这一方法。

既然浏览器认为这是一个可以缓存的资源,并且我们可以通过缓存头来控制。那么在使用 301 时,我们将其设置为不缓存就可以了。比如设置 Cache-Control: no-storeCache-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 使用需谨慎,一定要设缓存头 。

参考资料

[1]维基百科 - HTTP 状态码: https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81

[2]MDN - Redirections in HTTP: https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections

[3]浏览器将 HTTP 301 缓存多长时间?: https://qastack.cn/programming/9130422/how-long-do-browsers-cache-http-301s

[4]浏览器对 301 重定向的缓存: https://juejin.cn/post/6844904045614743560

[5]Chromium - Feature Request: I need a way to clear the 301 redirect cache: https://bugs.chromium.org/p/chromium/issues/detail?id=633023&can=1&q=clear%20301%20redirects&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8