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,与相应的 CSPframe-ancestors以及X-Content-Type-Options: nosniff一同发送。你的应用设置的响应头优先。
RFC 系列之外的几项标准也已覆盖:
- W3C Trace Context:分布式追踪会延续传入的
traceparent和tracestate,没有时则开启一条新的追踪。 - WHATWG Server-Sent Events:带有
Content-Type: text/event-stream的 PHP 响应会边写边流式发送给客户端。 - Prometheus 文本格式 0.0.4:内部服务器以这种格式在
/metrics上发布指标,与健康检查端点并列。你可以限制哪些地址能够读取它。
部分实现
- 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,这已经足够。