RFC 準拠マップ

このページでは、オリジンサーバーとしての OxPHP に関わる標準を一覧にします。対象は HTTP、TLS、静的ファイル、そして PHP へのゲートウェイです。記載内容は OxPHP 0.13.0 のものです。WebDAV、プロキシ機能、共有キャッシュは対象外です。

ここでは、その動作が実際に備わっていれば実装済みとみなします。OxPHP 自身が実装している部分もあれば、OxPHP が土台とし、設定して使っている HTTP、TLS、圧縮のライブラリが担っている部分もあります。

完全に実装済み

  • RFC 9112、HTTP/1.1: keep-alive、チャンク形式のボディ、Expect: 100-continue、パイプライン処理に対応します。クライアントがヘッダーを送り終えるまでの時間は、タイムアウトで制限されます。
  • RFC 9113、HTTP/2: TLS 上で動作し、事前知識(prior knowledge)で接続するクライアントには平文の TCP 上でも動作します。同時ストリーム数とヘッダーサイズには上限があり、Rapid Reset 攻撃と MadeYouReset 攻撃は防がれます。ドレイン中のサーバーは GOAWAY でそれを伝えます。
  • RFC 7541、HPACK: HTTP/2 のヘッダー圧縮です。デコード後のヘッダーサイズに上限があり、「HPACK ボム」のリクエストを止めます。
  • RFC 8446 と RFC 5246、TLS 1.3 と 1.2: 両方のバージョンに対応し、最小バージョンは 1.3 まで引き上げられます。0-RTT の早期データは無効なので、早期データのリクエストがリプレイされることはありません。
  • RFC 7301、ALPN: TLS ハンドシェイクで h2 か http/1.1 を選びます。
  • RFC 9110、Range リクエスト: GET と HEAD で単一のバイト範囲を受け付け、206 または 416 で応答します。複数の範囲を求めるリクエストにはファイル全体を 200 で返します。これは標準が認めている動作です。レスポンスを圧縮する場合、範囲指定は無効になります。
  • RFC 9110、If-Range: 標準の要求どおり、強いバリデーターの場合にだけ尊重します。
  • RFC 9110、コンテンツコーディング: Brotli、gzip、Zstandard に対応します。Accept-Encoding の q 値を尊重し、q=0 はクライアントがそのエンコーディングを拒否しているものとして扱います。圧縮されたレスポンスには Vary: Accept-Encoding と弱い ETag が付きます。イベントストリームは圧縮しません。
  • RFC 9110、Retry-After: 429(レート制限ウィンドウの残り時間)、サーバーのドレイン中またはシャットダウン中の 503、過負荷時の 529 に付けて送ります。
  • RFC 6585、429 Too Many Requests: IP 単位のレート制限は Retry-After 付きの 429 で応答します。
  • RFC 9110、HTTP の日付: ヘッダー内の日付は標準の形式で読み書きします。
  • RFC 10008、QUERY メソッド: ボディ付きで受け付け、PHP はそのボディを POST のボディと同じ方法で読み取ります。ボディサイズには専用の上限があります。Content-Type のない QUERY リクエストには 400 を返します。
  • RFC 7578、multipart/form-data: ファイルアップロードは、一般的な PHP 環境と同じように動作します。
  • RFC 9113 と RFC 6265、分割された Cookie ヘッダー: クライアントが Cookie を複数のヘッダー行に分けて送った場合(HTTP/2 クライアントによくあります)、OxPHP は PHP に渡す前にそれらを 1 つにまとめます。
  • RFC 8615、Well-Known URI: 他のドットパスはブロックしますが、/.well-known/ 配下のパスは配信します。このディレクトリ内の PHP ファイルが実行されることはありません。
  • RFC 1918、RFC 4193 と RFC 3927、プライベートネットワーク: 信頼済みプロキシのリストの private キーワードは、これらの範囲をすべてカバーします。キャリアグレード NAT(RFC 6598)は含まれないため、手動で列挙する必要があります。
  • RFC 7034、X-Frame-Options: デフォルトは SAMEORIGIN で、対応する CSP の frame-ancestors と X-Content-Type-Options: nosniff を一緒に送ります。アプリケーションが設定したヘッダーが優先されます。

RFC 以外の標準にもいくつか対応しています。

一部実装済み

  • RFC 3875、CGI/1.1: 標準の CGI 変数は $_SERVER に入り、すべてのリクエストヘッダーは HTTP_* として届きます。未対応: 複数行で送られたヘッダーが 1 つの値にまとめられません。標準ではまとめることが求められています。$_SERVER には最後の行が、リクエスト API には最初の行が残ります。REMOTE_HOST は設定されません。
  • RFC 7239、Forwarded: OxPHP は信頼済みプロキシからの Forwarded と X-Forwarded-* を読み取り、Cloudflare の背後では CF-Connecting-IP も読み取ります。未対応: カンマやセミコロンを含む引用符付きの値が、誤った位置で分割されます。X-Real-IP には対応していません。
  • RFC 9110、条件付きリクエスト: 静的ファイルは If-None-Match と If-Modified-Since に対応し、304 で応答します。未対応: If-Match、If-Unmodified-Since、412 Precondition Failed。
  • RFC 9111、HTTP キャッシュ: 静的ファイルには、max-age を設定できる Cache-Control と、ETag、Last-Modified、Vary が付きます。未対応: immutable はなく、種類やパスに関係なくすべてのファイルに同じポリシーが適用されます。
  • RFC 9110、リクエストメソッド: POST、PUT、PATCH、DELETE、QUERY ではボディを読み取り、大きすぎるボディは読み取る前に 413 で拒否します。未対応: 静的ファイルは POST や DELETE を含むすべてのメソッドに、200 とその内容で応答します。nginx の場合は Allow ヘッダー付きの 405 で応答します。
  • RFC 7617、Basic 認証: Authorization ヘッダーは HTTP_AUTHORIZATION として PHP に届きます。未対応: PHP_AUTH_USER、PHP_AUTH_PW、AUTH_TYPE は設定されません。
  • RFC 3986、URI: パーセントデコードとホストの解析は IPv6 アドレスも含めて動作し、. や .. のセグメントを含むパスには 404 を返します。未対応: ルーティングの前に末尾のスラッシュが取り除かれるため、/dir は /dir/ へリダイレクトせずにディレクトリインデックスを返し、ファイルは /file/ でも応答します。
  • RFC 6265、Cookie: OxPHP のプラグインが自分用に設定する Cookie には SameSite、HttpOnly、Secure、Max-Age が付きます。アプリケーションの Cookie は、通常どおり PHP の setcookie() を通ります。未対応: 不正な形式の Cookie ペアに対して、リクエスト API の cookie() と $_COOKIE が異なる値を返すことがあります。

