搞懂 HTTP 重定向 - 如何優(yōu)雅地使用 301
最近一段時(shí)間,連續(xù)遇到了兩次跟重定向相關(guān)的問題,本著知己知彼百戰(zhàn)百勝的態(tài)度,我決定深入了解一下,順便跟大家分享一下。
加入我們一起學(xué)習(xí),天天進(jìn)步
作為前端開發(fā),大家對重定向一定不陌生,不就是永久重定向和臨時(shí)重定向嘛,誰還不知道呢 ??。
那么大家是否知道永久重定向和臨時(shí)重定向的區(qū)別呢?如果不小心設(shè)置了永久重定向該如何取消呢?如何優(yōu)雅地使用重定向呢?接下來就讓我們來一探究竟吧。
URL 重定向,能夠?qū)⒍鄠€(gè) URL 指向同一個(gè)頁面,這一技術(shù)有著多種用途。在 HTTP 中有一個(gè)專門的響應(yīng),叫做 HTTP 重定向,也就是所有 3 開頭的響應(yīng)(這個(gè)相信大家都背過)。
除了 HTTP 重定向,還有其他方式能夠進(jìn)行重定向,本文也會介紹。
內(nèi)容較長,我們先看一下本文的內(nèi)容架構(gòu):
HTTP 重定向詳解 其他類型的重定向方式 重定向的使用場景 如何優(yōu)雅地使用 301
1. HTTP 重定向
在 HTTP 中,服務(wù)器可以通過返回一個(gè)重定向響應(yīng)來進(jìn)行重定向。這個(gè)重定向響應(yīng)有一個(gè)以 3 開頭的狀態(tài)碼 ,并且有一個(gè) Location 頭字段 表示要重定向到的位置。
瀏覽器接收到這個(gè)重定向之后,會立即加載 Location 中指定的 URL。通常這一過程耗時(shí)極端,用戶基本注意不到這個(gè)過程。
重定向過程如下圖所示:

