開發者工具

URL 編碼是什麼?中文網址與特殊字元如何 Encode/Decode

了解 URL 百分比編碼的用途,處理中文、空格、&、? 等特殊字元,避免網址參數失效。

#URL 編碼#URL Decode#百分比編碼

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,就能直接取得編碼結果並複製使用。

常見問題

網址全部都要編碼嗎?

通常只應編碼參數值或網址元件,不要把分隔結構一併錯誤編碼。

空格應該編碼成 %20 還是 +?

兩種都會遇到。%20 在網址的任何位置都代表空格;+ 只有在查詢字串採用表單編碼時才代表空格。自己組網址時用 %20 最不容易出錯。

為什麼解碼後會出現 %25E5 這類內容?

這是重複編碼。% 本身被再編碼一次變成 %25,所以 %E5 變成了 %25E5。解決方式是確認每個值在組網址時只編碼一次。

喜歡這篇文章嗎?

你的支持能幫助 Alan 工具箱持續維護,並新增更多免費工具與教學內容。

支持 Alan 工具箱 ↗