ロードマップ

以下は提案で、優先度順に並べています。どれも特定のリリースには予定されていません。

高優先度

  • RFC 3875: 複数行で届いたヘッダーを、カンマ区切りの 1 つの値にまとめます。これにより、複数のプロキシを経由した X-Forwarded-For のチェーン全体が PHP から見えるようになります。
  • RFC 7617: Basic 認証から PHP_AUTH_USER と PHP_AUTH_PW を設定します。現在、これらを読み取るアプリケーションは、エラーもなく空の値を受け取ります。
  • RFC 6797、HSTS: 静的ファイルやエラーページも含め、すべての HTTPS レスポンスで Strict-Transport-Security を送ります。静的ファイルへのリクエストは PHP に届かないため、PHP 側では静的ファイルにこのヘッダーを付けられません。

中優先度

  • RFC 7239: Forwarded 内の引用符付きの値を正しく解析します。
  • RFC 9110: 静的ファイルがそれ以外のメソッドを受けたときに、nginx と同じく Allow: GET, HEAD 付きの 405 で応答します。
  • RFC 3986: 従来型モードのルーティングで /dir を 301 で /dir/ へリダイレクトし、古いアプリケーションの相対リンクが解決されるようにします。
  • RateLimit ヘッダーフィールド(IETF のドラフト): RateLimit と RateLimit-Policy を送り、クライアントが上限に達する前にそれを把握できるようにします。現在、X-RateLimit-* ヘッダーは 429 にしか付きません。
  • W3C Server-Timing: キューでの待ち時間と PHP の実行時間を Server-Timing ヘッダーで報告します。OxPHP はメトリクスとトレースのために、すでに両方を計測しています。

低優先度

  • RFC 9110: 静的ファイルでの If-Match、If-Unmodified-Since、412。静的ファイルは読み取り専用なので、これが問題になることはほとんどありません。
  • RFC 6455 と RFC 8441、WebSocket: HTTP/1.1 と HTTP/2 上の WebSocket。設計はまだ検討中です。
  • RFC 8297、Early Hints: 103 Early Hints を送り、レスポンスの準備が整う前にブラウザがアセットのプリロードを始められるようにします。
  • RFC 8446、クライアント証明書: ゼロトラスト環境向けの相互 TLS。
  • RFC 9114、HTTP/3 と RFC 7838、Alt-Svc: 作業量が大きく、それに見合う効果があるかははっきりしません。

検討の上で見送ったもの

HTTP の処理はサーバーの仕事ですが、特定のサイトに属するコンテンツはサーバーの仕事ではありません。線はその間に引いています。

  • RFC 9116、security.txt: このファイルにはサイト運営者のセキュリティ連絡先を記載しますが、サーバーはそれを知りません。組み込みのデフォルトを用意しても、中身が空になるか、誤って OxPHP のメンテナーを指すことになります。ドキュメントルートにファイルを置けば、OxPHP は他の静的ファイルと同じように配信します。nginx、Apache、FrankenPHP もこのファイルを生成しません。
  • RFC 3875、PATH_TRANSLATED: 標準は PATH_INFO が設定されている場合にこの変数を推奨しています。これは別のファイルを処理する CGI スクリプトのためのものです。OxPHP で実行されるスクリプトは常に SCRIPT_FILENAME であり、nginx と PHP-FPM の一般的な構成や php -S もこの変数を設定しません。値はクライアントが送った PATH_INFO から組み立てられるため、$_SERVER['PATH_TRANSLATED'] のファイルを include する古いコードは、クライアントが選んだファイルを読み込んでしまいます。
  • RFC 7540、h2c アップグレード: RFC 9113 で Upgrade: h2c ハンドシェイクは廃止されました。OxPHP は事前知識を持つクライアントに TLS なしの HTTP/2 を提供しており、それで十分です。

関連項目

  • TLS — 証明書、バージョン、HTTP/2 のネゴシエーション
  • 圧縮 — OxPHP が Brotli、Zstandard、gzip のどれを選ぶか
  • 信頼済みプロキシ — プロキシや CDN の背後で実際のクライアントアドレスを得る
  • スーパーグローバル — PHP が $_SERVER などのスーパーグローバルで受け取るもの
  • アーキテクチャ概要 — OxPHP がリクエストを最初から最後までどう処理するか