URL解析とエンコーディング完全ガイド:構造の分解からセキュアな処理までの実践

URLの完全な構造

https://user:pass@example.com:8080/path/to/page?name=value&age=25#section
  │       │     │    │          │  │   │            │                │
  scheme  user  pass  host      port path           query            fragment
構成要素 例 説明
scheme https プロトコル
userinfo user:pass 認証情報(現在はほとんど使われない)
host example.com ドメインまたはIP
port 8080 ポート番号(デフォルトは80/443)
path /path/to/page パス
query ?name=value&age=25 クエリパラメータ
fragment #section アンカー(サーバーには送信されない)

URL APIで解析する

基本的な解析

const url = new URL('https://example.com:8080/path?q=test#section');

url.protocol  // "https:"
url.hostname  // "example.com"
url.port      // "8080"
url.pathname  // "/path"
url.search    // "?q=test"
url.hash      // "#section"
url.origin    // "https://example.com:8080"
url.href      // 完全なURL

クエリパラメータの解析

const url = new URL('https://example.com/api?name=Alice&age=25&tags=js&tags=css');

// URLSearchParams
url.searchParams.get('name')     // "Alice"
url.searchParams.get('age')      // "25"
url.searchParams.getAll('tags')  // ["js", "css"]
url.searchParams.has('name')     // true
url.searchParams.delete('age')

// イテレーション
for (const [key, value] of url.searchParams) {
  console.log(`${key} = ${value}`);
}

URLの変更

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

url.pathname = '/new-page';
url.searchParams.set('sort', 'desc');
url.searchParams.append('filter', 'active');
url.hash = '#top';

console.log(url.toString());
// "https://example.com/new-page?sort=desc&filter=active#top"

URLエンコーディングのルール

なぜエンコードが必要か?

URLはASCII文字のみ許可されるため、それ以外の文字はエンコードが必要です:

元:https://example.com/search?q=你好 世界
エンコード後:https://example.com/search?q=%E4%BD%A0%E5%A5%BD%20%E4%B8%96%E7%95%8C

encodeURI と encodeURIComponent

