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バイトエンコード |
- URLエンコード・デコードツールを開く
- 入力ボックスにURLまたはテキストを貼り付ける
- 「エンコード」または「デコード」をクリック
- 結果を確認
セキュアな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エンコーディングの問題を素早く処理できます。