日本語URLが%E6…になる理由とShift_JIS
2026.10.03 更新 · 4分で読めます
URLにはASCIIの一部しか使えないため、日本語はバイト列にしてから%XXで書きます。UTF-8とShift_JISで結果が違う理由、encodeURIComponentとencodeURIの違い、二重エンコードの問題を説明します。
URLエンコード・デコードをすぐに使う日本語のURLが%E6%97%A5…のように変わるのは、文字化けではなくパーセントエンコーディングです。URLにはASCIIの一部の文字しか使えないため、日本語はまずバイト列(通常はUTF-8)に変換し、各バイトを%と16進数2桁で書きます。「日」はUTF-8で3バイトのE6 97 A5なので、%E6%97%A5になります。URLエンコード・デコードで方向を「デコード」にして貼り付ければ元の文字に戻せます。文字コードをShift_JISにすれば、Shift_JISでエンコードされたURLも読めます。
パーセントエンコーディングの規則
RFC 3986の規則を整理すると、URLで使う文字は3つに分かれます。
| 区分 | 文字 | 扱い |
|---|---|---|
| 非予約文字 | 英字、数字、- . _ ~ |
そのまま書く |
| 予約文字 | :/?#[]@ と !$&'()*+,;= |
区切り文字。値として入れるときはエンコード |
| それ以外 | 日本語などの非ASCII文字、スペース、"<>などの一部のASCII文字 |
バイト列にして%XXで書く |
16進数は大文字が推奨されており、%e6と%E6は同じバイトを表します。
| 入力 | 結果(UTF-8、encodeURIComponent) |
|---|---|
日本語 |
%E6%97%A5%E6%9C%AC%E8%AA%9E |
日本語 dev&tools=1 |
%E6%97%A5%E6%9C%AC%E8%AA%9E%20dev%26tools%3D1 |
よく使う漢字やかなは1文字がUTF-8で3バイトなので、エンコードすると9文字になります。日本語を含むURLが長くなるのはこのためです。
UTF-8とShift_JISでは結果が違う
パーセントエンコーディングはバイトの書き方にすぎません。文字をどのバイトにするかは、文字コードが決めます。同じ「日本語」でも結果が異なります。
| 文字コード | バイト列 | パーセントエンコーディング |
|---|---|---|
| UTF-8 | E6 97 A5 E6 9C AC E8 AA 9E(9個) |
%E6%97%A5%E6%9C%AC%E8%AA%9E |
| Shift_JIS(CP932) | 93 FA 96 7B 8C EA(6個) |
%93%FA%96%7B%8C%EA |
| EUC-JP | C6 FC CB DC B8 EC(6個) |
%C6%FC%CB%DC%B8%EC |
RFC 3986は、新しいURIスキームでは文字データを先にUTF-8に変換してからエンコードするよう勧めています。ブラウザが従うWHATWG URL標準も、パスは常にUTF-8でエンコードします。ただしクエリ文字列は文書の文字コードに従うため、Shift_JISで作られたページのリンクやフォームからは%93%FA…のような値が送られます。Shift_JISで動いている古いシステムと連携するときに、パラメーターをShift_JISでエンコードするよう求められるのはこのためです。
見分け方と注意点は次のとおりです。
- 漢字やかな1文字が
%XX3つならUTF-8、2つならShift_JISかEUC-JPの可能性が高いです。 - Shift_JISの結果をUTF-8として戻そうとすると失敗します。JavaScriptの
decodeURIComponent('%93%FA%96%7B%8C%EA')はURIErrorを投げます。 - 逆に、UTF-8のバイト列をShift_JISとして読むと文字化けします。「ゼキロ」は
繧シ繧ュ繝ュになります。 - Shift_JISの2バイト目は、ASCIIと同じ値になることがあります。「ゼキロ」は
83 5B 83 4C 83 8Dで、4CはASCIIのLです。そのため%83%5B%83L%83%8Dのように、結果に英字が混ざる場合があります。
相手のシステムがどの文字コードを使っているかは、推測せずに連携仕様書で確認してください。
encodeURIComponent・encodeURI・フォーム形式
| 方式 | そのまま残す文字 | スペース | 使う場面 |
|---|---|---|---|
encodeURIComponent |
英字、数字、-_.!~*'() |
%20 |
クエリの値、パスの1区切り |
encodeURI |
上の文字と;/?:@&=+$,# |
%20 |
URL全体 |
| フォーム形式 | 英字、数字、*-._ |
+ |
HTMLフォーム、URLSearchParams |
- URL全体:
https://dev.zekilo.com/ja/検索?q=a bはhttps://dev.zekilo.com/ja/%E6%A4%9C%E7%B4%A2?q=a%20bになります。:、/、?、=は区切り文字として残ります。 - フォーム形式:
URLSearchParamsでqに日本語 URLを入れると、q=%E6%97%A5%E6%9C%AC%E8%AA%9E+URLになります。スペースが+に変わります。 !'()*~は、encodeURIComponentでは変わりません。
値の中に&や=があるのにencodeURIを使うと、その文字が区切りとして読まれ、パラメーターが分かれてしまいます。クエリの値を1つ入れるときは、encodeURIComponentかURLSearchParamsを使ってください。
よくある問題
- 二重エンコード:エンコード済みの文字列をもう一度エンコードすると、
%が%25になります。%E6%97%A5は%25E6%2597%25A5になります。RFC 3986は、同じ文字列を2回以上エンコードまたはデコードしてはならないとしています。 - 途中で切れたシーケンス:URLが途中で切れて
%E0%A4%Aのように終わっていると、デコードはエラーになります。チャットやメールで長いURLの末尾が欠けていないか確認してください。 +とスペース:デコードのときに+をスペースとして読むのは、フォーム形式だけです。decodeURIComponent('a+b')はa+bをそのまま返します。- 日本語ドメイン:ホスト名はパーセントエンコーディングではなく、Punycodeに変換されます。
https://日本語.jp/のホストはxn--wgv71a119e.jpになります。
まとめ
%E6%97%A5は「日」のUTF-8のバイトを書いたものです。デコードすれば元の文字に戻ります。- 新しく作るシステムではUTF-8を使います。Shift_JISやEUC-JPは、相手のシステムが求めるときだけ使います。
- 値には
encodeURIComponent、URL全体にはencodeURIを使い、エンコードは1回だけにします。 - URLを確認するときはURLエンコード・デコードを使ってください。入力はブラウザ内だけで処理されます。