基礎教學

URL 編碼完整指南:百分比編碼、RFC 3986 與保留字元

2026-03-13 閱讀時間約 8 分鐘 URL 編碼、RFC 3986、百分比編碼

什麼是 URL 編碼(百分比編碼)?

URL 編碼(URL Encoding),又稱百分比編碼(Percent-Encoding),是一種將字元轉換為 URL 安全格式的機制。由 IETF 在 RFC 3986 標準中定義,目的是讓所有字元都能在 URL 中安全傳輸,而不產生解析歧義。

URL 中只允許使用 ASCII 字元的一個子集。對於其他字元(如中文、日文、特殊符號),必須先將字元轉換為位元組序列,再以 %XX 的格式表示每個位元組,其中 XX 是兩位十六進位數字。

例如:中文「你好」的 UTF-8 位元組為 E4 BD A0 E5 A5 BD,URL 編碼結果為 %E4%BD%A0%E5%A5%BD

RFC 3986 標準概述

RFC 3986(Uniform Resource Identifier: Generic Syntax)是 URL/URI 的核心標準文件,於 2005 年由 IETF 發布,至今仍是業界最權威的規範。它定義了 URI 的語法、保留字元的分類,以及何時需要進行百分比編碼。

根據 RFC 3986,URI 字元分為三類:

保留字元完整列表

RFC 3986 將保留字元分為兩組:

一般分隔符(gen-delims)

字元用途URL 編碼
:協議與主機分隔、port 分隔%3A
/路徑分隔符%2F
?query string 起始%3F
#fragment 起始%23
[IPv6 位址%5B
]IPv6 位址%5D
@使用者資訊分隔%40

子分隔符(sub-delims)

字元常見用途URL 編碼
!特殊標記%21
$路徑參數%24
&query 參數分隔%26
'字串標記%27
(群組標記%28
)群組標記%29
*萬用字元%2A
+空格(舊式)%2B
,列表分隔%2C
;路徑參數%3B
=key=value 分隔%3D

不安全字元

字元原因URL 編碼
空格URL 不允許空格%20 或 +
"HTML 屬性引號衝突%22
<HTML 標籤衝突%3C
>HTML 標籤衝突%3E
{ }不安全字元%7B / %7D
|不安全字元%7C
\路徑衝突%5C
^不安全字元%5E
`不安全字元%60

中文字元的 URL 編碼原理

中文等非 ASCII 字元的 URL 編碼分為兩個步驟:

  1. UTF-8 編碼:將字元轉換為 UTF-8 位元組序列(每個中文字通常佔 3 個位元組)。
  2. 百分比表示:每個位元組以 %XX 格式表示。

「中」= UTF-8: E4 B8 AD → URL 編碼: %E4%B8%AD
「文」= UTF-8: E6 96 87 → URL 編碼: %E6%96%87

各程式語言實作範例

JavaScript

// encodeURIComponent - 最嚴格,連 URL 結構字元也編碼(推薦用於參數值)
encodeURIComponent('你好 world!')
// → "%E4%BD%A0%E5%A5%BD%20world!"

// encodeURI - 保留 URL 結構字元(用於完整 URL)
encodeURI('https://example.com/搜尋?q=你好')
// → "https://example.com/%E6%90%9C%E5%B0%8B?q=%E4%BD%A0%E5%A5%BD"

// 解碼
decodeURIComponent('%E4%BD%A0%E5%A5%BD')
// → "你好"

Python

from urllib.parse import quote, unquote, urlencode

# 編碼單一值(safe='' 代表連 / 也要編碼)
quote('你好 world!', safe='')
# → '%E4%BD%A0%E5%A5%BD%20world%21'

# 解碼
unquote('%E4%BD%A0%E5%A5%BD')
# → '你好'

# 編碼整個 query string
params = {'name': '張三', 'city': '台北市'}
urlencode(params)
# → 'name=%E5%BC%B5%E4%B8%89&city=%E5%8F%B0%E5%8C%97%E5%B8%82'

PHP

<?php
// rawurlencode - 遵循 RFC 3986(推薦)
rawurlencode('你好 world!');
// → '%E4%BD%A0%E5%A5%BD%20world%21'

// urlencode - 將空格編碼為 + 而非 %20(用於 HTML form 資料)
urlencode('你好 world!');
// → '%E4%BD%A0%E5%A5%BD+world%21'

// 解碼
rawurldecode('%E4%BD%A0%E5%A5%BD');
// → '你好'
?>

Java

import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

// 編碼
String encoded = URLEncoder.encode("你好 world!", StandardCharsets.UTF_8);
// → "%E4%BD%A0%E5%A5%BD+world%21"(注意:Java 將空格編碼為 +)

// 解碼
String decoded = URLDecoder.decode("%E4%BD%A0%E5%A5%BD", StandardCharsets.UTF_8);
// → "你好"

Go

import "net/url"

// 編碼
encoded := url.QueryEscape("你好 world!")
// → "%E4%BD%A0%E5%A5%BD+world%21"

// 路徑編碼(空格編碼為 %20)
pathEncoded := url.PathEscape("你好 world!")
// → "%E4%BD%A0%E5%A5%BD%20world%21"

// 解碼
decoded, _ := url.QueryUnescape("%E4%BD%A0%E5%A5%BD")
// → "你好"

常見錯誤與正確做法

錯誤 1:雙重編碼
將已編碼的字串再次編碼,導致 %25 出現(%25 是 % 的編碼)。
錯誤:encodeURIComponent('%E4%BD%A0')%25E4%25BD%25A0
正確:只對原始未編碼的字串執行一次編碼。

錯誤 2:使用過時的 escape() 函式
escape() 不會正確處理非 ASCII 字元(如中文),且已被標準廢棄。
請改用 encodeURIComponent()encodeURI()

錯誤 3:搞混 encodeURI 和 encodeURIComponent
在 query string 中使用 encodeURI() 會導致 &、=、? 等分隔符未被編碼,造成參數解析錯誤。
query string 的參數值請務必使用 encodeURIComponent()

立即試用 URL 編碼工具

使用我們的免費工具即時驗證您的 URL 編碼結果,支援中文與多語言。

前往工具頁面