要約

  • バイト位置は断片の置き場所を示すが、その断片がどの版に属するかまでは示さない。
  • Range と If-Range を組み合わせると、強いバリデータが一致した場合だけ範囲を処理し、不一致なら範囲を無視して現在の表現全体を返せる。
  • この分岐は二度目の要求と版の混在を避けるが、範囲対応、受信完了、保存、内容の正しさ、オリジンの真正性を保証しない。

続きの位置は、続きの身元ではなかった

クライアントが大きなファイルの先頭40パーセントを受け取ったところで接続を失ったとする。再接続後、最後に受け取った位置の次から要求すれば無駄がない。ただし、その間にファイルが差し替えられていれば、同じ位置は別のバイト列の途中にある。古い前半と新しい後半を結合すると、長さだけはもっともらしいのに、サーバーが一度も提供していない物体ができる。

これは接続回復とは異なる問題である。トランスポートは以前のストリームが終わったことを扱えるが、新しいHTTP要求が以前と同じ選択済み表現を得るとは限らない。Range は空間上の座標を伝える。時間をまたいだ同一性には、別の証拠が必要だった。

1997年の RFC 2068 は、標準化された最初のHTTP/1.1で If-Range を定義した。部分コピーを持つクライアントが通常の条件付きGETを使うと、表現の変更によって前提条件が失敗し、その後に完全な現行本体を求める二度目の要求が必要になる。If-Range はそれを短絡する。変わっていなければ不足部分を、変わっていれば新しい全体を一度で返す。

RFC 2616 は1999年にこの二分岐を引き継いだ。エンティティタグが一致すれば206 Partial Contentで部分を返し、一致しなければ200 OKで全体を返す。条件の偽は取得の失敗を意味しない。失われるのは旧版を完成させるという節約だけであり、現行版を読む機会は残る。

一つの要求が二つの有用な答えを許した

要求は次のような形になる。

Range: bytes=4194304-
If-Range: "asset-2026-08-30-a"

クライアントは「部分応答だけを返せ」と命令しているのではない。"asset-2026-08-30-a" が現在の選択済み表現にも強く一致する場合に限って、部分応答を受け入れると言っている。不一致なら、サーバーは Range を無視し、通常のGETとして完全な表現を返す。クライアント側はその200の本体を古い断片へ追加せず、古い断片を置き換えなければならない。

ここが他の条件ヘッダーとの違いである。If-Match は操作を止めて412 Precondition Failedを生じさせることがある。If-None-Match は304 Not Modifiedによって本体を不要にできる。If-Range は、条件が偽でも現行表現の全体が役立つことをあらかじめ表明する。可否ではなく、応答の形を選ぶ条件だ。

2014年の RFC 7233 は範囲要求を独立して整理した。クライアントは Range なしで If-Range を送ってはならず、サーバーは Range がない場合や対象が範囲を扱わない場合にそれを無視する。バリデータが一致すれば範囲を処理し、不一致なら必ず範囲を無視する。この仕組みが省くのは変更確認後の追加要求であって、範囲機能そのものを生み出すわけではない。

現在のHTTP意味論をまとめる RFC 9110 も同じ順序を保つ。GETに Range と If-Range があり、条件が真で範囲が適用可能なら206となる。それ以外は範囲を無視して200となる。先に判定できるエラーやリダイレクトは優先される。条件がサーバーの通常の選択・権限・構文検査を飛び越えることはない。

断片の接合には強い一致が必要だった

HTTPには強いバリデータと弱いバリデータがある。表示やキャッシュ再利用では、多少バイトが違っても意味が同じなら十分な場合がある。しかし断片の接合では、「だいたい同じ」は成立しない。先頭で一バイト増えただけでも、その後の座標と内容がずれる。

RFC 7232 は、観測可能な表現データが変化すれば強いバリデータも変化し、部分範囲にも使えるとする。弱いバリデータは正確な同一性を求めない用途のためのものだ。そのためクライアントは弱いETagを If-Range に入れてはならない。Last-Modifiedの日付を使えるのは、対応するETagを持たず、その日付自体が強いバリデータとして扱える場合だけである。