1.1 重定向狀態(tài)碼及含義
前面提到,重定向相關(guān)的狀態(tài)碼都是以 3 開頭的,主要有以下 9 種狀態(tài)碼:
| 狀態(tài)碼 | 狀態(tài)短語 | 狀態(tài)含義 |
|---|---|---|
| 300 | Multiple Choices | 當(dāng)請求的 URL 對應(yīng)有多個(gè)資源時(shí)(如同一個(gè) HTML 的不同語言的版本),返回這個(gè)代碼時(shí),可以返回一個(gè)可選列表,這樣用戶可以自行選擇。通過 Location 頭字段可以自定首選內(nèi)容。 |
| 301 | Moved Permanetly | 當(dāng)前請求的資源已被移除時(shí)使用,響應(yīng)的 Location 頭字段會提供資源現(xiàn)在的 URL。直接使用 GET 方法發(fā)起新情求。 |
| 302 | Found | 與 301 類似,但客戶端只應(yīng)該將 Location 返回的 URL 當(dāng)做臨時(shí)資源來使用,將來請求時(shí),還是用老的 URL。直接使用 GET 方法發(fā)起新情求。 |
| 303 | See Other | 用于在 PUT 或者 POST 請求之后進(jìn)行重定向,這樣在結(jié)果頁就不會再次觸發(fā)重定向了。 |
| 304 | Not Modified | 資源未修改,表示本地緩存仍然可用。 |
| 305 | Use Proxy | 用來表示必須通過一個(gè)代理來訪問資源,代理的位置有 Location 頭字段給出 |
| 306 | Switch Proxy | 在最新版的規(guī)范中,306 狀態(tài)碼已經(jīng)不再被使用。最初是指“后續(xù)請求應(yīng)使用指定的代理”。 |
| 307 | Temporary Redirect | 與 302 類似,但是使用原請求方法發(fā)起新情求。 |
| 308 | Permanent Redirect | 與 301 類似,但是使用原請求方法發(fā)起新情求。 |
總共有 9 個(gè)與重定向相關(guān)的狀態(tài)碼,其中 301/302/304 都比較常見,305/306 使用較少,本文不做介紹(其實(shí)我也不懂,也沒用過 ??)。這 9 種狀態(tài)碼可以分成 3 大類,分別是:永久重定向、臨時(shí)重定向以及特殊重定向。
1.2 永久重定向類
301 和 308 都屬于永久重定向。永久重定向意味著原始 URL 不再可用,替換成了一個(gè)新的內(nèi)容。所以搜索引擎、聚合內(nèi)容閱讀器以及其他爬蟲識別這兩個(gè)狀態(tài)碼時(shí),會更新舊 URL 的資源。
劃重點(diǎn):這個(gè)就是永久重定向和臨時(shí)重定向的區(qū)別。
規(guī)范中,301 本來不允許改變請求方法,但是已有的瀏覽器廠商都使用了 GET 方法進(jìn)行新的請求。所以創(chuàng)建了 308 用來處理需要使用非 GET 進(jìn)行重定向的場景。
1.3 臨時(shí)重定向類
302/303/307 都屬于臨時(shí)重定向。有時(shí),當(dāng)原有資源因?yàn)橐恍┎豢深A(yù)測的原因而臨時(shí)無法訪問時(shí),可以通過臨時(shí)重定向的方式將請求轉(zhuǎn)移到另一個(gè)地方。搜索引擎和爬蟲不應(yīng)該記住這個(gè)臨時(shí)的連接。
此外,臨時(shí)重定向還可以用來在創(chuàng)建、修改和刪除時(shí)展示臨時(shí)的進(jìn)度頁,這里通常使用 303。
302 和 307 的關(guān)系類似于 301 和 308,參見上文。
1.4 特殊重定向類
除此之外,300/304/305/306 可以歸屬到特殊重定向類。這里重點(diǎn)說一下 304,304 是 HTTP 緩存中的一個(gè)重要內(nèi)容,表示資源未修改,相當(dāng)于將資源重定向到本地緩存。
關(guān)于 HTTP 緩存的詳細(xì)內(nèi)容,可以查看這篇文章:瀏覽器緩存策略之掃盲篇
2. 其他類型的重定向方式
HTTP 是最簡易使用的重定向方式,但是有些時(shí)候我們并不能夠操作服務(wù)端。好在,除了 HTTP 重定向外,還有兩種方式:通過進(jìn)行 HTML 重定向和通過 DOM 的 JS 重定向。
2.1 HTML 重定向
如下代碼所示,我們可以通過在元素上設(shè)置http-equiv="Refresh可以實(shí)現(xiàn)頁面的重定向。
??"Refresh"?content="0;?URL=https://example.com/">
復(fù)制代碼content屬性需要以數(shù)字開頭,表示多少秒之后重定向到指定頁面。通常我們設(shè)置為 0。
注意,這一方式只適用于 HTML
2.2 JavaScript 重定向
這個(gè)大家都用過,使用window.location可以重定向頁面。這個(gè)方法很常見,不過多做介紹。
當(dāng)然,這一方式只在 JavaScript 的客戶端執(zhí)行環(huán)境有效。
上述所介紹的三種重定向方式中,按照優(yōu)先級順序如下:HTTP > HTML > JavaScript。這和我們所知道的文件的請求處理順序一致,不過多解釋。
3. 重定向的使用場景
不同類別的重定向有不同的使用場景,大致可以分為以下幾類:
網(wǎng)站別名:通常情況下,對于一個(gè)資源,我們只有一個(gè) URL,但有些特殊情況下,資源會存在多個(gè) URL,這個(gè)時(shí)候就需要用到重定向。 提高網(wǎng)站的可達(dá)率:比如 www.example.com和example.com都可以訪問到指定網(wǎng)站。遷移到新的站點(diǎn):因?yàn)槟承┰蚺f站點(diǎn)被廢棄,但仍然希望之前已經(jīng)存在的連接和收藏書簽?zāi)軌蛏?,這是可以使用重定向。 強(qiáng)制跳轉(zhuǎn) HTTPS:當(dāng)我們的網(wǎng)站支持 HTTPS 時(shí),通常會強(qiáng)制使用 HTTPS,所以訪問 HTTP 時(shí)需要做重定向跳轉(zhuǎn)。 保證已有鏈接可用:站點(diǎn)的維護(hù)是一個(gè)長時(shí)間的過程,有時(shí),我們在進(jìn)行重構(gòu)時(shí),會對一些鏈接或路由進(jìn)行調(diào)整,這時(shí)候我們內(nèi)部的 URL 可以修改,但是對于已在被外部引用了的鏈接卻無法修改。為了保證這部分的鏈接可用,我們通常需要設(shè)置重定向。 對于危險(xiǎn)操作進(jìn)行重定向:類似編輯刪除等危險(xiǎn)操作,為了避免用戶刷新時(shí)重復(fù)觸發(fā)危險(xiǎn)操作,我們可以將其重定向到臨時(shí)的進(jìn)度展示頁,比如使用 303。對于耗時(shí)較長的請求也可以這么處理。
4. 如何優(yōu)雅地使用 301
有些時(shí)候,我們對于永久重定向的理解并不夠,在倉促之中使用了 301 永久重定向時(shí)就會遇到這樣的一個(gè)坑,那就是不管我們怎么重新設(shè)置,(有些)瀏覽器都仍然使用最開始設(shè)置的 301 永久重定向。
這時(shí),我們的用戶甚至是我們自己的狀態(tài)大概是這樣的:
網(wǎng)站:忍法 - 永久重定向之術(shù) 用戶&我們:我是誰?我在哪?我該怎么回去?
往往在錯(cuò)誤配置了 301 之后,我們需要面臨的問題就是取消最初的 301?
然而,很不幸的是,似乎并沒有好的辦法能夠快速的清除用戶端已經(jīng)使用過的錯(cuò)誤 301 重定向。
如果用戶足夠聰明的話,還可以讓用戶按照我們的說明進(jìn)行處理。
所以最好的做法是能夠搞懂并優(yōu)雅地使用 301,這樣才能避免這一問題。
下面,我們先來復(fù)現(xiàn)問題,然后再解釋問題。
4.1 準(zhǔn)備:使用 Nginx 配置 301 永久重定向
在 Nginx 中,我們可以創(chuàng)建一個(gè) server 塊來指定所有內(nèi)容都進(jìn)行重定向:
server?{
?listen?80;
?server_name?example.com;
?return?301?$scheme://www.example.com$request_uri;
}
復(fù)制代碼也可以通過rewrite指令指定目錄和頁面進(jìn)行重定向:
rewrite?^/images/(.*)$?https://images.example.com/$1?redirect;
rewrite?^/images/(.*)$?https://images.example.com/$1?permanent;
復(fù)制代碼對于其他 web 服務(wù)器,可以參考 MDN 或者網(wǎng)上的其他教程進(jìn)行配置。
比如我準(zhǔn)備了下面這樣一個(gè) nginx.conf 文件。
server?{
??listen?80;
??server_name?redirect.example.com;
??root?/your-path/web-redirect;
??location?/original-page?{
????#?下面是兩種重定向方式,為了測試使用,啟用一個(gè)即可
????#?永久重定向
????rewrite?^/original-page?http://redirect.example.com/301?permanent;
????#?#?臨時(shí)重定向
????#?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;
??}
}
復(fù)制代碼301.html/302.html 自行準(zhǔn)備即可,內(nèi)容自己能區(qū)分出來就行。
現(xiàn)在我們假設(shè)不小心將初始頁面永久重定向到了 301 頁面,現(xiàn)在想取消這一行為,臨時(shí)重定向到 302 頁面。
先開啟永久重定向的規(guī)則,在瀏覽器訪問 http://redirect.example.com/original-page,會發(fā)現(xiàn)重定向到了 301。關(guān)閉永久重定向規(guī)則,開啟臨時(shí)重定向,再次訪問初始頁面,看看是否重定向到了 302 頁面。
至此,我們會發(fā)現(xiàn),301 之后,瀏覽器會記住第一次的 301,忽略之后的其他重定向。那這樣到底是為什么呢?
4.2 瀏覽器會緩存“301”永久重定向
這所以會這樣,這是因?yàn)闉g覽器會緩存“301”永久重定向。
經(jīng)不完全測試,各瀏覽器的緩存情況如下:
| 是否緩存 | 重啟是否清除 | 時(shí)間改為 1 年后是否失效 | 5 年后 | |
|---|---|---|---|---|
| Chrome | 是 | 未清除 | 未失效 | 未失效 |
| FireFox | 是 | 未清除 | 未失效 | 未失效 |
| Safari | 是 | 清除 | 失效 | 失效 |
| IE | --(沒測) | -- | -- | -- |
可以看出除了 Safari 重啟/修改時(shí)間之后,能夠使用新的重定向,Chrome/Firefox 都會無限期的緩存 301 重定向。
在 FireFox 中我們也可以簡單驗(yàn)證下,輸入about:cache,在磁盤緩存中可以找到相關(guān)的緩存項(xiàng)。如下:

