有沒有貼過一條連結,發現本該是文字的地方變成 %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 的表,這裡放的是我們自己的網址編碼工具對八個輸入實際回傳的結果 —— 這八個是刻意挑的,每一類至少中一個。其中兩個原封不動回來,而那才是重點:~、_、.、- 屬於未保留字元,正確的編碼器就該放它們過去。
| 輸入 | 編碼後 |
|---|---|
hello | hello |
hello world | hello%20world |
a+b=c | a%2Bb%3Dc |
100% | 100%25 |
台北 | %E5%8F%B0%E5%8C%97 |
?q=x&r=y | %3Fq%3Dx%26r%3Dy |
a/b | a%2Fb |
~_.- | ~_.- |
保留字元 vs 非保留字元
- 非保留字元(
A–Z a–z 0–9 - _ . ~)永遠不需編碼。 - 保留字元(
: / ? # [ ] @ ! $ & ' ( ) * + , ; =)在網址中有結構意義。要不要編碼看情境 —— 在「值」裡面必須編碼,當作「結構」時則不能編碼。
這個區別,正是下面 encodeURIComponent 與 encodeURI 差別的核心。
什麼時候要編碼?
一句話:編碼的是「你要塞進網址的那些片段」,不是「已經組好的整條網址」。
- 要編碼的是你正在插入的值 —— 搜尋關鍵字、檔名、要導向的目標網址,或任何來自使用者輸入、其他系統的東西。
- 不要無腦地把一條完整、已經組好的網址整個拿去編碼 —— 那會把撐起結構的
/ ? & =一起跳脫掉,連結就斷了。
一個好用的心法:網址的結構由你自己搭,每一塊值都在「放進去之前」先各自編碼。把參數值「紅 & 藍」編碼後得到 %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 編碼 / 解碼工具即時編碼與解碼網址與查詢參數 —— 全程在瀏覽器本機執行。