URLは一見シンプルに見えます。しかしhttps://api.example.com:8443/v2/search?q=hello+world&page=1&sort=desc&filter%5B%5D=active&filter%5B%5D=paid#resultsのようなURLを目にすると、各部分が何をしているのか正確に把握したくなります。URLパーサーはURLを分解し、パーセントエンコーディングを頭の中でデコードする必要なく、各コンポーネントを検査できます。

URLの解剖

すべてのURLはRFC 3986で定義された標準的な構造に従います:

scheme://[userinfo@]host[:port]/path[?query][#fragment]

上記の例を分解すると:

コンポーネント
スキームhttps
ホストapi.example.com
ポート8443
パス/v2/search
クエリ文字列q=hello+world&page=1&sort=desc&filter%5B%5D=active&filter%5B%5D=paid
フラグメントresults

ZeroToolでURLをすぐに解析する →

URLを貼り付けると、スキーム・オーソリティ・ホスト・ポート・パスセグメント・デコードされたクエリパラメーター・フラグメントがすべて分かれて表示されます。パーセントエンコードされたクエリパラメーターは自動的にデコードされます。

コンポーネントの詳細

スキーム

スキームはプロトコルを指定します。主なスキーム:

スキーム用途
httpsセキュアHTTP
http非暗号化HTTP(本番環境では非推奨)
ws / wssWebSocket
ftpファイル転送
mailtoメールリンク
dataインラインデータURI
blobブラウザオブジェクトURL

オーソリティ:ユーザー情報・ホスト・ポート

オーソリティコンポーネントは[userinfo@]host[:port]です。

ユーザー情報user:password@host)は現代のURLではほとんど使われず、セキュリティリスクとして扱われています。URLの認証情報はサーバーログに記録されてしまいます。

ホストはドメイン名・IPv4アドレス・IPv6アドレス(角括弧必須:[::1])になります。パーサーはサブドメイン構造を解決します。api.v2.example.comのサブドメインはapi.v2、ドメインはexample.com、TLDは.comです。

ポートはオプションです。省略した場合、デフォルトが使われます。HTTPSは443、HTTPは80。明示的にデフォルトポートを指定することは(HTTPSで:443)有効ですが冗長です。

パス

パスはリソースを識別します。パスセグメントは/で区切られます。先頭と末尾のスラッシュは意味を持ちます。/api/users/api/users/はフレームワークによって異なるルーティングになる場合があります。

パスパラメーター(/users/:id:id)はURL仕様の一部ではなく、ルーティングの慣習です。URLパーサーはリテラルのパスを表示し、ルーターがパターンを解釈します。

クエリ文字列

クエリ文字列は?以降#以前のすべてです。&で区切られたkey=valueペアの列です。

パーセントエンコーディング:URLで許可されていない文字は%XXXXは16進コード)としてエンコードされます。主な例:

文字エンコード
スペース%20または+
[%5B
]%5D
#%23
@%40
/%2F
=%3D

スペースを+でエンコードするのはapplication/x-www-form-urlencoded形式(HTMLフォーム)に特有です。モダンなAPIでは%20が推奨されます。

繰り返しキーfilter%5B%5D=active&filter%5B%5D=paidfilter[]=active&filter[]=paidにデコードされます。PHP・Rails・多くのフレームワークは繰り返しキーを配列として解釈します。

フラグメント

フラグメント(#results)はサーバーに送信されません。ブラウザだけが処理し、通常はそのIDを持つ要素にスクロールします。SPAはハッシュベースルーターでクライアントサイドルーティングにフラグメントを使用します。

URLパーサーが実際に必要な場面

APIデバッグ

ネットワークインスペクターからURLをコピーしてきました。エンコードされたクエリパラメーター・珍しいポートがあり、末尾の#がフラグメントなのかタイポなのか分かりません。パーサーに貼り付けると各コンポーネントが明確になります。

リダイレクトチェーンの分析

リダイレクトチェーンを追跡しています。チェーン内の各URLにパラメーターがあり、それらを比較する必要があります。各URLをパースすることで差異が明確になります。

セキュリティレビュー

長いURLにはクエリパラメーターに認証情報・内部ホスト名・エンコードされたペイロードが含まれていることがあります。URLをコンポーネントに分解することでこれらが見えやすくなります。クエリパラメーターのtoken=eyJ...やユーザー情報のadmin:[email protected]を発見できます。

プログラムによるURL構築

コードでURLを構築する際、各コンポーネントの正しいエンコーディングを理解することでバグを防げます:

// 誤り — クエリ値はエンコードが必要
const url = `https://api.example.com/search?q=${userInput}`;

// 正しい
const url = new URL('https://api.example.com/search');
url.searchParams.set('q', userInput);  // 自動でエンコード

コードでのURL解析

JavaScript(ブラウザ + Node.js)

URL APIはすべてのモダン環境で利用可能です:

const url = new URL('https://api.example.com:8443/v2/search?q=hello+world&page=1#results');

console.log(url.protocol);   // "https:"
console.log(url.hostname);   // "api.example.com"
console.log(url.port);       // "8443"
console.log(url.pathname);   // "/v2/search"
console.log(url.hash);       // "#results"

// クエリパラメーター
url.searchParams.get('q');           // "hello world"(デコード済み)
url.searchParams.get('page');        // "1"
url.searchParams.getAll('filter[]'); // ["active", "paid"]

// 全パラメーターを反復
for (const [key, value] of url.searchParams) {
  console.log(key, value);
}

Python

from urllib.parse import urlparse, parse_qs

raw = "https://api.example.com:8443/v2/search?q=hello+world&page=1&filter[]=active&filter[]=paid#results"

parsed = urlparse(raw)
print(parsed.scheme)    # https
print(parsed.hostname)  # api.example.com
print(parsed.port)      # 8443
print(parsed.path)      # /v2/search
print(parsed.fragment)  # results

params = parse_qs(parsed.query)
print(params['q'])       # ['hello world']
print(params['filter[]']) # ['active', 'paid']

Go

import (
    "fmt"
    "net/url"
)

func main() {
    raw := "https://api.example.com:8443/v2/search?q=hello+world&page=1#results"
    u, _ := url.Parse(raw)

    fmt.Println(u.Scheme)     // https
    fmt.Println(u.Hostname()) // api.example.com
    fmt.Println(u.Port())     // 8443
    fmt.Println(u.Path)       // /v2/search
    fmt.Println(u.Fragment)   // results
    fmt.Println(u.Query().Get("q")) // hello world
}

よくあるURLのミス

二重エンコード:すでにエンコードされたURLを再度エンコードしてしまう。%20%2520になります(%25%なので、%2520はスペースではなく%20にデコードされます)。常に生の値をエンコードし、すでにエンコードされた文字列は再エンコードしないでください。

フラグメントの除外を忘れる:サーバーがリクエストで#fragmentを探しても見つかりません。フラグメントはクライアント専用です。

順序の前提:クエリパラメーターの順序は保証されていません。a=1&b=2b=2&a=1の順序に依存するロジックは作らないでください。

比較でのポートの欠落https://example.comhttps://example.com:443は同じURLですが、文字列比較では異なります。

まとめ

URLの解析はAPI作業・デバッグ・セキュリティレビューで頻繁に必要になります。構造(スキーム・オーソリティ・パス・クエリ・フラグメント)と各コンポーネントのエンコーディングを理解することで、バグを防ぎデバッグ時間を節約できます。

URLをすぐに解析・デコードする — 各コンポーネントを即座に確認 →