URL をエンコード・デコードする方法
URLでスペースがあるべきところに%20を見たり、アクセント付き文字があるべきところに%C3%A9を見たことがあるなら、それがURLエンコーディングです。これはWebの動作の基本的な部分であり、これを理解することで壊れたリンク、APIの問題、フォーム送信のデバッグに役立ちます。ブラウザベースのエンコーダーは、データをサーバーにアップロードせずに、作業全体をローカルで処理します。
URLエンコーディングがすること
URLには安全に使える文字が限られています。文字(A-Z、a-z)、数字(0-9)、いくつかの特殊文字(-、_、.、~)です。それ以外のすべて(スペース、アクセント付き文字、絵文字、&、=、#、?などの記号)は安全な形式に変換する必要があります。
URLエンコーディング(パーセントエンコーディングとも呼ばれる)は、安全でない文字を%の後に16進数のバイト値を続けた形に置き換えます:
| 文字 | エンコード後 |
|---|---|
| スペース | %20 |
| & | %26 |
| = | %3D |
| # | %23 |
| ? | %3F |
| / | %2F |
| @ | %40 |
| : | %3A |
| + | %2B |
| , | %2C |
| ; | %3B |
| (改行) | %0A |
| (タブ) | %09 |
URLエンコーディングが必要なとき
- 特殊文字を含むクエリパラメーター:
price > 100 & category = shoesのような検索クエリは、URLで動作するためにエンコーディングが必要です - URL内の非英語文字: 他の言語の名前、都市、コンテンツはエンコードする必要があります
- APIリクエスト: APIコールを手動で構築するとき、パラメーター値にはしばしばエンコーディングが必要です
- デバッグ: URLが動作しないとき、デコードすると実際の値が明らかになります
- メールリンク(mailto:): mailtoリンクの件名と本文テキストはエンコーディングが必要です
- OAuthリダイレクトURI: OAuthプロバイダーに渡すredirect_uriパラメーターは完全にエンコードされる必要があります
- Webhookペイロード: StripeやSlackなどのサービスが配信するWebhook URLのクエリ文字列
- モバイルアプリへのディープリンク: iOS / Androidアプリ向けのカスタムURLスキームは、安全な処理のためにエンコーディングが必要です
- GraphQLの永続クエリ: URLパラメーターとして付加されたハッシュ化されたクエリはエンコーディングが必要です
- PostgreSQL接続文字列: DATABASE_URL値内のパスワードやその他の特殊文字
エンコードとデコードの手順
- エンコードかデコードを選ぶ: 方向を選択します。クエリパラメーターにはencodeURIComponent、完全なURLにはencodeURIを選びます。
- 入力を貼り付け: テキストまたはURLを入力します。結果は即座に更新されます。
- 出力をコピー: 結果をコード、APIリクエスト、ブラウザで使います。
URLエンコーディングの簡単な歴史
URLエンコーディングは1994年12月に、元のURL仕様とともにRFC 1738で定義されました。RFCはWebの発明者であるTim Berners-Leeが、IETF URI Working Groupからの入力を受けて執筆しました。元のエンコーディングスキームはASCIIバイト値を使い、すべての予約文字または安全でない文字は%の後に16進数2桁を続ける形でエンコードされました。
エンコーディングは何度か更新されました:
- RFC 1738(1994年): 元のURL仕様、ASCIIのみ
- RFC 2396(1998年): より厳格な構文。「予約」と「非予約」文字を分離
- RFC 3986(2005年): 現在のURI仕様。2つのエンコーディングモード(パス vs クエリ)を定義し、非ASCIIにはUTF-8バイト列を使用
- WHATWG URL Standard(継続中): ブラウザ標準のリビングスペック。すべてのモダンブラウザが使用し、後方互換性のためRFC 3986とわずかに異なるルールを持ちます
最大の変更はRFC 3986でのUTF-8への移行でした。それ以前は、エンコードされたURLはASCIIのみで、非ラテン文字には回避策(ドメインにはPunycode、国際アドレスにはIDN)が必要でした。今日、URL内のアクセント付き「é」は%C3%A9(その2つのUTF-8バイト)にエンコードされ、古いシステムが生成したであろうLatin-1バイト%E9ではありません。
encodeURI vs encodeURIComponent
JavaScriptには微妙に異なる動作を持つ3つのエンコーディング関数があります:
| 関数 | エンコードするもの | 保持するもの | 用途 |
|---|---|---|---|
| encodeURI() | すべての安全でない文字 | URL構文: : / ? & = # | URL全体のエンコード |
| encodeURIComponent() | URL構文を含むすべての安全でない文字 | A-Z a-z 0-9 - _ . ~ ! * ' ( ) のみ | クエリパラメーター値 |
| escape()(非推奨) | ほとんどの安全でない文字 | Latin-1のみ | 使わない |
Pythonでは:
urllib.parse.quote()はencodeURIに似ています(/を保持しますが、:は保持しません)urllib.parse.quote_plus()はencodeURIComponentに似ていますが、スペースには+を使いますurllib.parse.urlencode(dict)はクエリ文字列全体をエンコードします
他の言語では:
| 言語 | コンポーネントエンコーディング | 完全なURIエンコーディング |
|---|---|---|
| Java | URLEncoder.encode()(+に関する注意あり) | URI.toASCIIString() |
| C# | Uri.EscapeDataString | Uri.EscapeUriString |
| Ruby | CGI.escape() | URI.encode_www_form_component |
| PHP | rawurlencode() | urlencode()(注意: %2B vs +) |
| Go | url.QueryEscape() | url.PathEscape() |
| Rust | percent_encodingクレート | percent_encodingクレート |
よくある落とし穴
- URL全体をエンコードする:
https://example.com/search?q=helloをエンコードするとhttps%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhelloになり、もはや動作するURLではありません。構造文字ではなく、値だけをエンコードしてください。 - 二重エンコーディング: すでにエンコードされた文字列をエンコードすると
%2520のようなもの(%が%25としてエンコードされる)が生成されます。URLがおかしく見えたら、何かが二重にエンコードされていないか確認してください。 - スペースが + か %20 か:
application/x-www-form-urlencoded(フォームPOSTボディ)では、スペースは+になります。URLでは、スペースは%20になります。ほとんどのサーバーは両方を受け入れますが、一部の厳格なパーサーは受け入れません。 - 予約文字のエンコーディングを誤る:
?、#、&、=はURL構文で特別な意味を持ちます。値として現れる場合はエンコードする必要があり、構文として現れる場合はエンコードしてはいけません。 - 受信時のデコードを忘れる: 値をエンコードして送信し、サーバーが
?q=hello%20worldをデコードせずにそのまま読むと、アプリケーションはhello worldではなくhello%20worldを見ます。ほとんどのフレームワークは自動的にデコードしますが、カスタムコードでは確認してください。 - プラス記号の混乱:
+はパスセグメントではリテラルのプラスで、クエリ文字列ではスペースです。曖昧さを避けるため、クエリ値内の実際のプラス記号は%2Bとしてエンコードしてください。 - UTF-8 vs 他のエンコーディング: URLに「résumé」が含まれていて、サーバーがUTF-8ではなくLatin-1を期待していると、文字化けすることがあります。モダンWebはUTF-8です。レガシーシステムはそうではありません。
- URL長制限: 仕様には厳格な制限はありませんが、ブラウザとサーバーはしばしばURLを2048から8192文字に制限します。大量にエンコードされたデータは予想より早く制限に達することがあります。
- クッキーとRefererヘッダー: URLはRefererヘッダーで渡され、ログに記録される可能性があります。URL内の機密データ(パスワード、トークン)はログと分析に漏洩します。機密データにはPOSTボディを使ってください。
- 非ASCIIドメイン名: ドメインはパーセントエンコーディングではなくPunycode(RFC 3492)を使います。「münchen.de」はDNSルックアップで「m%C3%BCnchen.de」ではなく「xn--mnchen-3ya.de」になります。
具体例
| 入力 | encodeURI | encodeURIComponent |
|---|---|---|
hello world | hello%20world | hello%20world |
q=test&page=1 | q=test&page=1 | q%3Dtest%26page%3D1 |
https://x.com/path | https://x.com/path | https%3A%2F%2Fx.com%2Fpath |
caf é | caf%20%C3%A9 | caf%20%C3%A9 |
中文 | %E4%B8%AD%E6%96%87 | %E4%B8%AD%E6%96%87 |
100% | 100%25 | 100%25 |
email@test.com | email@test.com | email%40test.com |
使いこなしのヒント
- URL全体ではなく値をエンコードする: URL全体をエンコードすると、URLを構造化するスラッシュとコロンもエンコードされて壊れます。クエリパラメーター内の値だけをエンコードしてください。
- 二重エンコーディングに注意: すでにエンコードされた文字列をエンコードすると
%2520のようなものが生成されます。URLがおかしく見えたら、何かが二重にエンコードされていないか確認してください。 - デバッグ用にデコード: APIリクエストが失敗したりURLが文字化けして見えたら、デコードして実際のパラメーター値を確認します。これがすぐに問題を明かすことがよくあります。
- 言語の組み込み関数を使う: 本番コードでは、手動でエンコードするのではなく、つねに
encodeURIComponent()(JavaScript)、urllib.parse.quote()(Python)、URLEncoder.encode()(Java)を使ってください。 - エッジケースでテスト: スペース、アクセント、絵文字、特殊文字を含む入力を試します。それらすべてに対してエンコーディングが動作すれば、十分です。
- ブラウザアドレスバーで検証: エンコードされたURLをブラウザに貼り付けます。ページが読み込まれれば、URLは正しく形成されています。読み込まれなければ、エンコーディングにバグがあります。
- 複雑なケースにはクエリ文字列ライブラリを使う: 辞書やオブジェクトからクエリ文字列を構築する(
?a=1&b=2&c=3)のは、手動で組み立てるよりも、ライブラリ関数(Pythonのurlencode、JavaScriptのURLSearchParams)の方が簡単で安全です。 - パスとクエリのエンコーディングの違いを知る: パスセグメントのフォワードスラッシュ
/は構造的です。クエリ値ではエンコードする必要があります。RFC 3986はそれぞれに異なるルールを持ちます。
プライバシーと機密URL
URLエンコーダーとデコーダーは完全にブラウザ内で動作します。貼り付けたURL、中間処理、エンコード/デコードされた出力はすべてデバイス上に留まります。サーバーへのアップロード、ロギング、第三者との共有は一切ありません。
これはURLが極めて機密性の高いデータを含むことが多いからです。クエリパラメーター内のAPIキーとトークン、アカウントアクセスを付与するOAuth認可コード、セッションID、埋め込み認証情報を持つプライベートS3バケットの署名付きURL、マジックリンクのログイントークン、パスワードリセットURL、製品構造を明かす内部管理URL、購読解除リンクの顧客メールアドレス、フォーム送信の個人データなど。クラウド型のURLエンコーダーはすべての貼り付けをログに記録し、ときには「サービス改善」のために保持し、貼り付けられた認証トークンがログを監視する攻撃者によって抽出された実際の漏洩に関与してきました。ブラウザベースのエンコーダーは露出ゼロで、URLはマシンを離れません。
ブラウザベースのエンコーディングは、ページを一度読み込めばオフラインでも動作するため、飛行機の中、インターネットアクセスのないセキュアな環境、または認証情報を持つURLを第三者のサービスに貼り付けてはいけない場所でURLをエンコードするのに役立ちます。
よくある質問
encodeURI と encodeURIComponent の違いは何ですか?
encodeURI は URL 構造内の有効な文字(スラッシュ、コロン、クエスチョンマーク)を保持します。encodeURIComponent は文字、数字、いくつかの安全な文字以外をすべてエンコードします。クエリパラメータの値には encodeURIComponent、URL 全体には encodeURI を使ってください。
なぜスペースが %20 になったり + になったりするのですか?
URL エンコーディングではスペースは %20 になります。フォームデータ(application/x-www-form-urlencoded)ではスペースは + になります。それぞれの文脈ではどちらも有効ですが、%20 は URL の汎用的な標準です。
URL を手動でエンコードする必要がありますか?
ほとんどの場合、使用言語やフレームワークが自動的にエンコードしてくれます。手動エンコードが役立つのは、URL を手作業で組み立てるとき、API リクエストをデバッグするとき、特殊文字を含むクエリ文字列を扱うときです。
データはサーバーに送信されますか?
いいえ。すべてのエンコードとデコードはブラウザ内で行われます。