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

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

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