関数 エンコード範囲 用途
encodeURI URL構造文字(:/?#[]@!$&'()*+,;=)を保持 完全なURLをエンコード
encodeURIComponent すべての非ASCII+予約文字をエンコード クエリパラメータ値をエンコード
const url = 'https://example.com/path?name=你好&redirect=/home';

// encodeURI:URL構造を保持
encodeURI(url);
// "https://example.com/path?name=%E4%BD%A0%E5%A5%BD&redirect=/home"

// encodeURIComponent:すべての特殊文字をエンコード
encodeURIComponent(url);
// "https%3A%2F%2Fexample.com%2Fpath%3Fname%3D%E4%BD%A0%E5%A5%BD%26redirect%3D%2Fhome"

// ✅ 正しい使い方:パラメータ値には encodeURIComponent を使用
const name = '你好 世界';
const redirect = '/home';
const fullUrl = `https://example.com/path?name=${encodeURIComponent(name)}&redirect=${encodeURIComponent(redirect)}`;

主なエンコード対応表

文字 エンコード 説明
スペース %20 URLでは + を使わない
+ %2B クエリパラメータ内の + はスペースにデコードされる
& %26 クエリパラメータの区切り文字
= %3D キーと値の区切り文字
# %23 アンカー識別子
% %25 エンコードのプレフィックス自体
中国語 %E4%BD%A0 UTF-8の3バイトエンコード

ToolsKuでURLをエンコード/デコードする

  1. URLエンコード・デコードツールを開く
  2. 入力ボックスにURLまたはテキストを貼り付ける
  3. 「エンコード」または「デコード」をクリック
  4. 結果を確認

セキュアなURL処理

1. URLインジェクションの防止

// ❌ 危険:ユーザー入力を直接連結
const url = `/api/users/${userId}`;

// ✅ 安全:検証+エンコード
function buildUserUrl(userId) {
  if (!/^\d+$/.test(userId)) {
    throw new Error('Invalid user ID');
  }
  return `/api/users/${encodeURIComponent(userId)}`;
}

2. オープンリダイレクトの防止

// ❌ 危険:ユーザーが任意のリダイレクト先URLを指定できる
app.get('/login', (req, res) => {
  res.redirect(req.query.redirect);
});

// ✅ 安全:ホワイトリスト検証
function safeRedirect(url, allowedHosts) {
  try {
    const parsed = new URL(url, 'https://example.com');
    if (allowedHosts.includes(parsed.hostname)) {
      return url;
    }
  } catch {
    // 無効なURL
  }
  return '/'; // ホームページにフォールバック
}

3. URL経由のXSS防止

// ❌ 危険:javascript: プロトコル
const url = 'javascript:alert(document.cookie)';

// ✅ 安全:http/https プロトコルのみ許可
function sanitizeUrl(url) {
  try {
    const parsed = new URL(url);
    if (['http:', 'https:'].includes(parsed.protocol)) {
      return url;
    }
  } catch {
    // 無効なURL
  }
  return 'about:blank';
}

URLPattern API(新標準)

// ルートマッチング
const pattern = new URLPattern({ pathname: '/api/users/:id' });

const result = pattern.exec('https://example.com/api/users/123');
if (result) {
  console.log(result.pathname.groups.id); // "123"
}

// より複雑なパターン
const apiPattern = new URLPattern({
  protocol: 'https',
  hostname: ':env.example.com',
  pathname: '/api/:version/:resource/:id?',
});

const match = apiPattern.exec('https://prod.example.com/api/v2/users/456');
match.hostname.groups.env      // "prod"
match.pathname.groups.version  // "v2"
match.pathname.groups.resource // "users"
match.pathname.groups.id       // "456"

クエリパラメータ構築のベストプラクティス

URLSearchParamsを使う

// ✅ 推奨:URLSearchParams を使用
const params = new URLSearchParams({
  q: '検索キーワード',
  sort: 'desc',
  page: '1',
  limit: '20',
});

params.append('tags', 'javascript');
params.append('tags', 'css');

const url = `https://api.example.com/search?${params}`;
// "https://api.example.com/search?q=...&sort=desc&page=1&limit=20&tags=javascript&tags=css"

配列パラメータの主な慣例

慣例 例 サーバーフレームワーク
キーの繰り返し ?tags=js&tags=css Express, Koa
ブラケットサフィックス ?tags[]=js&tags[]=css PHP, Rails
インデックス付き ?tags[0]=js&tags[1]=css PHP
カンマ区切り ?tags=js,css カスタム

よくある質問

URLのスペースが %20 のときと + のときがあるのはなぜ?

  • URLパス内:スペース = %20(標準)
  • クエリパラメータ内:スペース = + または %20(+ は application/x-www-form-urlencoded の慣例)
  • 推奨:統一して %20 を使い、曖昧さを避ける

URLに最大長の制限はある?

シーン 制限
ChromeのURLバー 2MB
FirefoxのURLバー 制限なし
IEのURLバー 2083文字
HTTP GETリクエスト サーバー次第(通常8KB)
安全な上限 2048文字(すべてのブラウザで互換)

2048文字を超えるパラメータはPOSTリクエストに切り替えましょう。


まとめ

URLはWebの礎であり、その構造、エンコーディングルール、セキュアな処理を理解することはすべての開発者の基本スキルです。核心ポイントはパラメータ値は encodeURIComponent でエンコードし、プロトコルを検証してXSSを防ぎ、ホワイトリスト検証でオープンリダイレクトを防ぐことです。ToolsKuのURLエンコード・デコードツールを使えば、URLエンコーディングの問題を素早く処理できます。

#URL解析#URL编码#查询参数#Web开发#教程