土薯工具 Toolshu.com 登錄 用戶注冊

URL 裏的 %20 是什麼?一文搞懂 URL 編碼的原理與踩坑

原創 作者:bhnw 於 2026-04-07 15:40 發佈 4次瀏覽 收藏 (0)

從一個真實的 bug 說起

某天,後端同事反映接口收到的參數值不對:前端傳了一個包含&符號的字符串作爲查詢參數,但後端解析出來的值被截斷了。

原因很簡單:& 在 URL 裏是參數分隔符,沒經過編碼就直接拼進 URL,後端自然會把它當成參數邊界解析。

這就是 URL 編碼存在的意義——URL 裏有一批字符有特殊含義,如果數據本身包含這些字符,必須先編碼,才能安全傳輸。


URL 編碼的本質:百分號編碼

URL 編碼的正式名稱是百分號編碼(Percent-Encoding),規則很簡單:

把字符轉成 UTF-8 字節,每個字節用 % 加兩位十六進制數表示。

以空格爲例:

空格 → UTF-8字節: 0x20 → URL編碼: %20

以中文"中"爲例:

中 → UTF-8字節: 0xE4 0xB8 0xAD → URL編碼: %E4%B8%AD

所以 URL 裏看到的 %20 就是空格,%E4%B8%AD%E6%96%87 就是"中文"。

哪些字符需要編碼

RFC 3986 定義了 URL 中的"保留字符"和"非保留字符":

  • 非保留字符(不需要編碼):A-Z a-z 0-9 - _ . ~
  • 保留字符(有特殊含義,作爲數據時需要編碼):: / ? # [ ] @ ! $ & ' ( ) * + , ; =
  • 其他字符(中文、日文、空格、特殊符號等):必須編碼

encodeURI 和 encodeURIComponent:傻傻分不清

JavaScript 提供了兩個函數,很多人搞不清該用哪個。區分方法只有一條:你編碼的是整個 URL,還是 URL 裏的某個參數值?

encodeURI:編碼整個 URL

encodeURI 會保留 URL 結構字符不編碼(因爲它們是 URL 的組成部分),只編碼真正不合法的字符。

encodeURI("https://example.com/search?q=hello world&lang=zh")
// → "https://example.com/search?q=hello%20world&lang=zh"
// 注意:? & = 都沒有被編碼,因爲它們是 URL 結構的一部分

不會編碼的字符: A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) #

encodeURIComponent:編碼參數值

encodeURIComponent 會對幾乎所有特殊字符編碼,包括 ? & = # / 等,適合對單個參數值編碼。

const keyword = "hello world & more"
const url = "https://example.com/search?q=" + encodeURIComponent(keyword)
// → "https://example.com/search?q=hello%20world%20%26%20more"
// & 被編碼成了 %26,不會干擾參數解析

不會編碼的字符: A-Z a-z 0-9 - _ . ! ~ * ' ( )

對比一眼看懂

const str = "a=1&b=2/c?d=中文"

encodeURI(str)
// → "a=1&b=2/c?d=%E4%B8%AD%E6%96%87"
// = & / ? 都保留了

encodeURIComponent(str)
// → "a%3D1%26b%3D2%2Fc%3Fd%3D%E4%B8%AD%E6%96%87"
// = & / ? 全部編碼了

結論:拼接參數值時用 encodeURIComponent,幾乎不會用錯。


開發中最常見的幾個坑

坑1:空格編碼成了 + 而不是 %20

HTML 表單的 application/x-www-form-urlencoded 格式會把空格編碼成 +,而不是 %20。這是歷史遺留規範,和標準 URL 編碼不同。

表單提交: hello world → hello+world
標準URL編碼: hello world → hello%20world

後端解析時要注意區分。PHP 的 urldecode() 會把 + 還原爲空格,但 rawurldecode() 不會。

坑2:對同一個值編碼了兩次

// 第一次編碼
const encoded = encodeURIComponent("hello world")
// → "hello%20world"

// 又編碼了一次!
const doubleEncoded = encodeURIComponent(encoded)
// → "hello%2520world"  (%25 是 % 的編碼)

後端收到 %2520,解碼一次得到 %20,再解碼才能得到空格。這種 double encoding 是接口聯調時的常見問題。

坑3:encodeURI 無法編碼 #

# 在 URL 裏表示錨點,encodeURI 不會對它編碼。如果參數值裏包含 #,必須用 encodeURIComponent

encodeURI("https://example.com?tag=#title")
// → "https://example.com?tag=#title"  # 沒有被編碼!

"https://example.com?tag=" + encodeURIComponent("#title")
// → "https://example.com?tag=%23title"  正確

坑4:後端語言的解碼函數不統一

不同語言的 URL 解碼函數行爲略有差異:

# Python
from urllib.parse import unquote, unquote_plus
unquote("hello%20world")      # → "hello world"  (%20 → 空格)
unquote_plus("hello+world")   # → "hello world"  (+ → 空格)
// Java
URLDecoder.decode("hello+world", "UTF-8")   // → "hello world"(+ 會被解碼爲空格)
URI.create("hello%20world").getPath()        // → "hello world"(標準解碼)
urldecode("hello+world")     // → "hello world"
rawurldecode("hello+world")  // → "hello+world"(+ 不解碼)
rawurldecode("hello%20world") // → "hello world"

前後端約定好用哪種編碼方式,解碼函數要對應匹配。


中文 URL 的處理

瀏覽器地址欄輸入中文網址時,瀏覽器會自動編碼再發出請求。比如訪問:

https://example.com/搜索?關鍵詞=中文

實際發出的請求是:

https://example.com/%E6%90%9C%E7%B4%A2?%E5%85%B3%E9%94%AE%E8%AF%8D=%E4%B8%AD%E6%96%87

SEO 角度:中文 URL 對搜索引擎是友好的(百度尤其如此),但服務器日誌和代碼裏看到的是編碼後的版本,調試時注意解碼再看。


快速驗證

遇到一串看不懂的 URL 編碼字符,或者需要把中文/特殊字符編碼成 URL 安全的格式,可以直接用 toolshu.com 的 URL 編碼解碼工具。支持 encodeURI 和 encodeURIComponent 兩種模式,粘貼即用。

发现周边 发现周边
評論區

加載中...