エンコード

URLエンコード(パーセントエンコーディング)ガイド:文字が %20 や %E3%81%82 に変わる理由

日本語の検索語を含むリンクをコピーすると %E3%81%82 のような文字列になります。これがURLエンコードです。このガイドでは、その背後にあるルールと、Web開発、マーケティング用リンク、API連携で起きる問題を説明します。

URLで使える文字は限られている

URLの構文はRFC 3986で定義されています。URLにそのまま書けるのは、英字、数字、そしてハイフン(-)、ピリオド(.)、アンダースコア(_)、チルダ(~)の4つの記号だけです。これらは非予約文字と呼ばれます。

一方、:、/、?、#、&、= などの記号は予約文字で、URLの構造を表します。? はクエリ文字列の始まり、& はパラメーターの区切り、# はフラグメントを示します。検索語そのものに & や # が含まれていると、エンコードせずに挿入した時点でURL全体の意味が変わってしまいます。

パーセントエンコーディングの仕組み

そのまま書けない文字はバイトに分解され、各バイトが % と2桁の16進数で表されます。空白(0x20)は %20、アンパサンド(0x26)は %26、イコール(0x3D)は %3D になります。

ASCII以外の文字は、まずUTF-8のバイトに変換してから同じ方法でエンコードします。たとえば韓国語の「안」はUTF-8で3バイト(EC 95 88)なので %EC%95%88 になります。1文字が9文字に増えるわけです。日本語の「あ」も同様に %E3%81%82 になります。

안녕 하세요?  →  %EC%95%88%EB%85%95%20%ED%95%98%EC%84%B8%EC%9A%94%3F
a b&c=d      →  a%20b%26c%3Dd

デコードはこの逆です。%EC%95%88 はバイト EC 95 88 に戻され、UTF-8として解釈されます。サーバーがこれを古い文字コードで解釈すると文字化けが起こります。古いWebサイトで検索語が化ける典型的な原因です。

空白は %20 か + か

最も混乱しやすいのが空白です。RFC 3986では空白は %20 です。しかしHTMLフォームが使う application/x-www-form-urlencoded 形式では、クエリ文字列の空白を + と書きます。同じ a b というテキストが、パスでは a%20b、フォームのクエリでは a+b と表れることになります。

問題になるのは、本物のプラス記号を送りたいときです。+81 のような電話番号や 1+1 のような式をエンコードせずにクエリに入れると、空白として読まれることがあります。こうした値は %2B とエンコードしましょう。ライブラリによって挙動が異なり、JavaScriptの encodeURIComponent は %20 を、一部のサーバー側ライブラリは + を出力するため、送信側と受信側で同じ規則を使っているか確認が必要です。

URL全体ではなく部品をエンコードする

URL全体を一度にエンコードしてはいけません。https://example.com/search?q=... をまるごとエンコードすると : と / が %3A と %2F になり、アドレスとして機能しなくなります。エンコードはパラメーターの値など、データにあたる部分だけに適用します。

JavaScriptにはこの考え方を反映した2つの関数があります。encodeURI は構造を表す文字をそのまま残し、encodeURIComponent は /、?、&、= までエンコードします。クエリパラメーターの値には encodeURIComponent を使うのが安全です。

二重エンコード:%2520 を見たら疑う

すでにエンコードされた文字列をもう一度エンコードすると、% が %25 になります。空白が %20 ではなく %2520 と表示されていたら、二重にエンコードされています。複数のリダイレクトを経由するトラッキングリンクや、別のURLをパラメーターとして持つログイン後の戻り先URLでよく起こります。

二重にデコードするのも同じくらい有害です。データに含まれる本物の %25 が % に変わり、値が変わってしまいます。ルールはシンプルで、値をURLに入れる直前に一度だけエンコードし、読み取った直後に一度だけデコードすることです。

実務チェックリスト

AIBIZAPのURLエンコーダー/デコーダーで値を試すと、どの文字がどう変わるかを正確に確認できます。

  • UTMリンクのキャンペーン名に空白や日本語が含まれる場合はエンコードする。
  • 検索語、メールアドレス(@を含む)、タイムスタンプ(:を含む)などのAPIの値は部品単位でエンコードする。
  • リンクが壊れたら %25 を探して二重エンコードを疑う。
  • サーバーログが文字化けしていたら、値がUTF-8としてデコード・解釈されているか確認する。
  • Base64の値をURLに入れるときは、Base64URLを使うか結果をパーセントエンコードする。

このツールで試してみましょう

その他のガイド

すべてのガイド