Web便利ツール集

URLエンコードで文字化けが直らない原因|二重エンコードと文字コードの見分け方

URLエンコード文字化けパーセントエンコーディングデコード

URLエンコードされた文字列をデコードしても文字化けが直らないとき、原因はほぼ次の3パターンのどれかです。「二重エンコード」「文字コードの不一致(Shift_JISなど)」「encodeURIとencodeURIComponentの取り違え」。それぞれの見分け方と直し方を、実際のデコード結果を見ながら整理します。

原因1:二重にエンコードされている

デコードした結果に % が残っているなら、二重エンコードを疑ってください。

たとえば「あ」をエンコードすると %E3%81%82 になりますが、これをもう一度エンコードすると %25E3%2581%2582 になります。% という記号自体が %25 に変換されるため、%25 が並んでいたら「もう一段エンコードされている」というサインです。

なぜ二重エンコードが起きるのか:

  • CMSやフォームがURLを受け取るたびに自動でエンコードする仕様になっている
  • リンクを生成する処理と、そのリンクをクリックしたブラウザの両方がエンコードしている
  • Googleアナリティクスなどの計測パラメータを、既にエンコード済みの状態でさらにURL生成関数に渡している

直し方: 1回デコードして %25 が残っていたら、もう一度デコードします。URLエンコード / デコードツールなら「デコード」を選んだ状態で結果をコピーし、もう一度貼り付ければ2段階目のデコードができます。3重以上になっているケースは稀ですが、%2525 のように %25 が繰り返されていないかも確認してください。

原因2:文字コードがUTF-8ではない

デコードした結果が 縺ゅ▲ のような意味不明な文字列になる場合、二重エンコードではなく文字コードの不一致が原因です。

現在のWebはほぼUTF-8で統一されていますが、2000年代以前に作られた古いシステムや、一部の業務システム・メールフォームでは今もShift_JISやEUC-JPでURLを生成していることがあります。この場合、同じ「あ」でも次のようにバイト列がまったく違います。

文字コード「あ」のエンコード結果
UTF-8%E3%81%82
Shift_JIS%82%A0
EUC-JP%A4%A2

ブラウザの標準デコード関数(decodeURIComponent)はUTF-8前提で動くため、Shift_JISやEUC-JPでエンコードされたバイト列をそのまま渡すと、デコードできてしまう場合とエラーになる場合の両方が起こり、意味の通らない文字列や URIError: malformed URI sequence になります。これはこのサイトのURLエンコードツールを含め、ブラウザ標準の関数を使うツール全般に共通する制約です。

見分け方: デコード結果が % を含まないのに文字化けしている場合は、この原因を疑ってください。対処には、元のバイト列がどの文字コードで書かれたかを特定したうえで、その文字コードに対応した変換が必要になります。UTF-8以外の文字コードのデコードは、汎用のURLエンコードツールでは直せないケースがある、という点は覚えておく価値があります。

原因3:encodeURIとencodeURIComponentを取り違えている

自分でURLを組み立てるコードを書いている場合、この取り違えが最も多い原因です。両者は「どこまでの記号をエンコードするか」が違います。

関数用途& = ? / # の扱い
encodeURIURL全体をエンコードするエンコードしない(そのまま残す)
encodeURIComponentクエリパラメータの値をエンコードするエンコードする

たとえば検索キーワードに &= が含まれる場合、encodeURI で組み立てると、その記号がクエリパラメータの区切り文字と誤認識され、サーバー側でパラメータが分割されてしまいます。

const keyword = "A&B=C";

encodeURI(`https://example.com/search?q=${keyword}`);
// → https://example.com/search?q=A&B=C
// 「&」がエンコードされず、q と B という2つのパラメータに分かれてしまう

encodeURIComponent(keyword);
// → A%26B%3DC
// クエリパラメータの値として安全に埋め込める

判断基準はシンプルです。 URL全体(プロトコルやスラッシュを含む文字列)を扱うなら encodeURI、クエリパラメータの値1つだけを扱うなら encodeURIComponent を使います。実務でエンコードが必要になる場面のほとんどは後者です。

実際に直す手順

  1. まずそのままデコードしてみる
  2. 結果に %25 などの % が残っていたら、もう一度デコードする(原因1)
  3. % を含まないのに意味の通らない文字列になっていたら、文字コードの不一致を疑う(原因2)
  4. &= を境に意図しないパラメータ分割が起きているなら、エンコード時の関数選択を疑う(原因3)

URLエンコード / デコードツールには、入力したURLを構成要素(プロトコル・ホスト・パス・クエリパラメータ)に分解して表示する機能があります。パラメータがいくつに分かれているかを見れば、原因3の「意図しない分割」が起きていないかをその場で確認できます。

見落としがちな注意点:+ 記号の扱いがツールの表示箇所によって違う

もう一つ、実務でつまずきやすいのが + の扱いです。URLエンコードの規格上、半角スペースの表現方法は2種類あります。

  • application/x-www-form-urlencoded(フォーム送信のクエリ文字列)では、スペースは + で表される
  • パーセントエンコーディング全般では、スペースは %20 で表される

decodeURIComponent はこの2つのうち後者にしか対応していません。つまり decodeURIComponent("a+b") を実行しても、結果は a+b のままで、a b(スペース入り)にはなりません。+%2B としてエンコードしていない限り、decodeURIComponent+ をスペースとして扱わないという仕様です。

一方、ブラウザ標準の URLSearchParamsnew URL(...).searchParams など、クエリパラメータをキーと値に分解する機能)は、フォーム送信の仕様に従って + を自動的にスペースへ変換します。同じ「+」が、使う関数によって別の意味に解釈されるわけです。

これはこのサイトのURLエンコードツールでも同じ構造になっています。テキスト全体を「デコード」した上部の結果では + はそのまま残りますが、URLを貼り付けたときに下部へ表示される「クエリパラメータ」欄では、内部でURLSearchParamsを使って分解しているため + がスペースとして表示されます。同じ入力なのに上下で見え方が違っても、ツールの不具合ではなく、この仕様差によるものです。フォーム経由で発行されたURL(検索フォームの送信結果など)を扱うときは、この違いを念頭に置いてください。

まとめ

  1. デコード結果に %25 が残っていたら二重エンコード。もう一度デコードする
  2. % を含まないのに文字化けしているなら、UTF-8以外の文字コード(Shift_JISなど)を疑う。ブラウザ標準のデコード関数はUTF-8前提のため直せないケースがある
  3. &= を境にパラメータが分割されるなら、encodeURIencodeURIComponent の取り違えを疑う
  4. + はエンコード方式によってスペースを表す場合と、そのままの文字として扱われる場合がある。使っている関数がどちらの仕様に従っているかを確認する

文字化け全般の原因についてはメールが文字化けする原因と直し方でも扱っているので、あわせて参考にしてください。

この記事で使うツール

関連記事