RFC 3986: URI(統一資源識別子)
リンク切れやエンコード事故はなぜ起きるのか。URIの構文・パーセントエンコーディング・正規化・相対参照の解決アルゴリズムをRFC 3986の原典から精読し、Webアドレスの挙動を根本から理解し、実装や運用のミスを防げます。
- RFC 3986(2005年、STD 66)はURIの一般構文を「scheme://authority/path?query#fragment」と定め、旧RFC 2396を置き換えた。どの文字が区切りでどれがデータかを確定させたのが核心。
- 文字は非予約(英数と - . _ ~)と予約(: / ? # などの区切り)に分かれ、データが区切りと衝突するときだけ「%XX」でパーセントエンコードする。正規化は大文字小文字・パーセント・パスの三方向で揃える。
- 相対参照は基底URIと結合して目標URIへ解決する(RFC 3986 §5)。URIは上位概念でURL/URNはその性質の呼び分け、非ASCIIを許すIRIは「RFC 3987」でUTF-8のパーセントエンコードによりURIへ写す。
このRFCが決めたこと
横にスクロール
Webのリンク、APIのエンドポイント、設定ファイルに書くアドレス——それらはすべて URI(Uniform Resource Identifier/統一資源識別子) です。RFC 3986(2005年、STD 66)は、その一般構文を ABNF で厳密に定義した文書で、旧仕様 RFC 2396 を置き換えました。
このRFCの最大の功績は、どの文字が区切りで、どの文字がデータか を確定させたことです。scheme://authority/path?query#fragment という並びと各要素の境界を決めたことで、世界中のコンピュータが同じ文字列を同じ資源として解釈できるようになりました。
URIは資源を一意に指す名前を定義するだけで、取り方(プロトコルの動作)は各スキームの仕様に委ねます。RFC 3986 は文法の土台、http や mailto の意味づけは別RFC、という分業になっています。
構文を分解する
一般形は次のように分解できます。
foo://user@example.com:8042/over/there?name=ferret#nose
\_/ \________________/\_________/ \__________/ \__/
scheme authority path query fragment
各構成要素の区切りと役割は次のとおりです。
| 構成要素 | 区切り | 役割 |
|---|---|---|
| scheme | : の前 | アクセス手段。http・https・mailto・urn など。大文字小文字を区別しない |
| authority | // の後 | userinfo@host:port。資源が置かれた場所(多くはサーバー) |
| path | host に続く / | 階層パス。/ 区切りのセグメント列で資源を辿る |
| query | ? の後 | 非階層の補助データ。key=value&… という形は慣習にすぎない |
| fragment | # の後 | 副資源の指定。クライアント側で処理され、サーバーには送られない |
authority の host は、ドメイン名(reg-name)・IPv4・角括弧で囲む IPv6 リテラル [::1] のいずれかです。ドメイン名の解決は DNS が担います。また scheme + host + port の三つ組は Web のオリジンそのもので、同一オリジンポリシーと CORS の判定基準になります。
パーセントエンコーディングと正規化
区切り以外の文字は、次の二種類に大別されます。
- 非予約文字(unreserved):
A-Z a-z 0-9に記号- . _ ~を加えたもの。エンコード不要で意味も変わらない。 - 予約文字(reserved): 区切りとして特別な意味を持つ文字。
gen-delimsの: / ? # [ ] @とsub-delimsの! $ & ' ( ) * + , ; =。
データが区切り文字と衝突するときは、パーセントエンコーディング %XX(% +16進2桁)で退避します。たとえば検索語に含む & は %26 にします。
正規化は「同じ資源を指すのに文字列が違う」状態を揃える処理で、主に三段階です。
HTTP://Example.COM:80/a/./b/../c/%7Euser
→ http://example.com/a/c/~user
- 大文字小文字:
schemeとhostを小文字に、%に続く16進は大文字に統一。 - パーセント: 非予約文字のエンコード(
%7E→~)を復号する。 - パス:
.(現在)と..(親)のドットセグメントを解決。加えて既定ポート:80の除去など、スキーム固有の正規化も行う。
正規化で元に戻してよいのは非予約文字だけです。%2F を / に戻すとパスの区切りが増え、別の資源を指してしまいます。デコードの範囲を誤ると、経路トラバーサルなどの脆弱性にも直結します。
つまずきやすい点
query の key=value&… は、このRFCの定義ではありません。 それは HTML フォームの application/x-www-form-urlencoded という慣習で、+ が空白を意味するのもフォーム側の規則です。RFC 3986 上の query は不透明な文字列にすぎず、意味づけは各スキームやアプリに委ねられます。
もう一つの難所が相対参照の解決です。RFC 3986 §5 は、基底 URI と相対参照から目標 URI を機械的に求めるアルゴリズム(remove_dot_segments を含む)を定義します。
Base: http://a/b/c/d;p?q
g → http://a/b/c/g
./g → http://a/b/c/g
../g → http://a/b/g
/g → http://a/g
//g → http://g
?y → http://a/b/c/d;p?y
#s → http://a/b/c/d;p?q#s
/g は絶対パス、//g は scheme だけ引き継ぐスキーム相対参照でホストごと切り替わります。末尾スラッシュの有無で基底が動く点も、リンク切れの定番原因です。
URI・URL・URN・IRI
歴史的に URL は「場所で見つける識別子」、URN は urn: スキームによる「場所に依存しない永続的な名前」を指しました。RFC 3986 はこれらを排他的なカテゴリではなく、URI という上位概念の性質の呼び分けとして整理し直しています。
非ASCII文字を直接使いたい要求には、IRI(RFC 3987) が応えます。IRI は Unicode を許し、URI へ変換するときに非ASCII部を UTF-8 バイト列のパーセントエンコードへ写します。ブラウザのアドレス欄に日本語ドメインや日本語パスが表示されるのは、この IRI → URI 変換のおかげです。
まとめ
RFC 3986 は Web のアドレスすべての文法的な土台です。scheme://authority/path?query#fragment の分解、非予約/予約文字とパーセントエンコーディング、そして正規化と相対解決のアルゴリズム——この4点を押さえれば、リンクの不具合やエンコード事故の大半は原因を追えます。http スキームの具体は HTTPのバージョン へ、他の主要仕様は RFC精読 の一覧へどうぞ。
RFC精読の記事ガイド
RFC 3986: URI(統一資源識別子)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
RFC
比較で見る軸
難易度: advanced / カテゴリ: RFC精読 / タグ数: 5
導入後に効く点
文字は非予約(英数と - . _ ~)と予約(: / ? # などの区切り)に分かれ、データが区切りと衝突するときだけ「%XX」でパーセントエンコードする。正規化は大文字小文字・パーセント・パスの三方向で揃える。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- RFC精読
- タグ数
- 5
判断チェックリスト
- 自社の用途が「RFC / URI」に近いか確認する。
- 強みである「RFC 3986(2005年、STD 66)はURIの一般構文を「scheme://authority/path?query#fragment」と定め、旧RFC 2396を置き換えた。どの文字が区切りでどれがデータかを確定させたのが核心。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。