跳到主要內容

← 返回部落格

URL 編碼完整說明:百分比編碼、空格 %20、中文與線上編碼解碼

有沒有貼過一條連結,發現本該是文字的地方變成 %E4%B8%AD,或某個查詢參數莫名壞掉?那就是 URL 編碼在運作。搞懂它,那些看不懂的字串就變成你能讀、能推理的東西。

一個輸入,三個重點。 我們用 URL 編碼工具編碼 台北 101 & friends?,得到:

%E5%8F%B0%E5%8C%97%20101%20%26%20friends%3F

這串字裡有三件事。每個中文字變成三個位元組(%E5%8F%B0),因為 UTF-8 表示 CJK 需要三個。空白變成 %20不是 + —— 加號那種寫法屬於表單內容,不屬於路徑。而 &? 之所以被轉義,正是因為不轉義它們會結束這個值、開始一個新參數。同一段文字貼進去就能對照。

什麼是 URL 編碼?

URL 編碼又叫百分比編碼(percent-encoding),是把網址裡「不能直接放」或「放了會被誤讀」的字元,改用一種安全寫法表示的機制。做法是把該字元換成 % 加上它那個位元組的兩位十六進位值。例如空格變成 %20,& 變成 %26,中文的「中」(UTF-8)是三個位元組,寫成 %E4%B8%AD

這套規則來自網址標準 RFC 3986,目的很單純:一條網址不論被複製、貼到聊天室、寄 email,或被任何伺服器解析,都不會產生歧義。

你可以用 URL 編碼 / 解碼工具即時處理任何字串,而且全程在瀏覽器本機執行。

為什麼網址需要編碼

網址只能包含有限的 ASCII 字元。超出這個範圍的東西 —— 空格、帶重音的字母、中文日文等非 ASCII 文字,或像 ?&#/ 這種本身帶特殊意義的符號 —— 都得做百分比編碼,才不會被誤認成網址的結構。

? 來說:它在網址裡代表查詢字串的開頭。如果你想在「某個值裡面」放一個字面的問號,就得寫成 %3F,否則解析器會把它後面的東西全當成參數。編碼就是為了消除這種歧義。

常見符號怎麼編

以下是大家最常遇到的符號,以及它們的百分比編碼:

字元編碼後說明
空格%20只有表單資料才會用 +(見下方)
&%26分隔查詢參數
?%3F查詢字串的開頭
=%3D分開鍵與值
#%23片段(fragment)的開頭
/%2F路徑分隔符
+%2B字面的加號,避免被讀成空格
%E4%B8%AD非 ASCII,依 UTF-8 逐位元組編碼

非 ASCII 文字(中文、日文、emoji、帶重音的拉丁字母)會先轉成 UTF-8 位元組,再把每個位元組各自百分比編碼。這就是為什麼一個看得見的字,常常會展開成好幾組 %XX

這是一個字串跑過我們自己的 URL 編碼工具:台北 101 & friends? 變成 %E5%8F%B0%E5%8C%97%20101%20%26%20friends%3F。從裡面看得出三件事 —— 每個中文字佔了個位元組、空白變成 %20 而不是 +、而 &? 被轉義,因為不轉義它們會結束這個值、開始一個新參數。

與其再抄一次 RFC 的表,這裡放的是我們自己的網址編碼工具對八個輸入實際回傳的結果 —— 這八個是刻意挑的,每一類至少中一個。其中兩個原封不動回來,而那才是重點:~_.- 屬於未保留字元,正確的編碼器就該放它們過去。

輸入編碼後
hellohello
hello worldhello%20world
a+b=ca%2Bb%3Dc
100%100%25
台北%E5%8F%B0%E5%8C%97
?q=x&r=y%3Fq%3Dx%26r%3Dy
a/ba%2Fb
~_.-~_.-

