URL 編碼會把中文、空格與特殊字元轉為 % 開頭的格式,避免它們被誤認為網址結構。使用URL Encoder可產生編碼結果;收到難讀的參數時,用URL Decoder還原。這篇文章說明編碼規則怎麼運作、哪些字元需要編碼、JavaScript 兩個編碼函式的差別,以及實務上最常踩到的幾個問題。
為什麼網址需要編碼
網址裡有些字元負責標示結構:
?開始查詢字串&分隔不同的參數=分隔參數名稱與值#開始頁面內的片段/分隔路徑
假設搜尋關鍵字是 tom&jerry,直接放進網址會變成:
https://example.com/s?q=tom&jerry&page=2
伺服器讀到的 q 只有 tom,後面的 jerry 被當成另一個沒有值的參數。把 & 編碼成 %26 之後:
https://example.com/s?q=tom%26jerry&page=2
q 的值才會是完整的 tom&jerry。這就是 URL 編碼最主要的用途:讓資料裡的特殊字元不被當成網址結構。另一個用途是處理空格、中文這類不適合直接出現在網址裡的字元。
編碼規則:UTF-8 位元組加上百分比符號
URL 編碼的正式名稱是百分比編碼(percent-encoding)。做法是把字元先轉成 UTF-8 位元組,每個位元組寫成 % 加兩位十六進位數字。
- 空格的位元組是十六進位 20,所以編碼成
%20。 &的位元組是十六進位 26,所以編碼成%26。- 中文字「台」在 UTF-8 是三個位元組 E5、8F、B0,所以編碼成
%E5%8F%B0。
一個中文字通常會變成 9 個字元,表情符號則是四個位元組,例如 😀 編碼後是 %F0%9F%98%80。這也是含中文的網址複製出來常常很長的原因。
以下是用 encodeURIComponent 實際執行的結果:
| 原始文字 | 編碼結果 |
|---|---|
台北 101 |
%E5%8F%B0%E5%8C%97%20101 |
a&b=c |
a%26b%3Dc |
咖啡 & 茶 |
%E5%92%96%E5%95%A1%20%26%20%E8%8C%B6 |
C++ |
C%2B%2B |
100% |
100%25 |
1+1=2 |
1%2B1%3D2 |
英文字母、數字,以及 -、_、.、~ 這幾個符號不需要編碼,會原樣保留。
常見情況
查詢參數中的 &、?、# 有特殊意義。若它們本來是資料的一部分,應編碼後再放入參數值。編碼不是加密,網址中仍不應放入密碼或敏感資料。
任何人都可以把編碼後的內容還原,例如 %E5%8F%B0%E5%8C%97 解碼就是「台北」。另外,網址通常會留在瀏覽器紀錄與伺服器記錄檔裡,所以機密資料不適合放在網址中,和有沒有編碼無關。
encodeURI 與 encodeURIComponent 的差別
JavaScript 有兩個編碼函式,最常見的錯誤就是用錯。
encodeURIComponent:用來編碼單一個值,會把/、?、#、&、=、+等結構字元一併編碼。encodeURI:用來編碼整個網址,會保留上述結構字元,只處理空格、中文等不合法的字元。
同一段含結構字元的文字 /?#&=+ (最後是一個空格),兩者的輸出是:
encodeURIComponent → %2F%3F%23%26%3D%2B%20
encodeURI → /?#&=+%20
套用到完整網址 https://example.com/search?q=台北 101&lang=zh-TW:
encodeURI:
https://example.com/search?q=%E5%8F%B0%E5%8C%97%20101&lang=zh-TW
encodeURIComponent:
https%3A%2F%2Fexample.com%2Fsearch%3Fq%3D%E5%8F%B0%E5%8C%97%20101%26lang%3Dzh-TW
第一個結果仍然是可以開啟的網址;第二個結果把 ://、/、? 都編碼掉了,已經不能當網址使用,只適合當成「另一個網址裡的參數值」,例如登入後要導回的頁面。
判斷原則:編碼參數值用 encodeURIComponent,整理一整條網址才用 encodeURI。在 URL Encoder 工具裡,「處理模式」的「URI Component」與「完整網址」就是對應這兩種做法。
正確的組網址方式
比較安全的做法是只編碼值,再用 & 和 = 組起來:
const keyword = "咖啡 & 茶";
const url = "https://example.com/search?q=" + encodeURIComponent(keyword) + "&page=2";
// https://example.com/search?q=%E5%92%96%E5%95%A1%20%26%20%E8%8C%B6&page=2
或是交給瀏覽器與 Node.js 內建的 URL 物件處理,不必自己記規則:
const u = new URL("https://example.com/search");
u.searchParams.set("q", "咖啡 & 茶");
u.searchParams.set("page", "2");
u.toString();
// https://example.com/search?q=%E5%92%96%E5%95%A1+%26+%E8%8C%B6&page=2
兩種寫法的結果只有一個差別:空格在第一種是 %20,在第二種是 +。
空格:%20 還是 +
searchParams 採用的是表單編碼,會把空格寫成 +。這種寫法只在查詢字串裡有效,而且解碼端也要用同樣的規則。
需要注意的是,decodeURIComponent 不會把 + 還原成空格:
decodeURIComponent("a+b"); // "a+b"
new URLSearchParams("q=a+b").get("q"); // "a b"
所以如果解碼後發現文字裡的空格都變成 +,代表原本是表單編碼。URL Decoder 在 URI Component 模式下提供「將 + 轉換成空格」的選項,就是用來處理這種情況。反過來說,如果資料本身就有加號(例如 C++),務必編碼成 %2B,否則在表單編碼的規則下會被解讀成空格。
常見錯誤與除錯方式
重複編碼
已經編碼過的字串再編碼一次,% 會變成 %25:
台北 → %E5%8F%B0%E5%8C%97
再編碼一次 → %25E5%258F%25B0%25E5%258C%2597
看到 %25E5 這種樣式,就可以確定是重複編碼。原因通常是程式裡手動編碼了一次,使用的函式庫又自動編碼了一次。解決方式是找出其中一處並移除,而不是在接收端解碼兩次。
解碼時出現錯誤
decodeURIComponent 遇到不完整或不合法的編碼會直接丟出錯誤,例如:
decodeURIComponent("%E5%8F"); // URIError: URI malformed
decodeURIComponent("100%"); // URIError: URI malformed
第一個例子是中文字的三個位元組只剩兩個,常見於網址被截斷;第二個例子是資料裡有單獨的 %,沒有先編碼成 %25。處理使用者輸入時,記得用 try...catch 包起來。
把整個網址丟進 encodeURIComponent
結果就像前面看到的,https:// 會變成 https%3A%2F%2F,網址無法開啟。除非這條網址是要當作另一個網址的參數值,否則不應該這樣做。
沒有編碼就直接串接
參數值含有 &、#、+ 或空格時,沒有編碼會讓參數被截斷或解讀錯誤。只要值是來自使用者輸入或資料庫,都應該先編碼。
實用建議
- 拿到一長串看不懂的網址,先用URL Decoder解碼,比較容易看出每個參數的內容。
- 要把含中文的連結貼到文件或訊息裡,可以保留編碼後的版本,避免部分軟體在中文字或空格的地方把連結切斷。
- 參數值如果是 JSON,先確認內容正確再編碼。可以搭配JSON Formatter檢查格式。
- 網頁內容裡要顯示
<、>、&這類符號時,需要的是 HTML 實體編碼而不是 URL 編碼,兩者用途不同,可以使用HTML Entity Encoder。
小結
- URL 編碼把字元轉成 UTF-8 位元組,再寫成
%加兩位十六進位。 - 編碼參數值用
encodeURIComponent,整條網址才用encodeURI。 - 空格可能是
%20或+,解碼時要用對應的規則。 - 看到
%25開頭的編碼,先懷疑重複編碼。 - 編碼不是加密,不能用來保護資料。
需要快速轉換時,把文字貼進URL Encoder,就能直接取得編碼結果並複製使用。