最近一段時間,連續遇到了兩次跟重定向相關的問題,本着知己知彼百戰百勝的態度,我決定深刻了解一下,順便跟你們分享一下。html
做爲前端開發,你們對重定向必定不陌生,不就是永久重定向和臨時重定向嘛,誰還不知道呢 😂。前端
那麼你們是否知道永久重定向和臨時重定向的區別呢?若是不當心設置了永久重定向該如何取消呢?如何優雅地使用重定向呢?接下來就讓咱們來一探究竟吧。nginx
URL 重定向,可以將多個 URL 指向同一個頁面,這一技術有着多種用途。在 HTTP 中有一個專門的響應,叫作 HTTP 重定向,也就是全部 3 開頭的響應(這個相信你們都背過)。git
除了 HTTP 重定向,還有其餘方式可以進行重定向,本文也會介紹。github
內容較長,咱們先看一下本文的內容架構:web
在 HTTP 中,服務器能夠經過返回一個重定向響應來進行重定向。這個重定向響應有一個以 3 開頭的狀態碼 ,而且有一個 Location
頭字段 表示要重定向到的位置。瀏覽器
瀏覽器接收到這個重定向以後,會當即加載 Location
中指定的 URL。一般這一過程耗時極端,用戶基本注意不到這個過程。緩存
重定向過程以下圖所示:服務器
前面提到,重定向相關的狀態碼都是以 3 開頭的,主要有如下 9 種狀態碼:markdown
狀態碼 | 狀態短語 | 狀態含義 |
---|---|---|
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 大類,分別是:永久重定向、臨時重定向以及特殊重定向。
301 和 308 都屬於永久重定向。永久重定向意味着原始 URL 再也不可用,替換成了一個新的內容。因此搜索引擎、聚合內容閱讀器以及其餘爬蟲識別這兩個狀態碼時,會更新舊 URL 的資源。
劃重點:這個就是永久重定向和臨時重定向的區別。
規範中,301 原本不容許改變請求方法,可是已有的瀏覽器廠商都使用了 GET 方法進行新的請求。因此建立了 308 用來處理須要使用非 GET 進行重定向的場景。
302/303/307 都屬於臨時重定向。有時,當原有資源由於一些不可預測的緣由而臨時沒法訪問時,能夠經過臨時重定向的方式將請求轉移到另外一個地方。搜索引擎和爬蟲不該該記住這個臨時的鏈接。
此外,臨時重定向還能夠用來在建立、修改和刪除時展現臨時的進度頁,這裏一般使用 303。
302 和 307 的關係相似於 301 和 308,參見上文。
除此以外,300/304/305/306 能夠歸屬到特殊重定向類。這裏重點說一下 304,304 是 HTTP 緩存中的一個重要內容,表示資源未修改,至關於將資源重定向到本地緩存。
關於 HTTP 緩存的詳細內容,能夠查看這篇文章:瀏覽器緩存策略之掃盲篇
HTTP 是最簡易使用的重定向方式,可是有些時候咱們並不可以操做服務端。好在,除了 HTTP 重定向外,還有兩種方式:經過<meta>
進行 HTML 重定向和經過 DOM 的 JS 重定向。
以下代碼所示,咱們能夠經過在<meta>
元素上設置http-equiv="Refresh
能夠實現頁面的重定向。
<head>
<meta http-equiv="Refresh" content="0; URL=https://example.com/">
</head>
複製代碼
content
屬性須要以數字開頭,表示多少秒以後重定向到指定頁面。一般咱們設置爲 0。
注意,這一方式只適用於 HTML
這個你們都用過,使用window.location
能夠重定向頁面。這個方法很常見,不過多作介紹。
固然,這一方式只在 JavaScript 的客戶端執行環境有效。
上述所介紹的三種重定向方式中,按照優先級順序以下:HTTP > HTML > JavaScript。這和咱們所知道的文件的請求處理順序一致,不過多解釋。
不一樣類別的重定向有不一樣的使用場景,大體能夠分爲如下幾類:
www.example.com
和 example.com
均可以訪問到指定網站。有些時候,咱們對於永久重定向的理解並不夠,在倉促之中使用了 301 永久重定向時就會遇到這樣的一個坑,那就是無論咱們怎麼從新設置,(有些)瀏覽器都仍然使用最開始設置的 301 永久重定向。
這時,咱們的用戶甚至是咱們本身的狀態大概是這樣的:
網站:忍法 - 永久重定向之術 用戶&咱們:我是誰?我在哪?我該怎麼回去?
每每在錯誤配置了 301 以後,咱們須要面臨的問題就是取消最初的 301?
然而,很不幸的是,彷佛並無好的辦法可以快速的清除用戶端已經使用過的錯誤 301 重定向。
若是用戶足夠聰明的話,還可讓用戶按照咱們的說明進行處理。
因此最好的作法是可以搞懂並優雅地使用 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。至此,咱們會發現,301 以後,瀏覽器會記住第一次的 301,忽略以後的其餘重定向。那這樣究竟是爲何呢?
這因此會這樣,這是由於瀏覽器會緩存「301」永久重定向。
經不徹底測試,各瀏覽器的緩存狀況以下:
是否緩存 | 重啓是否清除 | 時間改成 1 年後是否失效 | 5 年後 | |
---|---|---|---|---|
Chrome | 是 | 未清除 | 未失效 | 未失效 |
FireFox | 是 | 未清除 | 未失效 | 未失效 |
Safari | 是 | 清除 | 失效 | 失效 |
IE | --(沒測) | -- | -- | -- |
能夠看出除了 Safari 重啓/修改時間以後,可以使用新的重定向,Chrome/Firefox 都會無限期的緩存 301 重定向。
在 FireFox 中咱們也能夠簡單驗證下,輸入about:cache
,在磁盤緩存中能夠找到相關的緩存項。以下:
瀏覽器爲何會緩存 301 重定向呢?其實,HTTP RFC 中規定 301 是一個可緩存的響應,因此瀏覽器會根據響應中的 HTTP 緩存頭進行緩存。若是咱們沒有提供明確的緩存頭,瀏覽器就會默認永久緩存 301 響應,由於 301 是永久重定向的意思。
這裏筆者偷懶沒有測試 IE,可是鑑於有瀏覽器(Chrome/Firefox)會無限期緩存 301 重定向,那麼咱們就須要試着去解決這一問題 —— 如何清除 301 重定向緩存。
心裏戲:不是說無法清除嗎?這怎麼介紹了。我:別急,先看完。
既然是緩存行爲,那麼咱們就能夠經過常規的緩存清理方式來處理,包括但不限於如下幾種方式:
這裏你們能夠自行嘗試如下,若是不行的話,記得多試 1-2 遍就行(至於爲何要多試,我也很奇怪,有的時候就是清兩遍就行了)。
若是你們驗證了上面的幾種清除方式,就會發現確實是行之有效的。那爲何我會說沒有很好地方式去清除呢?
你們細想,當咱們將錯誤的 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 使用需謹慎,必定要設緩存頭 😂。
若是你喜歡,歡迎掃碼關注個人公衆號,我會按期陪讀,並分享一些其餘的前端知識喲。