Knowledge
URLエンコードとは?日本語URLの仕組みと%エンコーディングの基礎
URLに使える文字・使えない文字の基準(RFC 3986)、encodeURI と encodeURIComponent の違い、日本語パラメータや特殊文字を扱う際の注意点を実例つきで解説します。
1. URLエンコードとは
URLエンコード(パーセントエンコーディング)とは、 URLとして使用できない文字を %XX の形式に変換する処理です。 XXは文字のUTF-8バイト値を16進数で表したものです。 例えば日本語の「東京」は %E6%9D%B1%E4%BA%AC になります。
URLはもともとASCII文字(英数字と一部の記号)しか扱えない仕様で設計されました。 Webがグローバルに普及し、日本語・中国語・アラビア語などのマルチバイト文字や 記号をURL中に含める必要が生じたため、 これらの文字を安全に表現する仕組みとしてパーセントエンコーディングが定められました。 現在の標準はRFC 3986(2005年制定)です。
エンコードとデコードの変換にはURL Encoder / Decoder を使うと リアルタイムで双方向の変換ができます。
2. URLで使える文字・使えない文字
RFC 3986では、URLに予約なく使用できる文字(非予約文字)を以下と定めています。
- アルファベット:A〜Z・a〜z
- 数字:0〜9
- 記号4種:
-(ハイフン)・_(アンダースコア)・.(ドット)・~(チルダ)
これ以外の文字はすべてエンコードが必要です。 ただしURLには構造上の意味を持つ予約文字も存在します。/(パス区切り)・?(クエリ開始)・&(パラメータ区切り)・=(キーと値の区切り)・#(フラグメント)などがこれに当たります。 予約文字は「構造として使う場合」はエンコード不要ですが、 「値として使う場合」はエンコードが必要です。
例えば https://example.com/search?q=a&b の & はパラメータ区切りとして機能しますが、 検索クエリとして a&b という文字列を送りたい場合はhttps://example.com/search?q=a%26b のようにエンコードが必要です。
3. %エンコーディングの仕組み
パーセントエンコーディングは以下の手順で行われます。
- エンコード対象の文字をUTF-8バイト列に変換する
- 各バイトを
%XX(XXは16進数2桁)の形式で表現する
例として「あ」をエンコードしてみます。 「あ」のUTF-8バイト列は E3 81 82(16進数)なので、 エンコード結果は %E3%81%82 になります。 スペースは %20、感嘆符 ! は %21 です。
デコードはこの逆で、%XX の形式をバイト値に戻し、 UTF-8として文字列に復元します。 URLをデコードせずにそのまま表示すると、%E6%9D%B1%E4%BA%AC のような文字列が見えることがありますが、 これは正しくエンコードされた日本語で、 デコードすると「東京」になります。
4. encodeURI と encodeURIComponent の違い
JavaScriptには2つのエンコード関数があり、目的によって使い分けます。
encodeURI(url): URL全体をエンコードする用途向けです。 URLの構造として機能する予約文字(/・?・&・=・#・:など)はエンコードしません。encodeURI('https://example.com/search?q=東京&lang=ja')のように 完全なURLを渡すと、日本語部分だけをエンコードできます。encodeURIComponent(str): URLの一部分(主にクエリパラメータの値)をエンコードする用途向けです。/・?・&・=なども含めてエンコードします。 クエリパラメータの値を構築するときは必ずこちらを使います。'q=' + encodeURIComponent(userInput)のように使います。
よくある間違いは、クエリパラメータの値に encodeURI を使うことです。 例えばユーザーが「A&B」と入力した場合、encodeURI では & がエンコードされずq=A&B になってしまい、 サーバー側では q=A と B= の2つのパラメータとして解釈されます。encodeURIComponent を使えば q=A%26B となり正しく送信できます。
5. 日本語URLとパーセントエンコーディング
日本語のパスやクエリを含むURLは、ブラウザのアドレスバーでは 日本語のまま表示されることがありますが、 内部的にはパーセントエンコードされた形式で通信されています。 Chromeのアドレスバーで日本語URLをコピーすると、 エンコードされた形式でクリップボードに入ることを確認できます。
HTMLの <a href> や fetch に日本語URLを直接渡しても 多くの場合は動作しますが、 HTTPヘッダーやcurl等のツールに渡す際は明示的にエンコードが必要です。 また Content-Disposition ヘッダーでダウンロードファイル名を指定する場合も、 日本語ファイル名はRFC 5987に基づいてエンコードが必要です。
国際化ドメイン名(日本語.jp のようなドメイン)は Punycode変換(xn--wgv71a309e.jp)が使われており、 パーセントエンコーディングとは別の仕組みです。 現代のブラウザはどちらも透過的に扱いますが、 バックエンドでURLを文字列処理する際には注意が必要です。
6. よくある落とし穴
- スペースの
+表現: HTMLフォームのapplication/x-www-form-urlencoded形式では、 スペースを%20ではなく+で表します。 URLクエリ文字列では両方見かけますが、RFC 3986準拠の純粋なパーセントエンコーディングでは スペースは%20です。 サーバーサイドでデコードする際、+をスペースとして扱うかどうかは 使用するライブラリや言語の実装に依存するため、注意が必要です。 - 二重エンコード: すでにエンコードされた文字列を再度エンコードすると、
%20が%2520(%が%25にエンコードされる)になります。 URLを自前で組み立てる処理では、エンコード済みかどうかを常に意識しましょう。 - デコード忘れ: ルーターやフレームワークがパスパラメータを自動デコードするかどうかは実装次第です。 日本語のパスパラメータを含む場合、ミドルウェアで明示的にデコードしているか確認します。
7. 実務での活用場面
- 検索フォームのクエリ組み立て: ユーザーが入力したキーワードを含むURLを
fetchやrouter.pushに渡す前に、encodeURIComponentでエンコードします。 Next.jsのuseSearchParamsやURLSearchParamsAPIを使うと エンコードを自動で処理してくれるため、手動エンコードのミスを防げます。 - OGPやメールテンプレートのリンク: 日本語のタイトルやカテゴリをURLに含める場合、 メールクライアントやSlackなどのツールで正しく認識されるよう エンコードが必要です。
- curl・HTTPクライアントのデバッグ: APIのテストでcurlを使う際に日本語パラメータを渡す場合、 ターミナルで直接入力するとシェルが特殊文字として解釈することがあります。 事前にエンコードした値を使うか、
--data-urlencodeオプションを活用します。 - ログの確認: アクセスログに記録されたパーセントエンコードされたURLを人間が読める形に変換したい場合に、URL Encoder / Decoder のデコード機能が役立ちます。