指南)
1. 重定向需求源于哪里做前端的人應(yīng)該都跟Netlify打過交道它確實是目前最省心的靜態(tài)站托管平臺之一。但很多人在部署之后才意識到一個問題站點跑起來了URL卻遠(yuǎn)沒有你想的那么聽話。我在幫團(tuán)隊遷移博客、搭建落地頁、給SPA應(yīng)用配路由時遇到過太多類似的場景舊域名上線前需要把所有流量轉(zhuǎn)到新域名站點改版后一堆舊鏈接要301到新路徑前端路由用history模式刷新就404還有多個營銷頁面需要按設(shè)備或地區(qū)做分發(fā)。這些需求全都指向同一個功能——重定向。Netlify對這一塊的支持相當(dāng)完善主要體現(xiàn)在兩種配置方式上根目錄下的_redirects文件和項目根目錄的netlify.toml配置文件。兩種方式各有側(cè)重用好了基本上能覆蓋日常開發(fā)中絕大多數(shù)的URL管理需求。這篇文章我會把這兩種方式從語法、優(yōu)先級到實戰(zhàn)逐層拆開順帶把容易踩的坑和調(diào)試手段一并講清楚。你完全可以把它當(dāng)作一份可直接查的參考手冊來用。不管你是剛接觸Netlify的獨立開發(fā)者還是正在給團(tuán)隊梳理部署規(guī)范的工程化負(fù)責(zé)人只要你需要在Netlify上管理URL行為這篇文章都值得讀完。2. 兩種配置方式弄清楚才能不踩坑2.1_redirects文件簡單直接隨手可寫Netlify在構(gòu)建的時候會自動識別發(fā)布目錄下的_redirects文件你只需要把規(guī)則一行一行地寫進(jìn)去每條規(guī)則表示一個重定向關(guān)系?;菊Z法如下從路徑 目標(biāo)路徑 狀態(tài)碼三個部分用空格分隔大概是這個意思/old-blog-post /new-blog-post 301這條規(guī)則的含義是當(dāng)用戶訪問/old-blog-post時Netlify會返回301狀態(tài)碼永久重定向并把用戶帶到/new-blog-post。如果你用302那就是臨時重定向搜索引擎不會把權(quán)重遷移過去適合臨時促銷頁之類的場景。_redirects文件支持通配符這是它最方便的地方。一條規(guī)則可以覆蓋一組路徑/blog/* /news/:splat 301這里的:splat表示把*匹配到的內(nèi)容原樣傳遞到目標(biāo)地址。比如/blog/hello-world會被轉(zhuǎn)發(fā)到/news/hello-world。如果你要把整組路徑遷移到另一個域名這個通配符可以直接減少幾十條規(guī)則的書寫量。還有一點值得注意_redirects文件可以放在發(fā)布目錄的根目錄下也可以放在項目根目錄中。放在項目根目錄時Netlify會在構(gòu)建階段自動將其復(fù)制到發(fā)布目錄。我更建議你把它放在項目根目錄因為這樣版本控制會一并管理它團(tuán)隊成員改起來也都有跡可循。2.2netlify.toml配置全局管理項目即配置如果你的團(tuán)隊已經(jīng)習(xí)慣用netlify.toml統(tǒng)一管理構(gòu)建命令、環(huán)境變量和部署分支那么重定向規(guī)則也可以直接寫進(jìn)去收斂在一處配置里。netlify.toml中的重定向配置長這樣[[redirects]] from /old-path to /new-path status 301它和_redirects文件的核心功能是一致的但多了一些額外參數(shù)[[redirects]] from /old-path/* to /new-path/:splat status 200 force true headers { X-From-Netlify true }這里的status 200不是重定向而是重寫。也就是說URL保持為/old-path/*不變但返回的是/new-path/:splat的內(nèi)容用戶地址欄不發(fā)生變化。這在做多語言頁面或者保留舊鏈接訪問時很好用。force true也很好理解它可以覆蓋Netlify的默認(rèn)行為。比如你可能想對一個已經(jīng)存在的文件路徑強(qiáng)制做重定向而不希望它直接返回文件內(nèi)容加上force true就能實現(xiàn)。2.3 兩種方式的優(yōu)先級關(guān)系如果你同時使用了_redirects和netlify.toml并且規(guī)則有沖突Netlify會怎么處理這是很多人困惑的地方。Netlify官方文檔的說明是_redirects文件的優(yōu)先級高于netlify.toml中配置的重定向規(guī)則。也就是說當(dāng)兩條規(guī)則匹配同一個請求時_redirects里的規(guī)則會先生效。因此我個人的實踐建議是把最關(guān)鍵的、需要確定的規(guī)則放在_redirects里比如按域名的強(qiáng)制HTTPS跳轉(zhuǎn)、舊域名301到新域名這類全局規(guī)則而把那些跟環(huán)境、分支相關(guān)的動態(tài)規(guī)則放在netlify.toml里。這樣兩者各司其職也不容易混亂。3. 配置優(yōu)先級與規(guī)則匹配的底層邏輯3.1 靜態(tài)文件優(yōu)先重定向次之剛開始用Netlify重定向時我最容易犯的錯就是明明在_redirects里寫好了路徑跳轉(zhuǎn)但訪問時總是打到原來的文件上。后來看文檔才發(fā)現(xiàn)Netlify處理請求的順序是這樣的檢查是否有靜態(tài)文件直接匹配該路徑。如果有直接返回文件內(nèi)容。如果沒有靜態(tài)文件再檢查是否存在匹配的重定向規(guī)則。如果都沒有才走404邏輯。這意味著如果你項目里真實存在/about.html文件即便你在_redirects里寫了/about /new-about 301用戶訪問/about時也不會跳轉(zhuǎn)因為/about本身就是一個文件路徑。如果你確實想讓重定向規(guī)則優(yōu)先可以在規(guī)則后面加force標(biāo)記。在_redirects文件中是這樣寫的/about /new-about 301!注意那個結(jié)尾的感嘆號它表示“強(qiáng)制執(zhí)行重定向而不是處理靜態(tài)文件”。這在很多時候非常有用但也要謹(jǐn)慎使用。因為一旦加了感嘆號如果你的目標(biāo)地址寫錯了用戶就會直接面對404而不是你預(yù)期的處理邏輯。對于基礎(chǔ)路徑Netlify也有一個隱藏規(guī)則它會把所有不帶斜杠的目錄路徑自動補(bǔ)上一個斜杠再做匹配。比如請求/about時實際匹配的是/about/。這就引出一個常見現(xiàn)象你寫了規(guī)則/about /new-about 301但訪問/about時卻感覺好像沒生效。其實是因為Netlify內(nèi)部先把請求標(biāo)準(zhǔn)化成了/about/而你的規(guī)則寫的是無斜杠形式所以匹配不上。解決方法是把你的規(guī)則改成/about/* /new-about/:splat 301或者直接在規(guī)則里帶上斜杠去匹配。3.2 規(guī)則匹配順序誰先寫誰先贏Netlify的規(guī)則匹配是按順序執(zhí)行的從_redirects文件的第一行開始依次往下匹配。一旦某條規(guī)則匹配成功就會立即執(zhí)行重定向或重寫后面的規(guī)則不再參與。這個特性直接影響了你寫規(guī)則的順序。比如你有兩條規(guī)則/* /zh/home 200 /zh/* /zh/:splat 200如果你把/*寫在前面那么所有請求都會被重寫到/zh/home第二條規(guī)則完全沒有發(fā)揮空間。反之如果你想讓/zh/*路徑優(yōu)先走細(xì)分邏輯必須把它放在前面。有人可能會想那我把所有規(guī)則都寫得寬泛一點通過test來驗證順序不就行了但在線上環(huán)境中規(guī)則的順序一旦出錯影響的是真實用戶的訪問。我的習(xí)慣是先寫具體規(guī)則再寫兜底規(guī)則最后才寫全局通配規(guī)則。這樣能極大降低順序錯誤帶來的風(fēng)險。3.3 一個容易忽略的細(xì)節(jié)Splat參數(shù)與查詢參數(shù)的處理通配符*和:splat是你做批量跳轉(zhuǎn)時的利器但它的匹配細(xì)節(jié)有講究。比如規(guī)則/news/* /blog/:splat 301假設(shè)用戶訪問/news/archive/post1那么重定向后的URL是/blog/archive/post1:splat會保留完整的子路徑。這個行為很直觀但要注意如果*匹配到的內(nèi)容恰好沒有任何字符比如訪問/news/本身那/news/其實可能匹配不上這條規(guī)則。穩(wěn)妥的做法是同時再加一條/news /blog 301把不帶子路徑的情況也覆蓋到。查詢參數(shù)的處理也值得一提。重定向規(guī)則本身默認(rèn)會保留原始請求的查詢參數(shù)。你從/old-page?utm_sourcetest重定向到/new-page時用戶看到的其實是/new-page?utm_sourcetest。這是符合預(yù)期的大多數(shù)時候我們不需要做什么額外處理。如果你特別想去掉某些參數(shù)那沒法用Netlify原生的重定向規(guī)則實現(xiàn)只能通過netlify.toml配合函數(shù)來處理。4. 典型場景實戰(zhàn)記錄4.1 SPA回退與前端路由修復(fù)SPA應(yīng)用部署到Netlify是很多人的日常操作但配置不當(dāng)最容易暴露的問題就是刷新某個子路由時直接404。比如你的Vue或React應(yīng)用部署后訪問/login時一切正常但一刷新頁面就變成“Page Not Found”。原因是SPA只有一個index.html入口所有路由都是由前端JS在瀏覽器端解析的服務(wù)器端并不存在/login這個文件。Netlify默認(rèn)只服務(wù)真實存在的文件找不到就404。解決方案是用一個Splat規(guī)則把未匹配的請求全部重寫到index.html/* /index.html 200這行配置非常有迷惑性看起來簡單但以下幾個點決定了它是否真的好用第一這條規(guī)則必須是_redirects文件中最后一條兜底規(guī)則不能置于其他具體規(guī)則之前否則會干擾其他路徑。第二如果你部署的是帶子路徑的應(yīng)用比如應(yīng)用自身掛在/app/下則要寫成/app/* /app/index.html 200這樣才能保證其他根路徑的訪問不被打擾。第三如果你還用了Service Worker做離線緩存那么index.html的緩存策略要特別注意否則會緩存到舊版本導(dǎo)致路由更新后刷新不到新內(nèi)容。此外如果你的SPA做了按路由拆分index.html里會引用/assets/index-xxx.js這樣帶哈希的資源文件這些路徑都是真實存在的文件所以不會被Splat規(guī)則影響到。這就是為什么兜底規(guī)則放在最后是安全的。4.2 子路徑遷移與舊域名切換站點改版時最頭疼的是舊鏈接全部失效搜索引擎的收錄也會受影響。用Netlify做301跳轉(zhuǎn)是標(biāo)準(zhǔn)解法而且遷移邏輯可以寫得很清晰。比如舊博客的URL結(jié)構(gòu)是/posts/2023/hello-netlify新站的URL結(jié)構(gòu)變成了/blog/hello-netlify你就可以這樣寫/posts/:year/:slug /blog/:slug 301我直接用命名占位符:year和:slug比單純的*更可讀而且:slug會精確匹配對應(yīng)的路徑段不影響后續(xù)子路徑的拼接邏輯。如果整個域名都要切換比如從old-site.com遷移到new-site.com規(guī)則可以寫成/* https://new-site.com/:splat 301!這里用301!強(qiáng)制跳轉(zhuǎn)是因為你希望所有舊域名的請求都直接被送到新域名哪怕請求本來能匹配到某個靜態(tài)文件也不能例外。這里有個細(xì)節(jié)跳轉(zhuǎn)到外部URL時協(xié)議部分必須寫全https://不能省。如果你只寫了//new-site.comNetlify會把它當(dāng)成相對路徑解析結(jié)果就是跳到//new-site.com在瀏覽器里被解析成協(xié)議相對地址看起來像http://new-site.com或者h(yuǎn)ttps://new-site.com但具體取決于當(dāng)前頁面的協(xié)議容易引起混合內(nèi)容警告。我在做舊域名遷移時一般還會順手配一個netlify.toml里的Domain規(guī)則把根域名的/.well-known一類特殊路徑排除在跳轉(zhuǎn)之外避免影響SSL證書簽發(fā)或第三方驗證。這是很多人容易忽略的細(xì)節(jié)。4.3 多語言與區(qū)域分發(fā)Netlify的重定向規(guī)則是可以讀取Country、Language等請求頭的你可以在_redirects里寫如下規(guī)則/ /zh-cn 302 Countrycn / /en 302第一條規(guī)則的含義是當(dāng)請求的國家代碼為cn時訪問首頁會302到/zh-cn。第二條是兜底規(guī)則其他地區(qū)一律跳到英文首頁。用這個思路做國際站的區(qū)域分發(fā)非常順手。而且Netlify的每個部署站點都默認(rèn)開啟了全球CDN請求頭的Country是由CDN邊緣節(jié)點注入的所以這個判斷是實時且準(zhǔn)確的不需要你在應(yīng)用端再做IP庫查詢。不過有一點要提醒Countrycn這類規(guī)則在重定向配置里的匹配值Netlify文檔中稱之為“Country code”。它使用的是ISO 3166-1 alpha-2標(biāo)準(zhǔn)例如US、DE、JP等。如果你要匹配香港地區(qū)對應(yīng)的code是HK但注意大小寫敏感寫錯就不會生效。多語言站點的另一種做法是用URL前綴比如/zh/、/en/。這時用重寫比用重定向更適合因為你希望路徑保持為/zh/about但內(nèi)容實際來自/about-zh.html之類的文件。/zh/* /:splat-zh 200這種方式在SEO上更友好因為URL語義清晰且不需要301跳轉(zhuǎn)帶來的額外流量損耗。5. 安全邊界與必須避開的坑5.1 開放重定向一個容易致命的安全隱患寫重定向規(guī)則最危險的一種情況是允許用戶提供的輸入直接拼進(jìn)重定向目標(biāo)。這句話要細(xì)細(xì)拆解。假設(shè)你的站內(nèi)有一個跳轉(zhuǎn)接口本來是用來做短鏈接中轉(zhuǎn)的你可能會寫成/go/* https://example.com/:splat 301但這個:splat是直接拼接在目標(biāo)URL后面的。如果使用者構(gòu)造一個類似于/go/evil.com的訪問地址實際重定向會變成https://example.com/evil.com這倒還好。但如果規(guī)則寫得不嚴(yán)謹(jǐn)比如/go/* /redirect?url:splat 301而你的應(yīng)用代碼又沒有對url參數(shù)做域名白名單校驗?zāi)蔷徒o釣魚攻擊開了口子。攻擊者可以構(gòu)造/go/https://evil.com誘導(dǎo)用戶點擊后跳轉(zhuǎn)到惡意網(wǎng)站這屬于典型的開放重定向漏洞。我自己處理這類需求時會堅持兩個原則第一能不用用戶輸入拼目標(biāo)地址就盡量不用第二必須在應(yīng)用層加白名單校驗不要在Netlify規(guī)則層解決所有問題因為Netlify的規(guī)則層沒有“允許列表”這種邏輯。如果你確實需要做一個短鏈接跳轉(zhuǎn)服務(wù)我建議用Netlify Function來寫一段校驗邏輯而不是依賴重定向規(guī)則直接拼接。5.2 循環(huán)重定向?qū)懸?guī)則時最容易翻車循環(huán)重定向大概是最令人抓狂的線上事故之一而且它在本地測試時往往表現(xiàn)正常一旦部署到CDN就可能出問題。一個典型的循環(huán)規(guī)則是/old-page /new-page 301 /new-page /old-page 301訪問/old-page跳到/new-page然后再跳到/old-page瀏覽器最終會報ERR_TOO_MANY_REDIRECTS。這類問題通常在規(guī)則數(shù)量多的時候更容易出現(xiàn)尤其是存在通配符規(guī)則時你很難一眼看出兩條規(guī)則是否互相覆蓋。避免循環(huán)重定向的核心是每寫一條規(guī)則都要在腦子里模擬一次完整請求鏈路。我個人的做法是維護(hù)一個“已占用路徑清單”每新增一條規(guī)則前先查一下目標(biāo)路徑是否已經(jīng)被其他規(guī)則引用為源路徑。聽起來很原始但在規(guī)則數(shù)量超過20條之后這個方法比依賴記憶可靠得多。另一個容易引發(fā)循環(huán)的場景是使用force或200!強(qiáng)制重寫但目標(biāo)路徑又被另一條規(guī)則重定向。比如/old/* /new/:splat 200! /new/* /elsewhere/:splat 301本來想“暗中加載/new的內(nèi)容”結(jié)果/new又被跳走訪問者最終還是被重定向了而且行為鏈會變得難以預(yù)測。強(qiáng)制重寫和重定向并存時最好把目標(biāo)路徑徹底排除在其它重定向規(guī)則之外。5.3 動態(tài)規(guī)則與CDN緩存配置改完但不生效許多人反饋我已經(jīng)改好了_redirects規(guī)則并重新部署但線上訪問還是舊行為。這多半是CDN緩存導(dǎo)致的。Netlify的CDN會緩存HTTP響應(yīng)包括301、302這類重定向響應(yīng)。瀏覽器端會遵循Cache-Control頭來決定是否緩存Netlify邊緣節(jié)點也會按一定的TTL緩存。如果你在規(guī)則里設(shè)置了一個永久重定向301它可能會被客戶端瀏覽器長期緩存即便你刪除或修改了規(guī)則用戶的瀏覽器里仍然保存著舊的跳轉(zhuǎn)結(jié)果。這正是我在規(guī)則變更之后必然提醒團(tuán)隊做一次全鏈路驗證的原因??梢杂靡粋€無痕窗口測試或者用curl直接請求并觀察響應(yīng)頭curl -I https://your-site.com/old-page如果返回的狀態(tài)碼和Location頭跟你預(yù)期不一致你再去看是不是有瀏覽器緩存。CDN緩存導(dǎo)致的“不生效”其實可以通過在Netlify后臺做一次“Purge Cache”或者“Clear cache and deploy site”來解決。不要以為重新部署就代表緩存被清掉了這兩件事是分開的。這意味著線上環(huán)境的規(guī)則變更你要先清緩存再驗證否則容易得出“配置沒生效”的錯誤結(jié)論。6. 調(diào)試重定向的實用手段6.1 用Netlify CLI在本地調(diào)試線上調(diào)試重定向最難受的地方是任何改動都要經(jīng)歷“代碼提交 - 部署 - CDN緩存刷新 - 驗證”這條鏈路來回一趟至少幾分鐘遇到緩存問題甚至要等更久。好在Netlify CLI提供了一個本地預(yù)覽功能可以模擬生產(chǎn)環(huán)境的諸多行為包括重定向規(guī)則。我通常的操作步驟是安裝CLInpm install -g netlify-cli登錄并鏈接站點netlify login然后netlify link啟動本地開發(fā)服務(wù)器netlify devnetlify dev會讀取你項目中的netlify.toml和_redirects文件在本地啟動一個模擬服務(wù)。這時候你可以直接訪問localhost上的端口測試各種重定向規(guī)則是否按預(yù)期工作。這個模擬環(huán)境對規(guī)則順序、通配符匹配的還原度相當(dāng)高踩過坑之后我?guī)缀醪辉僖蕾嚒安渴鸷笤诰€驗證”來做規(guī)則調(diào)試。還有一個比較好用的技巧在本地測試時加上curl -I看響應(yīng)頭。curl -I http://localhost:8888/old-path把Location字段看清楚你就知道瀏覽器最終會跳去哪。如果發(fā)現(xiàn)規(guī)則沒生效第一步就是檢查路徑是否帶斜杠、通配符是否拼寫正確以及是否被更靠前的規(guī)則截胡。6.2 線上日志驗證如果問題只在線上出現(xiàn)本地模擬無法復(fù)現(xiàn)那就要借助Netlify的Deploy Log和Edge Log去判斷。Netlify后臺的“Logs”面板里能看到邊緣側(cè)的請求日志包括規(guī)則命中的情況。雖然它不會明確告訴你“命中了哪一條規(guī)則”但通過狀態(tài)碼和返回路徑你可以反推出請求是否進(jìn)入了預(yù)期分支。比如你訪問/about返回200且內(nèi)容是/zh/about的內(nèi)容那就說明匹配到了一條200重寫規(guī)則。如果返回301且Location是/blog/about就說明匹配到了301跳轉(zhuǎn)。這種反推思路適用于絕大多數(shù)排查場景。如果你的站點開啟了Netlify Analytics還可以用“Top Pages”和“Redirects”視圖觀察哪些規(guī)則被觸發(fā)得最頻繁。我一般在設(shè)定新的跳轉(zhuǎn)規(guī)則后會過一周去查看這部分?jǐn)?shù)據(jù)確認(rèn)舊鏈接的流量是否都按預(yù)期遷移完畢。6.3 規(guī)則調(diào)試速查表我在項目里沉淀了一個內(nèi)部用的排查表格分享出來供大家參考癥狀可能原因排查方向訪問路徑顯示404規(guī)則未匹配到路徑大小寫不一致確認(rèn)路徑精確性檢查通配符寫法跳轉(zhuǎn)生效但URL不變配置成了200重寫而非301重定向檢查狀態(tài)碼是否為200確認(rèn)是否預(yù)期行為規(guī)則改了但線上沒變化CDN或瀏覽器緩存清緩存用無痕窗口驗證瀏覽器提示重定向過多存在循環(huán)規(guī)則逐條梳理源與目標(biāo)路徑找出互相引用特定地區(qū)訪問落到錯誤頁面Country匹配大小寫或code寫錯核對ISO標(biāo)準(zhǔn)確認(rèn)規(guī)則順序舊路徑訪問到了靜態(tài)文件靜態(tài)文件優(yōu)先級高于重定向在目標(biāo)規(guī)則末尾加感嘆號!強(qiáng)制執(zhí)行這張表不只是給新人用的我自己的經(jīng)驗是規(guī)則過多時即使老手也容易一時腦熱漏掉某個細(xì)節(jié)。排查時按表逐項對照能在最短時間內(nèi)定位問題少折騰幾輪部署。7. 規(guī)則維護(hù)的個人經(jīng)驗最后再分享一點我在實際項目里長期維護(hù)重定向規(guī)則的心得。首先給每一組規(guī)則寫清楚注釋。在_redirects文件里注釋行以#開頭不要吝嗇那幾行字。寫清楚“為什么有這個規(guī)則”、“對應(yīng)哪個舊需求”、“目標(biāo)路徑是哪次改版引入的”半年后你排查問題時會感激當(dāng)時的自己。其次不要把無關(guān)規(guī)則堆在一個文件里。如果你的站點體量比較大涉及多語言、多版本控制、活動頁跳轉(zhuǎn)等強(qiáng)烈建議按場景拆分規(guī)則用netlify.toml分塊管理而不是全部擠在_redirects里。雖然_redirects單文件寫法簡單但可讀性會快速惡化規(guī)則超過30條之后你很難一眼看出每條規(guī)則的作用。再次盡量在規(guī)則里使用301而不是302作為默認(rèn)跳轉(zhuǎn)。雖然302對搜索引擎更溫和但從運營角度看大部分跳轉(zhuǎn)場景都是永久性的。而且301對SEO的權(quán)重傳遞也更有利。如果只是臨時活動或A/B測試頁面才應(yīng)該使用302。這兩者的語義差異在搜索引擎眼中非常明顯弄反了會造成權(quán)重?zé)o法正確歸集。最后有一個提案值得嘗試把重定向配置納入代碼評審的流程中。重定向規(guī)則雖然不直接參與業(yè)務(wù)邏輯但它直接影響用戶體驗和SEO屬于“改了錯誤影響很大”的配置類型。每次變更都讓團(tuán)隊里至少另一個人review一遍能避免不少低級錯誤。我在平常工作中見過最糟糕的重定向事故幾乎都不是因為語法難寫而是因為規(guī)則在不知不覺中互相覆蓋或者順序錯亂。你只要在流程上稍微留個心眼就能避免很多不必要的線上故障。