URLエンコードとは何か
URLエンコード(パーセントエンコーディング)とは、URLに含められない文字を %XX の形式に変換する仕組みです。XX の部分にはその文字のバイト値が16進数で入ります。
たとえば、日本語の「東京」をURLに含めたい場合、次のように変換されます。
元の文字: 東京
URLエンコード後: %E6%9D%B1%E4%BA%AC
ブラウザのアドレスバーでは日本語がそのまま表示されることもありますが、実際にサーバーに送信されるHTTPリクエストでは、上記のようにエンコードされた文字列が使われています。
なぜURLエンコードが必要なのか
URLには「使える文字」と「使えない文字」があります。RFC 3986で定義されたURL構文では、安全に使えるのは以下の文字だけです。
URLにそのまま使える文字(非予約文字)
英字: A-Z a-z
数字: 0-9
記号: - _ . ~
URLで特別な意味を持つ文字(予約文字)
: / ? # [ ] @ ! $ & ' ( ) * + , ; =
これらの予約文字は、URLの構造を区切るために使われます。たとえば ? はクエリパラメータの開始、& はパラメータの区切り、# はフラグメントの開始を意味します。
エンコードが必要な場面
予約文字を「区切り」ではなく「データの一部」として使いたいときや、日本語のようなASCII範囲外の文字をURLに含めたいとき、URLエンコードが必要になります。
# 検索クエリに「C++」を含める場合
# + はそのまま使うとスペースと解釈される
https://example.com/search?q=C%2B%2B
# 日本語の検索キーワード
https://example.com/search?q=%E6%9D%B1%E4%BA%AC
もしエンコードしなければ、URLの構文が壊れたり、サーバー側で意図しない解釈がされたりします。
パーセントエンコーディングの仕組み
URLエンコードの変換プロセスを、ステップごとに見てみましょう。
ステップ1: UTF-8でバイト列に変換
まず、エンコードしたい文字をUTF-8のバイト列に変換します。
「あ」のUTF-8バイト列:
あ → 0xE3 0x81 0x82(3バイト)
ステップ2: 各バイトを %XX 形式に変換
各バイトの値を16進数で表し、先頭に % を付けます。
0xE3 → %E3
0x81 → %81
0x82 → %82
結果: 「あ」 → %E3%81%82
日本語文字のエンコード例
| 文字 | UTF-8バイト列 | URLエンコード |
|---|---|---|
| あ | E3 81 82 | %E3%81%82 |
| い | E3 81 84 | %E3%81%84 |
| 東 | E6 9D B1 | %E6%9D%B1 |
| 京 | E4 BA AC | %E4%BA%AC |
| スペース | 20 | %20(または +) |
| & | 26 | %26 |
スペースの特殊な扱い
スペース(半角空白)のエンコードには2つの方式があります。
| 方式 | 変換結果 | 使われる場面 |
|---|---|---|
| パーセントエンコーディング | %20 | URL全般(パス部分) |
| フォームエンコーディング | + | HTMLフォームのクエリ文字列 |
# パス部分では %20
https://example.com/my%20document.pdf
# クエリ文字列では + も使われる
https://example.com/search?q=hello+world
多くのWebサーバーは両方を正しく処理しますが、厳密にはRFC 3986では %20 が正式な表記です。HTMLフォームの application/x-www-form-urlencoded 形式では + が使われる慣習があります。
日本語とURLエンコード
なぜ日本語URLが増えたのか
かつてはURLに日本語を使うことはほとんどありませんでした。しかし、IRIの標準化やブラウザの対応により、日本語ドメインや日本語パスが普及しました。
# ブラウザのアドレスバーに表示される形
https://ja.wikipedia.org/wiki/東京都
# 実際にサーバーに送信される形
https://ja.wikipedia.org/wiki/%E6%9D%B1%E4%BA%AC%E9%83%BD
ブラウザは表示上は日本語で見せていますが、内部的にはURLエンコードされた文字列で通信しています。
文字コードの問題
URLエンコードでは、どの文字コードでバイト列に変換するかが重要です。現在はUTF-8が標準ですが、古いシステムではShift_JISやEUC-JPでエンコードされることがあります。
# 「東京」をUTF-8でエンコード
%E6%9D%B1%E4%BA%AC
# 「東京」をShift_JISでエンコード
%93%8C%8B%9E
同じ日本語でも文字コードが違えばまったく異なるエンコード結果になります。エンコードとデコードで文字コードが一致しないと文字化けの原因になります。
現代のWebではUTF-8を使うのが標準であり、特別な理由がない限りUTF-8でエンコードしてください。
エンコード・デコードの実装方法
JavaScriptの場合
JavaScriptには3つのエンコード関数があります。用途に応じて使い分けが必要です。
const text = '東京 タワー&スカイツリー';
// encodeURI — URL全体をエンコード(予約文字はそのまま)
console.log(encodeURI(text));
// → %E6%9D%B1%E4%BA%AC%20%E3%82%BF%E3%83%AF%E3%83%BC&%E3%82%B9%E3%82%AB%E3%82%A4%E3%83%84%E3%83%AA%E3%83%BC
// ※ & はエンコードされない
// encodeURIComponent — URLの一部をエンコード(予約文字もエンコード)
console.log(encodeURIComponent(text));
// → %E6%9D%B1%E4%BA%AC%20%E3%82%BF%E3%83%AF%E3%83%BC%26%E3%82%B9%E3%82%AB%E3%82%A4%E3%83%84%E3%83%AA%E3%83%BC
// ※ & も %26 にエンコードされる
// デコード
console.log(decodeURIComponent('%E6%9D%B1%E4%BA%AC'));
// → 東京
使い分けの原則:
| 関数 | 用途 | 例 |
|---|---|---|
encodeURI() | URL全体をエンコードしたいとき | encodeURI('https://example.com/東京') |
encodeURIComponent() | URLの一部(クエリパラメータ等)をエンコードしたいとき | ?q= + encodeURIComponent('東京&大阪') |
注意: encodeURIComponent() は :, /, ?, # などもエンコードするため、URL全体に使うとURLが壊れます。クエリパラメータの値部分にだけ使ってください。
// 正しい使い方
const query = encodeURIComponent('東京&大阪');
const url = `https://example.com/search?q=${query}`;
// → https://example.com/search?q=%E6%9D%B1%E4%BA%AC%26%E5%A4%A7%E9%98%AA
// 間違った使い方(URL全体にencodeURIComponentを使う)
const broken = encodeURIComponent('https://example.com/search?q=東京');
// → https%3A%2F%2Fexample.com%2Fsearch%3Fq%3D%E6%9D%B1%E4%BA%AC(URLとして機能しない)
Pythonの場合
from urllib.parse import quote, unquote, urlencode
# 文字列をURLエンコード
encoded = quote('東京タワー')
print(encoded) # %E6%9D%B1%E4%BA%AC%E3%82%BF%E3%83%AF%E3%83%BC
# デコード
decoded = unquote('%E6%9D%B1%E4%BA%AC')
print(decoded) # 東京
# クエリパラメータをまとめてエンコード
params = urlencode({'city': '東京', 'area': '渋谷区'})
print(params) # city=%E6%9D%B1%E4%BA%AC&area=%E6%B8%8B%E8%B0%B7%E5%8C%BA
PHPの場合
// エンコード(スペースを %20 に変換)
echo rawurlencode('東京 タワー');
// → %E6%9D%B1%E4%BA%AC%20%E3%82%BF%E3%83%AF%E3%83%BC
// エンコード(スペースを + に変換 — フォーム形式)
echo urlencode('東京 タワー');
// → %E6%9D%B1%E4%BA%AC+%E3%82%BF%E3%83%AF%E3%83%BC
// デコード
echo urldecode('%E6%9D%B1%E4%BA%AC');
// → 東京
コマンドライン(curl, Python)
# curlでURLエンコード付きリクエスト(--data-urlencodeを使用)
curl -G "https://example.com/search" --data-urlencode "q=東京タワー"
# Pythonのワンライナーでエンコード
python3 -c "from urllib.parse import quote; print(quote('東京タワー'))"
# Pythonのワンライナーでデコード
python3 -c "from urllib.parse import unquote; print(unquote('%E6%9D%B1%E4%BA%AC'))"
オンラインツールで変換
コードを書かずに手軽にURLエンコード・デコードしたい場合は、URLエンコード・デコードツール が便利です。テキストを貼り付けるだけで即座に変換でき、UTF-8はもちろんShift_JISなど複数の文字コードにも対応しています。
URLエンコードが関わる具体的な場面
Google検索のURL
Googleで「東京 天気」と検索すると、URLは次のようになります。
https://www.google.com/search?q=%E6%9D%B1%E4%BA%AC+%E5%A4%A9%E6%B0%97
%E6%9D%B1%E4%BA%AC が「東京」、+ がスペース、%E5%A4%A9%E6%B0%97 が「天気」に対応しています。
HTMLフォームの送信
HTMLフォームを GET メソッドで送信すると、入力内容がURLエンコードされてクエリ文字列に含まれます。
<form method="GET" action="/search">
<input name="q" value="東京タワー">
<button type="submit">検索</button>
</form>
<!-- 送信先: /search?q=%E6%9D%B1%E4%BA%AC%E3%82%BF%E3%83%AF%E3%83%BC -->
POST メソッドで application/x-www-form-urlencoded 形式を使う場合も、リクエストボディ内でURLエンコードが使われます。
REST APIのパラメータ
API呼び出しでクエリパラメータに日本語や特殊文字を含める場合、適切なエンコードが不可欠です。
// APIキーに特殊文字が含まれる場合
const apiKey = 'abc+def/ghi=';
const url = `https://api.example.com/data?key=${encodeURIComponent(apiKey)}`;
// → https://api.example.com/data?key=abc%2Bdef%2Fghi%3D
SNSシェアリンク
TwitterやFacebookのシェアリンクにURLを含める場合、二重エンコードが必要になることがあります。
const shareUrl = 'https://example.com/記事タイトル';
const twitterLink = `https://twitter.com/intent/tweet?url=${encodeURIComponent(shareUrl)}`;
よくあるトラブルと対処法
二重エンコードの問題
すでにエンコード済みの文字列をもう一度エンコードしてしまうトラブルは非常に多いです。
元の文字: 東京
1回目: %E6%9D%B1%E4%BA%AC(正しい)
2回目: %25E6%259D%25B1%25E4%25BA%25AC(% が %25 に変換されてしまう)
対処法: エンコード処理が何回呼ばれているかを確認しましょう。フレームワークが自動エンコードしている場合に、手動でもエンコードすると二重になります。
文字化けが発生する
エンコード時とデコード時で文字コードが一致しないと文字化けが起きます。
# UTF-8でエンコードした文字列をShift_JISでデコードすると文字化け
UTF-8: %E6%9D%B1%E4%BA%AC → 「東京」
Shift_JIS: %93%8C%8B%9E → 「東京」
対処法: 現代のWebではUTF-8が標準です。古いシステムとの連携時に文字化けが起きる場合は、文字コードの指定を確認してください。
スペースが + になる問題
HTMLフォーム由来のURLでスペースが + で表されることがあります。decodeURIComponent() は + をデコードしないため、手動で変換が必要です。
// + をスペースに戻してからデコード
function decodeFormData(str) {
return decodeURIComponent(str.replace(/\+/g, '%20'));
}
decodeFormData('hello+world'); // "hello world"
長すぎるURLの問題
URLエンコードにより文字列が長くなるため、大量の日本語をクエリパラメータに含めるとURL長の制限に引っかかることがあります。
- ブラウザのURL長制限: 一般的に2,000〜8,000文字程度
- サーバーのURL長制限: Apache/Nginxのデフォルトは8,192バイト
対処法: 大量のデータを送る場合はGETではなくPOSTメソッドを使うか、検索パラメータをIDに変換するなどの工夫が必要です。
URLエンコードとセキュリティ
XSS(クロスサイトスクリプティング)対策
URLパラメータに悪意のあるスクリプトが含まれる場合があります。URLデコード後の値をHTMLにそのまま出力するのは危険です。
# 攻撃者が仕込む可能性のあるURL
https://example.com/search?q=%3Cscript%3Ealert('XSS')%3C/script%3E
# デコードすると: <script>alert('XSS')</script>
対処法: URLデコードした値をHTMLに出力する際は、必ずHTMLエスケープを行ってください。
SQLインジェクション
URLデコードした値をSQLクエリに直接埋め込むのも危険です。プリペアドステートメントを使いましょう。
オープンリダイレクト
URLパラメータにリダイレクト先URLを含めるとき、エンコードされた悪意あるURLに気づきにくくなります。リダイレクト先のバリデーションを必ず行ってください。
まとめ
URLエンコード(パーセントエンコーディング)は、Web開発で避けて通れない基本的な仕組みです。
押さえておくべきポイント:
- URLに使える文字は限られており、日本語や特殊文字は
%XX形式にエンコードする必要がある - 現在の標準はUTF-8。特別な理由がない限りUTF-8でエンコードする
- JavaScriptでは**
encodeURIComponent()をクエリパラメータの値に**、encodeURI()をURL全体に使う - 二重エンコードに注意。フレームワークの自動エンコードと手動エンコードが重複していないか確認する
- スペースは
%20と+の2通りの表現がある。用途に応じて使い分ける - URLデコード後の値を出力する際は、XSS対策としてHTMLエスケープを忘れない
実際のURLエンコード・デコードを試したい方は、URLエンコード・デコードツール をご活用ください。テキストを入力するだけでリアルタイムに変換結果を確認でき、ブラウザ上で完結するため安全にお使いいただけます。
この記事の内容はAssistyのURLエンコード・デコードで実際にお試しいただけます。