瀏覽器為什么會緩存 301 重定向呢?其實(shí),HTTP RFC 中規(guī)定 301 是一個(gè)可緩存的響應(yīng),所以瀏覽器會根據(jù)響應(yīng)中的 HTTP 緩存頭進(jìn)行緩存。如果我們沒有提供明確的緩存頭,瀏覽器就會默認(rèn)永久緩存 301 響應(yīng),因?yàn)?301 是永久重定向的意思。
這里筆者偷懶沒有測試 IE,但是鑒于有瀏覽器(Chrome/Firefox)會無限期緩存 301 重定向,那么我們就需要試著去解決這一問題 —— 如何清除 301 重定向緩存。
4.3 如何清除 301 重定向緩存
內(nèi)心戲:不是說沒法清除嗎?這怎么介紹了。我:別急,先看完。
既然是緩存行為,那么我們就可以通過常規(guī)的緩存清理方式來處理,包括但不限于以下幾種方式:
控制臺禁用緩存 清除歷史記錄 Network 面板清除緩存
這里大家可以自行嘗試以下,如果不行的話,記得多試 1-2 遍就行(至于為什么要多試,我也很奇怪,有的時(shí)候就是清兩遍就好了)。
如果大家驗(yàn)證了上面的幾種清除方式,就會發(fā)現(xiàn)確實(shí)是行之有效的。那為什么我會說沒有很好地方式去清除呢?
大家細(xì)想,當(dāng)我們將錯(cuò)誤的 301 請求發(fā)布到線上環(huán)境了,并且影響了數(shù)以萬計(jì)的用戶時(shí),我們要怎么通知并教會用戶按照我們的方式去清除緩存呢?當(dāng)然,清除歷史記錄算是最便捷的方式了,如果真的不行遇到了這種情況,那就通知用戶這么清除吧 ??。
4.4 優(yōu)雅地使用 301
為了避免上面需要清除的情況,最好的做法是優(yōu)雅地使用 301。
前面解釋瀏覽器為什么會緩存 301 重定向時(shí),已經(jīng)隱晦地提到了這一方法。
既然瀏覽器認(rèn)為這是一個(gè)可以緩存的資源,并且我們可以通過緩存頭來控制。那么在使用 301 時(shí),我們將其設(shè)置為不緩存就可以了。比如設(shè)置 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;
??#?臨時(shí)重定向
??#?rewrite?^/original-page?http://redirect.example.com/302?redirect;
}
復(fù)制代碼這樣設(shè)置之后,如果我們再將重定向切換成 302,會發(fā)現(xiàn)瀏覽器不會緩存 301 了,新的重定向可以立即生效了。
總結(jié)
以上就是重定向相關(guān)的內(nèi)容。301 使用需謹(jǐn)慎,一定要設(shè)緩存頭 ??。
參考資料
維基百科 - HTTP 狀態(tài)碼:?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 緩存多長時(shí)間?:?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
