你可能遇過這種狀況:明明前端把查詢參數帶好了,後端卻收到亂碼;或是網址中的 +、空白、中文一混在一起,API 就開始回 400。這些問題多半不是「網路壞掉」,而是 URL 編碼(URL Encoding) 沒有在正確的時機、對正確的區段處理。
一、URL 編碼是什麼?為什麼要做?
URL 原本只能安全傳遞一小部分字元(英數與少數符號)。當你要在 URL 放入空白、中文、emoji、或像 &、= 這種具語意的符號時,必須先把它轉成百分比編碼(Percent-Encoding),格式是 % 加上兩位十六進位值,例如空白常見為 %20。
如果不編碼,URL 解析器可能把你的資料當成分隔符號,導致參數被切斷、值被誤解,甚至被惡意插入額外參數。
二、哪些字元需要編碼?
實務上可以分成三類來理解:
- Unreserved(通常不需編碼):
A-Z、a-z、0-9、-、_、.、~ - Reserved(有特殊語意):如
:、/、?、#、[、]、@、&、= - 非 ASCII 字元:中文、日文、韓文、emoji 等都要先轉成 UTF-8 再百分比編碼
重點不是「全部都編」,而是「在正確區段中,只編該編的字元」。
三、空白為什麼有時是 %20,有時是 +?
這是最常見的混淆點。一般 URL 百分比編碼中,空白常表示為 %20。但在 application/x-www-form-urlencoded(例如 HTML 表單提交)規則中,空白會轉成 +。因此你會看到同一個空白在不同情境出現兩種形式。
若系統把 + 當字面值,而不是空白,就會發生資料不一致。前後端需要先對齊編碼規格。
四、JavaScript:encodeURI 與 encodeURIComponent 差在哪?
兩者都會做編碼,但用途不同:
encodeURI:用在整段 URL,會保留像:、/、?這些 URL 結構符號。encodeURIComponent:用在單一參數值,會連&、=也一起編掉,避免汙染查詢字串結構。
const keyword = 'C++ 教學 & 範例';
const url = '/search?q=' + encodeURIComponent(keyword);
// /search?q=C%2B%2B%20%E6%95%99%E5%AD%B8%20%26%20%E7%AF%84%E4%BE%8B
如果你把參數值用 encodeURI 代替,& 可能被誤判成新參數分隔符。
五、常見錯誤:雙重編碼(Double Encoding)
雙重編碼通常發生在「前端先編一次,後端或 SDK 又再編一次」。例如原本的 % 會變成 %25,資料看起來就像被破壞。
- 原始:
hello world - 一次編碼:
hello%20world - 二次編碼:
hello%2520world
建議在系統設計文件明確定義「誰負責編碼、誰負責解碼」,避免責任重疊。
六、Path、Query、Fragment 要分開處理
URL 不同區段有不同規則。最常見的錯誤是把整段 URL 當同一字串處理,造成路徑斜線被錯誤編碼,或查詢參數漏編。
| 區段 | 建議做法 | 常見錯誤 |
|---|---|---|
| Path | 逐段編碼,保留路由分隔結構 | 把整段 /a/b 一次編碼成不可路由格式 |
| Query | 參數鍵與值分開編碼 | 未編碼 &、= 導致參數拆裂 |
| Fragment | 依前端路由需求處理 | 與後端查詢參數混在一起編解碼 |
七、安全觀點:編碼不是防禦本身
URL 編碼只能確保傳輸格式正確,不等於防止 XSS 或 SQL Injection。它是「資料表示層」的處理,不是「輸入驗證」或「輸出逃脫」的替代品。
- 接收端仍需做白名單驗證與長度限制。
- 輸出到 HTML、SQL、Shell 時要用該情境的專用 escaping。
- 不要把解碼後內容直接插入 DOM。
結語
URL 編碼的關鍵不在記憶所有字元表,而在建立一個可預測的協作流程。當你把「區段分離、單一責任、規格對齊」做好,編碼問題會從難以追查的 bug,變成可快速定位的工程細節。