URLエンコーディング(パーセントエンコーディングとも呼ばれる)は、Web開発における最も基本的なメカニズムの一つです。フォームの送信、クエリパラメータ付きリンクのクリック、REST APIの呼び出しのたびに、URLエンコードとURLデコード操作が行われています。この包括的なガイドでは、RFC 3986仕様からJavaScript、Python、Bash、PHP、Javaの実践的なコード例まで、すべてをカバーします。
無料オンラインURLエンコード/デコードツールをお試しください。
URLエンコーディング(パーセントエンコーディング)とは?
URLエンコーディングはRFC 3986で定義されており、安全でない文字をパーセント記号(%)と2桁の16進数に置き換えます。例えば、スペースは%20になります。
URLには限られたASCII文字セットしか含められません。エンコード不要なURL安全文字は:英字、数字、4つの特殊文字-、_、.、~です。その他すべての文字はエンコードが必要です。
&、=、?、#などの文字はURLで構造的な意味を持ちます。適切なURLエンコードがなければ、URLは破損したり誤って解釈されたりします。
URLエンコーディングの仕組み
URLエンコードプロセスはシンプルなアルゴリズムに従います:
- 文字をUTF-8バイトに変換。
- 各バイトに対して、
%の後に16進数値を記述。 - 非予約文字はそのまま。
- 予約文字は曖昧な場合のみエンコード。
例:Hello World!はHello%20World%21になります。
UTF-8マルチバイト例:非ASCII文字は複数のパーセントエンコードされたバイトを生成します。
URLエンコーディング参照テーブル
よくエンコードされる文字の参照テーブル:
| Character | Encoded | Description |
|---|---|---|
(space) | %20 | Space |
! | %21 | Exclamation mark |
# | %23 | Hash / Fragment |
$ | %24 | Dollar sign |
% | %25 | Percent sign |
& | %26 | Ampersand |
' | %27 | Apostrophe |
( | %28 | Left parenthesis |
) | %29 | Right parenthesis |
* | %2A | Asterisk |
+ | %2B | Plus sign |
, | %2C | Comma |
/ | %2F | Forward slash |
: | %3A | Colon |
; | %3B | Semicolon |
= | %3D | Equals sign |
? | %3F | Question mark |
@ | %40 | At sign |
[ | %5B | Left bracket |
] | %5D | Right bracket |
{ | %7B | Left brace |
} | %7D | Right brace |
| | %7C | Pipe |
< | %3C | Less than |
> | %3E | Greater than |
" | %22 | Double quote |
スペースのエンコーディング:%20 vs +
URLでスペースをエンコードする方法は2つあります:
%20:URIのRFC 3986標準エンコーディング。
+:application/x-www-form-urlencoded(HTMLフォーム)で使用。
どちらを使う?プログラムでURLを構築する場合は%20を使用。+はフォームデータで自動的に使用されます。
// Space encoding comparison
// RFC 3986 (URI standard) — space = %20
"hello world" → "hello%20world"
// Used in: URL paths, API endpoints, general URIs
// application/x-www-form-urlencoded — space = +
"hello world" → "hello+world"
// Used in: HTML form submissions, URLSearchParams
// JavaScript comparison:
encodeURIComponent("hello world") // "hello%20world" (%20)
new URLSearchParams({q: "hello world"}).toString() // "q=hello+world" (+)
// Python comparison:
urllib.parse.quote("hello world") # "hello%20world" (%20)
urllib.parse.urlencode({"q": "hello world"}) # "q=hello+world" (+)
// PHP comparison:
rawurlencode("hello world") // "hello%20world" (%20)
urlencode("hello world") // "hello+world" (+)URLエンコード/デコードのコード例
JavaScript URLエンコード/デコード
JavaScriptはencodeURIComponentとencodeURIを提供しています。それぞれの使い分けの理解が重要です:
// ===== encodeURIComponent vs encodeURI =====
// encodeURIComponent — encodes EVERYTHING except: A-Z a-z 0-9 - _ . ~
// Use for: individual query parameter values, path segments
encodeURIComponent("hello world & goodbye");
// "hello%20world%20%26%20goodbye"
encodeURIComponent("price=100&category=books");
// "price%3D100%26category%3Dbooks"
// encodeURI — preserves URL structure characters: : / ? # & = @ + $
// Use for: encoding a complete URL
encodeURI("https://example.com/search?q=hello world&lang=en");
// "https://example.com/search?q=hello%20world&lang=en"
// WARNING: Do NOT use encodeURIComponent on full URLs!
encodeURIComponent("https://example.com/path");
// "https%3A%2F%2Fexample.com%2Fpath" ← BROKEN URL!
// ===== Decoding =====
decodeURIComponent("hello%20world%20%26%20goodbye");
// "hello world & goodbye"
decodeURI("https://example.com/search?q=hello%20world");
// "https://example.com/search?q=hello world"
// ===== URLSearchParams (handles form encoding automatically) =====
const params = new URLSearchParams({
query: "cats & dogs",
page: "1",
filter: "price > 50"
});
params.toString();
// "query=cats+%26+dogs&page=1&filter=price+%3E+50"
// Note: spaces become + (form encoding), & in values becomes %26
// Parse query string
const parsed = new URLSearchParams("?q=hello+world&lang=en");
parsed.get("q"); // "hello world" (+ decoded to space)
parsed.get("lang"); // "en"
// ===== Building URLs with the URL API =====
const url = new URL("https://api.example.com/search");
url.searchParams.set("q", "hello world & more");
url.searchParams.set("limit", "10");
url.toString();
// "https://api.example.com/search?q=hello+world+%26+more&limit=10"Python URLエンコード/デコード
Pythonのurllib.parseモジュールは包括的なURLエンコード関数を提供します:
import urllib.parse
# ===== quote / unquote (RFC 3986 percent-encoding) =====
# Encode a string (spaces become %20)
urllib.parse.quote("hello world & goodbye")
# "hello%20world%20%26%20goodbye"
# By default, / is NOT encoded (safe="/")
urllib.parse.quote("/path/to/file name.txt")
# "/path/to/file%20name.txt"
# Encode everything including /
urllib.parse.quote("/path/to/file", safe="")
# "%2Fpath%2Fto%2Ffile"
# Decode
urllib.parse.unquote("hello%20world%20%26%20goodbye")
# "hello world & goodbye"
# ===== urlencode (for query strings, uses + for spaces) =====
params = {"q": "cats & dogs", "page": 1, "filter": "price > 50"}
urllib.parse.urlencode(params)
# "q=cats+%26+dogs&page=1&filter=price+%3E+50"
# ===== quote_plus (form encoding, spaces become +) =====
urllib.parse.quote_plus("hello world")
# "hello+world"
urllib.parse.unquote_plus("hello+world")
# "hello world"
# ===== With the requests library =====
import requests
# requests handles URL encoding automatically
response = requests.get(
"https://api.example.com/search",
params={"q": "hello world", "lang": "en"}
)
# Actual URL: https://api.example.com/search?q=hello+world&lang=en
# For path segments, encode manually
username = "john@example.com"
url = f"https://api.example.com/users/{urllib.parse.quote(username, safe='')}"
# "https://api.example.com/users/john%40example.com"Bash / curl URLエンコード
curlの--data-urlencodeフラグが最も簡単な方法です:
# ===== curl --data-urlencode =====
# GET request with URL-encoded query parameter
curl -G "https://api.example.com/search" \
--data-urlencode "q=hello world & more" \
--data-urlencode "lang=en"
# Request URL: /search?q=hello%20world%20%26%20more&lang=en
# POST with form-encoded body
curl -X POST "https://api.example.com/submit" \
--data-urlencode "name=John Doe" \
--data-urlencode "message=Hello! How are you?"
# ===== Pure Bash URL encoding (using printf) =====
urlencode() {
local string="${1}"
local strlen=${#string}
local encoded=""
local pos c o
for (( pos=0 ; pos<strlen ; pos++ )); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9]) o="${c}" ;;
*) printf -v o '%%%02X' "'$c" ;;
esac
encoded+="${o}"
done
echo "${encoded}"
}
urlencode "hello world & goodbye"
# "hello%20world%20%26%20goodbye"
# ===== Using Python one-liner in Bash =====
python3 -c "import urllib.parse; print(urllib.parse.quote('hello world'))"
# "hello%20world"
# ===== Decode URL-encoded strings =====
python3 -c "import urllib.parse; print(urllib.parse.unquote('hello%20world'))"
# "hello world"PHP URLエンコード/デコード
PHPはurlencode()とrawurlencode()を提供しています:
<?php
// ===== urlencode / urldecode =====
// Uses + for spaces (application/x-www-form-urlencoded)
echo urlencode("hello world & goodbye");
// "hello+world+%26+goodbye"
echo urldecode("hello+world+%26+goodbye");
// "hello world & goodbye"
// ===== rawurlencode / rawurldecode =====
// Uses %20 for spaces (RFC 3986)
echo rawurlencode("hello world & goodbye");
// "hello%20world%20%26%20goodbye"
echo rawurldecode("hello%20world%20%26%20goodbye");
// "hello world & goodbye"
// ===== When to use which? =====
// urlencode() → for query string values (form-style)
// rawurlencode() → for URL path segments (RFC 3986)
// Building a URL with encoded path and query
$path = rawurlencode("my file.pdf");
$query = urlencode("search term & more");
$url = "https://example.com/files/{$path}?q={$query}";
// "https://example.com/files/my%20file.pdf?q=search+term+%26+more"
// ===== http_build_query (encode arrays as query strings) =====
$params = [
"q" => "cats & dogs",
"page" => 1,
"tags" => ["php", "url encoding"]
];
echo http_build_query($params);
// "q=cats+%26+dogs&page=1&tags%5B0%5D=php&tags%5B1%5D=url+encoding"
?>Java URLエンコード/デコード
JavaのURLEncoderとURLDecoderクラスがURLエンコードを処理します:
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
// ===== URLEncoder.encode (uses + for spaces) =====
// Always specify UTF-8 charset!
String encoded = URLEncoder.encode(
"hello world & goodbye", StandardCharsets.UTF_8
);
// "hello+world+%26+goodbye"
// Decode
String decoded = URLDecoder.decode(
"hello+world+%26+goodbye", StandardCharsets.UTF_8
);
// "hello world & goodbye"
// ===== Convert + to %20 for RFC 3986 compliance =====
String rfc3986 = URLEncoder.encode("hello world", StandardCharsets.UTF_8)
.replace("+", "%20");
// "hello%20world"
// ===== Java 11+ URI encoding =====
import java.net.URI;
URI uri = URI.create("https://example.com/search");
// For building URLs with encoded parameters:
String query = "q=" + URLEncoder.encode("hello world", StandardCharsets.UTF_8);
URI fullUri = URI.create("https://example.com/search?" + query);
// https://example.com/search?q=hello+world
// ===== Encoding path segments =====
String pathSegment = URLEncoder.encode("my file.pdf", StandardCharsets.UTF_8)
.replace("+", "%20");
String url = "https://example.com/files/" + pathSegment;
// "https://example.com/files/my%20file.pdf"一般的なURLエンコーディングの間違い
避けるべき最も一般的な落とし穴:
二重エンコーディング:既にエンコードされた文字列を再度エンコード。%20が%2520になります。
// Double encoding example — a common bug!
const value = "hello world";
// Correct: encode once
const correct = encodeURIComponent(value);
// "hello%20world" ✓
// Bug: encode twice
const doubleEncoded = encodeURIComponent(encodeURIComponent(value));
// "hello%2520world" ✗ (%25 is the encoded form of %)
// How to detect double encoding:
// If you see %25 in your URLs, you likely have a double encoding issue
// %2520 = double-encoded space (%25 = %, 20 = space)
// %253A = double-encoded colon (%25 = %, 3A = :)URL全体をエンコード:完全なURLにencodeURIComponent()を使用するとURL構造が壊れます。
// Encoding the entire URL — WRONG!
const url = "https://example.com/path?q=hello world";
// WRONG: breaks URL structure
encodeURIComponent(url);
// "https%3A%2F%2Fexample.com%2Fpath%3Fq%3Dhello%20world"
// CORRECT option 1: use encodeURI for full URLs
encodeURI(url);
// "https://example.com/path?q=hello%20world"
// CORRECT option 2: encode only the value
const base = "https://example.com/path";
const query = encodeURIComponent("hello world");
const fullUrl = base + "?q=" + query;
// "https://example.com/path?q=hello%20world"UTF-8の忘れ:URLエンコーディングには常にUTF-8を使用してください。
パスセグメントのエンコード漏れ:スペースを含むパスもエンコードが必要です。
+記号の誤処理:+はフォームではスペースを意味しますが、通常のURLではリテラルです。
APIでのURLエンコーディング
APIでの正しいURLエンコーディングは重要です:
クエリパラメータ:各パラメータ名と値を個別にエンコードする必要があります。
パスパラメータ:URLパスに埋め込まれた値もエンコードが必要です。
フォームデータ:application/x-www-form-urlencodedはスペースに+を使用します。
JSONペイロード:リクエストボディのJSONコンテンツはURLエンコード不要です。
// API URL encoding examples
// Query parameters — encode each value individually
const searchTerm = 'price > 100 & category = "electronics"';
const apiUrl = `https://api.example.com/search?q=${encodeURIComponent(searchTerm)}&limit=10`;
// "https://api.example.com/search?q=price%20%3E%20100%20%26%20...&limit=10"
// Path parameters — encode values embedded in paths
const username = "john@example.com";
const profileUrl = `https://api.example.com/users/${encodeURIComponent(username)}/profile`;
// "https://api.example.com/users/john%40example.com/profile"
// Form data (POST) — uses + for spaces
const formBody = new URLSearchParams({
username: "john doe",
password: "p@ss w0rd!"
}).toString();
// "username=john+doe&password=p%40ss+w0rd%21"
// JSON as query parameter — encode the entire JSON string
const filters = JSON.stringify({ price: { min: 10, max: 100 } });
const url = `/api/products?filters=${encodeURIComponent(filters)}`;
// "/api/products?filters=%7B%22price%22%3A%7B%22min%22%3A10%2C%22max%22%3A100%7D%7D"よくある質問
URLエンコーディングとは何ですか?なぜ必要ですか?
URLエンコーディングは、安全でない文字をパーセント-16進数シーケンスに置き換えてURLで安全に送信するためのプロセスです。&、=、?、#などの特殊文字は構造的な意味を持ち、データとして使用する場合はエンコードが必要です。
encodeURIとencodeURIComponentの違いは?
encodeURI()はURL構造文字(:、/、?、#)を保持します。encodeURIComponent()は非予約文字以外のすべてをエンコードします。個別のパラメータ値にはencodeURIComponentを使用してください。
スペースには%20と+のどちらを使うべき?
URL(RFC 3986)には%20を使用。application/x-www-form-urlencodedにのみ+を使用。迷った場合は%20がより安全な選択です。
URLエンコーディングはすべての開発者が日常的に遭遇する基本的なWebスキルです。無料オンラインツールで即座にエンコードとデコードを行えます。