URL 編碼 bug 通常出現在參數裡包含 &、=、?、#、空格、中文、Emoji,或者另一個完整 URL 的時候。JavaScript 裡最常見的錯誤,是應該用 encodeURIComponent() 時用了 encodeURI()。
簡單規則:
- 編碼完整 URL:用
encodeURI() - 編碼參數值或路徑片段:用
encodeURIComponent()
為什麼差別很重要
encodeURI() 會保留完整 URL 中有結構意義的字元,例如:
: / ? # & =
這適合你已經有一個完整 URL,只想編碼空格或非 ASCII 字元的情況。
encodeURIComponent() 會編碼更多字元,因為它處理的是 URL 的某一個組成部分,比如 query 參數值。
如果參數值裡有 &、=、#,卻用了 encodeURI(),query string 就可能被拆壞。
Query 參數範例
假設你要傳:
const search = 'a=b & c=d'
錯誤:
const url = '/search?q=' + encodeURI(search)
裡面的 & 可能被當成新的 query 參數分隔符。
正確:
const url = '/search?q=' + encodeURIComponent(search)
這樣整個值都會留在 q 參數裡。
URL 裡再巢狀 URL
登入跳轉、OAuth 回呼、分享連結都容易遇到這個問題。
const next = 'https://example.com/a?x=1&y=2'
const loginUrl = '/login?next=' + encodeURIComponent(next)
如果用 encodeURI(next),裡面的 &y=2 可能變成外層 /login 的參數,而不是留在 next 裡。
路徑片段範例
路徑片段也應該用 encodeURIComponent():
const username = 'Ada Lovelace/notes'
const url = '/users/' + encodeURIComponent(username)
這裡的 / 是使用者名稱的一部分,不是路徑分隔符,所以必須編碼。
如果用 encodeURI(),斜線會保留,路由結構就變了。
優先使用 URL 和 URLSearchParams
建構 query string 時,現代 JavaScript 有更安全的 API:
const url = new URL('https://example.com/search')
url.searchParams.set('q', 'a=b & c=d')
url.searchParams.set('lang', 'zh-TW')
console.log(url.toString())
URLSearchParams 會幫你處理編碼和分隔符,也能減少重複編碼的風險。
重複編碼
重複編碼就是把已經編碼過的值又編碼一次。
hello world
hello%20world
hello%2520world
%25 是 % 的編碼形式。如果你看到 %2520,通常說明原本的 %20 又被編碼了一遍。
編碼或解碼前,先判斷你正在處理哪一層:
- 使用者原始輸入
- query 參數值
- 完整 URL
- JSON payload 裡的 URL
- 另一個 URL 裡的 redirect 參數
空格:%20 還是 +
URL 裡空格常見寫法是 %20。表單編碼的 query string 中,空格也可能表示為 +。
除非你明確知道目標格式,否則不要手動替換空格。JavaScript 裡產生 query string 時,通常用 URLSearchParams 更穩。
排錯清單
URL 參數出錯時:
- 先判斷你編碼的是完整 URL,還是某個組件。
- query 參數值用
encodeURIComponent()。 - 建構 query string 優先用
URLSearchParams。 - 檢查
%252F、%2520這類重複編碼。 - 一次只解一層。
- 特別注意巢狀 redirect URL。
- 用包含
&、=、?、#、空格、中文、日文、Emoji 的值測試。
相關工具
用 URL 編碼解碼工具 檢查單一值,用 URL 參數解析工具 查看完整 query string 如何被拆分。如果參數裡包含 JSON,先解 URL 這一層,再用 JSON 格式化工具 校驗。