日付には粒度の問題がある。同じ秒の中で内容が二度変わることもある。If-Range の日付評価はLast-Modifiedとの完全一致であり、If-Unmodified-Since の「以前または同時」とも異なる。十分に強くない日付は条件を偽にする。帯域を惜しむために、粗い時刻へ版の権威を持たせない設計である。

ETagも不透明な値にすぎない。暗号学的ハッシュである必要はなく、引用符から生成方法を推測できない。強さが意味するのは、このリソースの関連する表現で観測可能なバイトが変わったとき、オリジンが同じ値を使い回さないという範囲内の約束だ。発行者の身元、内容の妥当性、別URLの物体との等価性は証明しない。RFC 2616は、異なるURIで同じタグを使っても等価性を意味しないと明記していた。

一致しても206はまだ確定しない

強い比較が成功した後も、サーバーには判断が残る。要求された範囲単位を扱えるか、構文は正しいか、区間は現在の長さに重なるか、過剰な範囲指定ではないか。対応済みで有効かつ充足可能な範囲なら、通常は206 Partial Contentとなる。単一部分には Content-Range が付き、複数部分は multipart/byteranges で各部分の位置を示す。どの区間も届かない場合は416 Range Not Satisfiableとなり得る。

Content-Range は受け取った本体の座標を説明するが、以前の断片の版は証明しない。共有された強いバリデータが版の連続性を支える。一方、全バイトが到着し、正しい順序で保存されたかを示せるのはクライアントやキャッシュの記録だけである。

誤りの重さは左右非対称だ。不一致を余分に判定すれば、完全な表現を再送するため帯域を失うが、得られる物体は一貫している。誤って一致させれば、異なる版の断片が静かに混ざる。HTTPは不確かな節約より、目に見える再送を選んだ。

キャッシュは断片の来歴を背負った

キャッシュは不完全な200、206、あるいは離れた複数区間を保存できる。結合を始めた時点で、単なる中継ではなく来歴の管理者になる。RFC 7234 は、Range と Content-Range を理解するキャッシュだけに不完全応答の保存を認め、同じ強いバリデータを共有する区間だけに結合を認めた。

座標が隙間なく埋まることは、版が一つである証拠ではない。月曜の先頭、火曜の中央、水曜の末尾でも、算術上は完全な長さになる。キャッシュは結合時に応答メタデータも更新し、本当に完全になる前に完全な200として再利用してはならない。

現行の RFC 9111 も同じ責任分担を維持する。後の範囲要求で不完全応答を完成させることはできるが、完成するまでは完全物として扱えない。部分応答は206として明示しなければならない。If-Range は保存や結合を代行せず、一回の分岐に必要な版証拠を提供するだけだ。

運用上は、要求先、コンテントネゴシエーション入力、Range、以前と現在のバリデータ、比較方式、ステータス、Content-Range、完成物のハッシュを別々に残す必要がある。サーバーは送信した分岐を説明できる。クライアントやキャッシュは受信・結合・保持した事実を説明できる。片方のログだけで全工程の証明にはならない。

世界共通の版台帳を作らない連続性

If-Range は、すべてのファイル版を中央で登録する仕組みも、中断したクライアントのために資源をロックする仕組みも求めなかった。オリジンがバリデータを作り、クライアントが断片とともに保持し、サーバーが現在値を比較し、キャッシュが自分の断片を管理する。

共有される問いは一つだけである。「この新しい区間を、手元の表現の続きとして扱えるか」。否なら部分取得の特典を失うが、現在の完全版は受け取れる。この狭さが、実行中の各参加者の権限を残したまま相互運用を可能にした。

RFCが定義するのはこの契約であって、現在の製品の実装実態ではない。実際のETagが強いか、中間装置が値を維持したか、範囲を本当に扱うか、最終物が永続化されたかは分からない。それらには要求・応答の記録、選択条件、バリデータ履歴、各区間のハッシュ、組み立て記録が必要である。