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 使用,也可对以预先知晓方式连接的客户端经由明文 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,范围请求:支持 GET 和 HEAD 上的单个字节范围,以 206 或 416 应答。请求多个范围时会以 200 返回整个文件,这是标准所允许的。响应经过压缩时,范围处理会被禁用。
  • RFC 9110,If-Range:只有在携带强验证器时才会被遵守,这是标准的要求。
  • RFC 9110,内容编码:Brotli、gzip 和 Zstandard。Accept-Encoding 中的权重会被遵守,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 看到之前把它们合并成一行。
  • 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_* 的形式到达。尚缺:分成多行发送的请求头不会合并为一个值,而标准要求合并。$_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:把分成多行到达的请求头合并为一个以逗号分隔的值,使 PHP 能看到经过多级代理后完整的 X-Forwarded-For 链。
  • RFC 7617:根据 Basic 认证填充 PHP_AUTH_USER 和 PHP_AUTH_PW。目前读取它们的应用会得到空值,而且没有任何报错。
  • RFC 6797,HSTS:在每个 HTTPS 响应上发送 Strict-Transport-Security,包括静态文件和错误页面。PHP 无法给静态文件加上这个头,因为它根本看不到这些请求。

中优先级

  • RFC 7239:正确解析 Forwarded 中的带引号值。
  • RFC 9110:静态文件收到其他方法时,像 nginx 一样以带 Allow: GET, HEAD 的 405 应答。
  • RFC 3986:在传统路由模式下用 301 把 /dir 重定向到 /dir/,让旧应用中的相对链接能够正确解析。
  • RateLimit 头字段(IETF 草案):发送 RateLimit 和 RateLimit-Policy,让客户端在触及限制之前就能看到它。目前 X-RateLimit-* 头只出现在 429 响应上。
  • W3C Server-Timing:在 Server-Timing 头中报告队列等待时间和 PHP 执行时间。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'] 的旧代码会加载一个由客户端选定的文件。
  • RFC 7540,h2c 升级:RFC 9113 已经废弃了 Upgrade: h2c 握手。OxPHP 向以预先知晓方式连接的客户端提供不经 TLS 的 HTTP/2,这已经足够。

参见

  • TLS —— 证书、版本以及 HTTP/2 协商
  • 压缩 —— OxPHP 如何在 Brotli、Zstandard 和 gzip 之间选择
  • 可信代理 —— 在代理或 CDN 之后获取真实的客户端地址
  • 超全局变量 —— PHP 在 $_SERVER 及其他超全局变量中能看到什么
  • 架构概览 —— OxPHP 如何从头到尾处理一个请求
发现错误?报告问题 →