URL エンコードの仕様と「日本語が文字化けする」を本気で直すガイド
執筆: デジタル道具屋 編集部 / Web 標準担当
検索フォーム・OAuth コールバック URL・Open Graph 等、URL を真面目に扱う実装を多数経験してきた編集チームです。
本記事の執筆方針と編集ポリシーについてはAbout ページをご覧ください。
導入:「URL に日本語を入れたら全部 % になった」

https://example.com/?q=%E3%81%82%E3%81%84 って『%』だらけになっちゃって意味不明なんだぜ! これって壊れてるのかよ?」
%E3%81%82 は『あ』を UTF-8 で 3バイト表現した結果じゃよ。」
1. percent-encoding の仕組み
URL では 「予約文字(reserved characters)」と「非予約文字(unreserved characters)」が区別されます。 非予約文字(英数字とごく一部の記号)以外を URL の意味のある場所に入れたい場合、各バイトを %XX という 16進数表記に変換します。
'あ' → UTF-8: 0xE3 0x81 0x82 → "%E3%81%82"
'A' → ASCII: 0x41 → "A"(変換不要)
' ' → ASCII: 0x20 → "%20" もしくは "+"
'?' → ASCII: 0x3F → "%3F"(クエリ区切りと混同しないため)2. encodeURI と encodeURIComponent の違い

encodeURI と encodeURIComponent 両方あるけど、どっち使うんだぜ?」ざっくり言うと、パスやクエリの「区切り記号」を残すかどうかの違いです。
| 関数 | 対象 | ? や / |
|---|---|---|
encodeURI | URL 全体 | そのまま残す |
encodeURIComponent | クエリの値、フラグメント等の「断片」 | エンコードする |
実務では クエリパラメータの値はほぼ常に encodeURIComponentを使います。encodeURI は『URL 全体をまとめて変換したい』という用途のとき使いますが、ベストは URL オブジェクトと URLSearchParams を活用することです:
// 推奨:URLSearchParams を使う
const params = new URLSearchParams({ q: '東京 ラーメン', page: '2' });
const url = `https://example.com/search?${params.toString()}`;
// → https://example.com/search?q=%E6%9D%B1%E4%BA%AC+%E3%83%A9%E3%83%BC%E3%83%A1%E3%83%B3&page=2
URLSearchParams なら percent-encoding を全部勝手にやってくれるから、文字列を自分で連結するより安全よ。」3. 「+ はスペース」問題
URL のクエリ部分では、スペースが %20 ではなく + で表されることがあります。 これは application/x-www-form-urlencoded(HTML フォームの送信形式)の歴史的経緯です。パス部分では %20、クエリ部分では + または %20、というのが現在の事実上のルール。
デコード側でも注意が必要:
decodeURIComponent('東京+ラーメン') // → "東京+ラーメン"(+ は変換されない)
new URLSearchParams('q=東京+ラーメン').get('q') // → "東京 ラーメン"(+ がスペースに)4. 日本語ファイル名のダウンロード
Content-Disposition: attachment; filename="日本語.pdf" のような書き方は、 ASCII 以外の扱いが歴史的に混乱しています。 現在は RFC 5987 に従い、以下のように書くのが標準です:
Content-Disposition: attachment;
filename="report.pdf";
filename*=UTF-8''%E6%97%A5%E6%9C%AC%E8%AA%9E.pdffilename(旧)に ASCII フォールバック、filename*(新)に RFC 5987 形式で日本語を入れます。 モダンブラウザは filename* を優先するため、日本語ファイル名で正しくダウンロードされます。
5. SNS シェア用 URL の組み立て
X(Twitter)や LINE のシェア URL を生成するとき、ユーザー入力をそのまま連結すると壊れます:
// 失敗:& や # が混入すると壊れる
const url = `https://twitter.com/intent/tweet?text=${title}&url=${pageUrl}`;
// 正しい
const url = new URL('https://twitter.com/intent/tweet');
url.searchParams.set('text', title);
url.searchParams.set('url', pageUrl);
window.open(url.toString());6. 試して確かめる
URL に含まれる謎の %XX をデコードしたり、自分の文字列を percent-encoding したりするには、URL エンコード・デコードツールが便利です。 クエリパラメータのデバッグや、SNS シェア URL の構築にもどうぞ。
関連: HTML エンティティ変換(HTML 上で < を表示する用)、Base64 変換(バイナリを URL に乗せる別手段)。
🚨 現場の失敗あるある
OAuth の state や redirect_uri をエンコードし忘れて、+ や = を含む値が壊れて認証フローが止まる事故をしばしば見ます。 URL を組み立てるときは、String 連結ではなく URL オブジェクトとURLSearchParams を使う癖を付けると、ほとんどの落とし穴を回避できます。
参考にした一次情報
本記事の内容は、以下の公式仕様や一次情報を参照して執筆しています。
- RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax
IETF
https://www.rfc-editor.org/rfc/rfc3986
- URL Living Standard
WHATWG
https://url.spec.whatwg.org/
- encodeURIComponent() - JavaScript | MDN
MDN Web Docs
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
この記事の内容を実際に試す
解説した内容は、以下のツールでブラウザ上から無料で試せます。