url

URL

概要

URL(Uniform Resource Locator)は、インターネット上のリソースの場所を示すアドレス。Webページ、画像、APIエンドポイントなど、あらゆるリソースを一意に識別する。

URLの構造

URLは以下の要素で構成される:

https://example.com:8080/path/to/page?key=value&foo=bar#section
└─┬──┘ └────┬────┘└─┬─┘└─────┬─────┘└────────┬───────┘└───┬──┘
スキーム   ホスト  ポート    パス        クエリパラメータ  フラグメント
要素説明
スキームプロトコルhttps, http, ftp
ホストサーバーのアドレスexample.com
ポート接続先ポート(省略可):8080
パスリソースの場所/path/to/page
クエリパラメータ追加情報(?で開始)?key=value
フラグメントページ内位置(#で開始)#section

各要素の区切り文字

文字役割
://スキームとホストの区切り
:ホストとポートの区切り
/パスの区切り
?クエリパラメータの開始
&クエリパラメータ間の区切り
=キーと値の区切り
#フラグメントの開始(クエリの終了)

URLエンコーディング

なぜエンコーディングが必要か

URLには使用できる文字が制限されている。予約文字(/, ?, & など)は特別な意味を持つため、データとしてこれらの文字を含めたい場合はエンコードが必要。

エンコーディングの仕組み

文字を %XX の形式に変換する(XXは16進数のASCIIコード)。

文字 → ASCIIコード(16進数) → %XX
 /   →        0x2F          → %2F

よく使うエンコード一覧

文字ASCIIコードエンコード
スペース0x20%20 または +
!0x21%21
#0x23%23
%0x25%25
&0x26%26
/0x2F%2F
?0x3F%3F
@0x40%40

スペースのエンコード

スペースは場所によってエンコード方法が異なる:

使用場所エンコード
パス部分%20
クエリパラメータ+ または %20
https://example.com/my%20file.txt?name=hello+world
                      ↑ パス部分        ↑ クエリ部分

スラッシュ(/)と %2F

パス区切りとしての /

スラッシュはURLのパス区切り文字として予約されている。

https://example.com/users/123
                   ↑    ↑
              パス区切り文字

データとしての /%2F

パスの値の一部としてスラッシュを含めたい場合は %2F にエンコードする。

# ファイル名が "2025/08/07.md" の場合
https://example.com/files/2025%2F08%2F07.md
                         └── ファイル名 ──┘

よくある問題

パス区切りの /%2F になってしまうと、ルーティングエラーが発生する:

URLサーバーの解釈
/users/123users123 の2つのセグメント ✅
/users%2F123users%2F123 という1つのセグメント ❌

二重エンコード問題

原因

すでにエンコードされたURLを再度エンコードすると発生する。

/path/to/file
↓ 1回目のエンコード
/path/to/file(正常)
↓ 誤って2回目のエンコード
%2Fpath%2Fto%2Ffile(問題発生)

% 自体がエンコードされると %25 になる:

%2F → %252F(%が%25にエンコードされた)

回避策

1. エンコード前にチェック

function safeEncode(str) {
  try {
    if (str !== decodeURIComponent(str)) {
      return str; // すでにエンコード済み
    }
  } catch (e) {}
  return encodeURIComponent(str);
}

2. エンコード処理を一箇所に集約

// ❌ 悪い例:複数箇所でエンコード
const path = encodeURIComponent(userInput);
const url = buildUrl(encodeURIComponent(path));

// ✅ 良い例:最終出力時に一度だけ
const url = buildUrl(userInput);

3. URLオブジェクトを使用

const url = new URL('https://example.com');
url.pathname = '/path/to/file';
url.searchParams.set('name', 'foo/bar');
console.log(url.toString());
// → https://example.com/path/to/file?name=foo%2Fbar

4. 運用ルール

レイヤーデータの状態
内部処理常にデコード済みで扱う
外部出力時出力直前に一度だけエンコード

デバッグ方法

# 変数の中身を詳細に確認
echo "$VAR" | xxd
echo "$VAR" | cat -A
console.log(JSON.stringify(variable));

フラグメント

基本

# 以降はフラグメント(アンカー)と呼ばれ、ページ内の特定位置を示す。

https://example.com/page?id=123#section1
                              └─ フラグメント

特徴

  • クエリパラメータの終了を示す
  • サーバーには送信されない(ブラウザ側でのみ使用)
  • ページ内リンクやSPAのルーティングに使用
// URL: https://example.com/page?id=123#section
// サーバーが受け取るのは /page?id=123 のみ

JavaScriptでのURL操作

エンコード関数

// パス・フラグメント用(/, ?, # はエンコードしない)
encodeURI("https://example.com/path?q=hello world")
// → https://example.com/path?q=hello%20world

// パラメータ値用(すべての予約文字をエンコード)
encodeURIComponent("hello/world?foo=bar")
// → hello%2Fworld%3Ffoo%3Dbar

URLオブジェクト

const url = new URL('https://example.com/path');

// クエリパラメータの操作
url.searchParams.set('key', 'value');
url.searchParams.append('tag', 'javascript');
url.searchParams.get('key'); // → 'value'

// 各部分の取得
url.protocol  // → 'https:'
url.hostname  // → 'example.com'
url.pathname  // → '/path'
url.search    // → '?key=value&tag=javascript'
url.hash      // → ''(フラグメント)

参考リンク