保留字元 vs 非保留字元

  • 非保留字元(A–Z a–z 0–9 - _ . ~)永遠不需編碼。
  • 保留字元(: / ? # [ ] @ ! $ & ' ( ) * + , ; =)在網址中有結構意義。要不要編碼看情境 —— 在「值」裡面必須編碼,當作「結構」時則不能編碼。

這個區別,正是下面 encodeURIComponentencodeURI 差別的核心。

什麼時候要編碼?

一句話:編碼的是「你要塞進網址的那些片段」,不是「已經組好的整條網址」。

  • 要編碼的是你正在插入的 —— 搜尋關鍵字、檔名、要導向的目標網址,或任何來自使用者輸入、其他系統的東西。
  • 不要無腦地把一條完整、已經組好的網址整個拿去編碼 —— 那會把撐起結構的 / ? & = 一起跳脫掉,連結就斷了。

一個好用的心法:網址的結構由你自己搭,每一塊值都在「放進去之前」先各自編碼。把參數值「紅 & 藍」編碼後得到 %E7%B4%85%20%26%20%E8%97%8D,就能安全地嵌進 ?color=...

encodeURIComponent vs encodeURI

  • encodeURIComponent 連保留字元也編碼。用於單一值 —— 一個查詢參數、一個路徑片段。
  • encodeURI 保留結構字元(/ ? & =)。用於你不想拆散的整條網址
encodeURIComponent("a b&c=d") // "a%20b%26c%3Dd"  ✅ 當值很安全
encodeURI("https://x.com/a b") // "https://x.com/a%20b"  ✅ 整條網址

選錯是常見 bug 來源:對一個值用了 encodeURI,裡面的 & 不會被跳脫,就會破壞你的查詢字串。

編碼空格:%20 還是 +

空格大概是最容易讓人搞混的字元。%20+ 都能代表空格,但用在不同情境:

  • 網址路徑與現代的查詢字串裡,標準是 %20
  • + 代表空格源自較舊的 application/x-www-form-urlencoded 格式(HTML 表單送出用)。在那種格式裡,字面的 + 反而要寫成 %2B

不確定時,%20 是安全、通用的選擇 —— 也是我們工具產生的格式。

對照一下,同一段文字丟進我們的 Base64 工具5Y+w5YyXIDEwMQ==。兩者的起點是同樣那些 UTF-8 位元組,接著就完全分岔:Base64 把每一個位元組重新用 64 個字元的字母表表示、並補齊到 4 的倍數;百分比編碼則讓安全字元原樣留著,只轉義其餘的。

+ 的混亂也是從這裡開始的。我們的編碼器對 a+b=c 回傳 a%2Bb%3Dc —— 加號自己變成了 %2B。它不得不這樣做。如果字面的 + 被原樣留下,任何把 + 讀成空白的解碼器都會靜靜地把值弄壞,而表單解碼器就是這樣讀的。所以「+ 代表空白」和「+ 代表加號」不可能同時未編碼地存在;編碼器的解法就是根本不輸出裸的 +

經典的「重複編碼」bug

如果一個字串被編碼兩次,%20 會變成 %2520(因為 % 自己被編碼成 %25)。症狀:瀏覽器裡出現字面的 %20,或空格變成 %2520。解法是剛好只編碼一次 —— 不確定輸入狀態就先解碼。用解碼工具可以輕鬆檢視你手上到底是什麼。

同一個機制也解釋了重複編碼。% 在網址裡同樣不安全 —— 我們的編碼器把 100% 變成 100%25。把這個輸出再丟進編碼器一次,%25 就變成 %2525:仍然是合法網址、仍然解得開,只是解出來的是錯的字串。重複編碼從來不會是語法錯誤,這也是它能一路活到正式環境的原因。

如何線上編碼與解碼網址

你不用背十六進位對照表。把文字或網址貼進線上 URL 編碼 / 解碼工具,在「編碼」和「解碼」之間切換,再複製結果就好。它完全在瀏覽器裡運算,貼進去的內容不會上傳 —— 對含有權杖或個資的連結特別安心。如果要處理的是二進位資料或標頭,你要的是 Base64;Base64 與 URL 編碼的差別這篇會說明各自的用途。

常見問題

分享一條看起來正常的網址前,需要先編碼嗎? 通常不用 —— 如果它在瀏覽器裡已經能用,就沒問題。只需要編碼你正在組進新網址的那些值。

為什麼我的 & 把連結截成兩段? 沒編碼的 & 會開啟新的查詢參數。在值裡面它必須寫成 %26

連結裡怎麼冒出 %2520? 那是被重複編碼的空格。解碼一次,就能還原成原本的 %20

%20+ 可以互換嗎? 只有在 x-www-form-urlencoded 的表單內容裡可以。在路徑與一般查詢字串裡,請優先用 %20

快速對照

  • 編碼參數值?→ encodeURIComponent
  • 編碼整條網址?→ encodeURI
  • 要放空格?→ %20(只有表單資料才用 +)。
  • 看到零星的 %XX?→ 解碼它讀出原文。

用免費的 URL 編碼 / 解碼工具即時編碼與解碼網址與查詢參數 —— 全程在瀏覽器本機執行。