URL 編碼與解碼工具
「參數」會連 / ? & = 一併跳脫,適合單一查詢值。「完整網址」會保留這些字元,讓網址仍可正確解析。
您輸入的內容不會被儲存、記錄或分享。
將文字進行網址百分比編碼,或解碼回原文。編碼時可選擇處理單一查詢值或整個網址,因為兩者需要不同的跳脫方式。
使用方式
- 選擇編碼或解碼。
- 編碼時,單一值請選「參數」,完整網址請選「完整網址」。
- 貼上文字並複製結果。
為什麼有兩種模式
RFC 3986 把網址中可能出現的字元分為兩組。非保留字元,也就是英文字母、數字,以及 - _ . ~ 四個符號,永遠代表字元本身,從不編碼。保留字元則具有結構意義:通訊協定後面的冒號、路徑區段之間的斜線、開始查詢字串的問號、分隔參數的 & 與等號,以及開始片段的井字號。
兩種模式只差在如何處理第二組字元。元件模式也會跳脫保留字元,適用於文字要放進單一位置、不能被當作結構解讀的情況,例如單一查詢值或一個路徑區段。完整網址模式則保留它們,讓整個位址在轉換後,通訊協定、斜線與分隔符號仍能正常發揮作用。
用錯模式通常很明顯。把整個位址用元件模式處理,https://example.com 會變成 https%3A%2F%2Fexample.com,就再也連不上了。把單一值用完整網址模式處理,值裡混進的 & 會悄悄把你的參數拆成兩個。
會惹麻煩的字元
空格是大家最先遇到的。在路徑或查詢中它會變成 %20;在 HTML 表單送出時則會變成 +,因為較舊的 application/x-www-form-urlencoded 規則就是這樣規定,而這兩種慣例至今都還天天在用。這就是解碼時有「將 + 視為空格」選項的原因:處理表單查詢字串時打開,處理路徑時關閉,因為在路徑中 + 就是字面上的加號,轉成空格會破壞值。
值裡面的 & 和等號必須跳脫,否則伺服器會在錯誤的位置切割查詢字串,最典型的例子就是剛好含有這些字元的搜尋詞或 Base64 字串。井字號則根本不會送到伺服器:它後面的一切都是由瀏覽器處理的片段,所以參數中未跳脫的 # 會悄悄截斷請求。
值中的斜線需要特別留意。以 %2F 百分比編碼是合法的,但不少伺服器與代理伺服器會在路由前先解碼,所以它仍可能被當成路徑分隔符號。如果值可能包含斜線,把它放在查詢字串而不是路徑中,就能完全避開這個問題。
雙重編碼
百分比編碼不具冪等性。百分比符號本身也是需要編碼的字元,會變成 %25,所以對已經編碼過的字串再編碼一次,%20 就會變成 %2520。這個序列是網址多經過一次編碼的明顯痕跡,看過一次之後,你會在各處認出它。
當應用程式碼先編碼了一個值,接著框架、HTTP 用戶端或以為自己收到原始文字的範本又編碼一次,就會發生這種情況。解法是移除兩個步驟中的一個,而不是在接收端解碼兩次,後者只是把錯誤移到別處。在這裡每層解碼一次,觀察 %2520 是否先變成 %20、再變成空格,就能知道實際上有幾層。
非 ASCII 字元,以及百分比編碼不處理的部分
文字會先轉成 UTF-8,再把每個位元組做百分比編碼,所以一個中文字會變成三組跳脫序列,而不是一組。這是網址標準的要求,也是中文、日文、韓文、帶重音的拉丁字母與表情符號都能在這裡正確來回轉換的原因。
網域名稱是例外,而且這個區別很重要。網址的主機部分不做百分比編碼:國際化網域名稱改用 Punycode 轉換,產生以 xn-- 開頭的名稱。把主機名稱做百分比編碼,得到的東西看起來像已編碼,卻無法解析到任何地方。本工具只處理路徑、查詢字串與片段。
一個 URL 的組成
URL 的組成部分比多數人實際用到的還多:scheme、選用的 userinfo 區段、host、選用的 port、path、query 和 fragment。完整寫出來的話,https://user:[email protected]:8443/a/b?x=1#top 就把每一個部分都用上了。userinfo 這種寫法在 HTTP 中已經不建議使用,瀏覽器會把它移除或發出警告,原因正是它曾被用來讓惡意主機看起來像是使用者熟悉的那一個。
各個部分的規則並不相同。host 不分大小寫,而且不能包含百分號編碼;path 在多數伺服器上是區分大小寫的;query 除了「是一串字串」之外沒有標準文法,用 key=value 搭配 & 分隔只是慣例而非規格;而 fragment 根本不會離開瀏覽器。
最容易讓人意外的是相對路徑的解析。從 /a/page 連到 b/c 會解析成 /a/b/c,但從 /a/page 出發就會變成 /a/page/b/c,結尾的斜線改變了基準位址。這就是為什麼少一條或多一條結尾斜線,會讓頁面上所有相對資源都失效,也是為什麼值得花一次功夫,在兩種形式之間設好正規化的轉址。
查詢字串只有慣例,沒有規則
查詢字串沒有表達陣列的標準寫法,所以每個框架的做法都不一樣:tags=a&tags=b 重複同一個鍵,tags[]=a&tags[]=b 加上中括號,tags=a,b 則用分隔符號。這三種都是合法的 URL,而且會依照另一端使用的函式庫解析成不同的東西。
巢狀結構、布林值與空值也是同樣的情況。flag= 到底代表 false、空字串,還是「有這個鍵但沒有值」?答案取決於接收端的解析器怎麼決定,所以在意這件事的 API 應該把它寫進文件,並且實際驗證,而不是憑假設。
在 JavaScript 中,URLSearchParams 能正確處理編碼與重複的鍵,應該優先使用它,而不是自己手動拼字串。要注意的是,它沿用表單的慣例把空白編碼成加號,而 encodeURIComponent 產生的是 %20;兩者在查詢字串中都會解碼成空白,但在路徑中只有其中一種會。
藏在 URL 處理中的資安問題
開放轉址指的是某個端點會把訪客導向參數中傳入的 URL,卻不做任何檢查。它被用來讓釣魚連結從受害者信任的網域開始,至今仍是資安檢視中經常出現的問題。修正方式是建立一份目的地的允許清單,或是只接受路徑而不接受絕對 URL。
伺服器端請求偽造是同一類問題轉向內部的版本:伺服器去抓取使用者提供的 URL,而使用者提供的是內部位址、雲端的中繼資料端點,或是指向這些位置的轉址。不同函式庫在解析上的差異在這裡一再被利用,因為兩個解析器對於一個精心構造的 URL 究竟指向哪一台主機,可能有不同的認定。
同形異義字攻擊則利用國際化網域名稱:西里爾字母與拉丁字母在畫面上看起來完全一樣,卻是不同的主機。瀏覽器的防禦方式是當名稱混用多種文字系統時顯示 punycode 形式,這就是為什麼可疑連結有時會在網址列顯示成 xn-- 開頭。這個前綴值得記起來。
正規網址、重複內容與追蹤參數
同一個頁面通常可以透過好幾個網址抵達:有 www 和沒有 www、有結尾斜線和沒有、http 和 https、帶著 index.html 結尾,以及後面接上任意數量的追蹤參數。對搜尋引擎來說,這些可能看起來是內容相同的不同頁面,於是這個頁面累積下來的權重就被分散掉了。
有兩種機制可以解決。301 轉址處理結構上的變體:挑一種形式,把其他全部導過去。head 裡的 canonical 連結元素則為那些無法轉址的變體指名偏好的網址,主要就是帶參數的情況。
追蹤參數值得特別說明:utm_source 這一系列參數是由來源系統加上去的,每一檔活動都會產生一個不同的網址。它們屬於分析工具,不該出現在正規網址裡;在記錄完成之後於用戶端把它們去掉,可以讓分享出去的連結保持乾淨。某些平台會自動附加的 fbclid 與 gclid 參數也是一樣。
設計經得起時間考驗的路徑
URL 是一種承諾,因為別人會連到它,而你無法控制那些連結。這意味著路徑應該描述資源本身,而不是實作方式:用 /guides/subnetting 而不是 /page.php?id=4718,也不要留下把網址綁在特定技術上的副檔名。
全部用小寫並以連字號分隔。伺服器區分大小寫代表 /About 和 /about 可能是兩個不同的頁面,而這絕不會是刻意為之;連字號則是 slug 中慣用的分詞符號。除非內容真的屬於封存性質,否則避免在路徑裡放日期,因為 /2024/pricing 過了一年就顯得過時,即使內容其實是最新的。
當網址非改不可時,請把舊網址設成永久轉址,並且一直保留下去。這份清單裡唯一無法從你這端挽回的決定,就是把連結弄壞,因為最重要的那些連結往往來自你從沒聽過的網站。
常見問題
- 「參數」與「完整網址」有什麼差別?
- 「參數」使用 encodeURIComponent,會連保留字元 / ? & = : 一併跳脫,適合單一查詢值或路徑片段。「完整網址」使用 encodeURI,保留這些字元讓網址仍可解析。若用「參數」處理整個網址,https:// 會變成 https%3A%2F%2F 而失效。
- 查詢參數應該用哪一個?
- 用「參數」。若值中含有 & 或 =,未跳脫時伺服器會當成分隔符號,導致參數在錯誤的位置被切開。
- 為什麼解碼時出現跳脫格式錯誤?
- 百分比符號後面必須接兩個十六進位數字。像「100%」這樣單獨的 %,或 %E4%B8 這種被截斷的序列都是無效的,因此解碼會停止而不是猜測。
- 什麼時候需要「將 + 視為空格」?
- 表單送出時會把空格編碼為 + 而非 %20。解碼來自 HTML 表單的查詢字串時請啟用。處理路徑片段時請關閉,因為 + 就是加號本身。
- 可以處理中文嗎?
- 可以。文字會先轉換為 UTF-8 再進行百分比編碼,因此中文、日文、韓文與表情符號都能正確往返轉換。
最後更新:
請提供操作步驟、預期結果及實際結果。請移除機密與個人資料。
