RFC Compliance Map
This page lists the standards that matter for OxPHP as an origin server: HTTP, TLS, static files and the gateway to PHP. It describes OxPHP 0.13.0. WebDAV, proxying and shared caches are outside its scope.
A standard counts as implemented when the behavior is there. Some of it is OxPHP's own work, and some comes from the HTTP, TLS and compression libraries that OxPHP is built on and configures.
Fully implemented
- RFC 9112, HTTP/1.1: keep-alive, chunked bodies,
Expect: 100-continueand pipelining. A timeout limits how long a client may take to send its headers. - RFC 9113, HTTP/2: over TLS, and over plain TCP for clients that connect with prior knowledge. Concurrent streams and header size are capped, the Rapid Reset and MadeYouReset attacks are blocked, and a draining server says so with
GOAWAY. - RFC 7541, HPACK: HTTP/2 header compression, with a limit on decoded header size that stops "HPACK bomb" requests.
- RFC 8446 and RFC 5246, TLS 1.3 and 1.2: both versions, with a minimum version you can raise to 1.3. 0-RTT early data is off, so replayed early requests can't happen.
- RFC 7301, ALPN: the TLS handshake picks
h2orhttp/1.1. - RFC 9110, range requests: a single byte range on GET and HEAD, answered with
206or416. A request for several ranges gets the whole file with200, which the standard allows. Ranges are turned off when the response is compressed. - RFC 9110,
If-Range: honored only with a strong validator, as the standard requires. - RFC 9110, content coding: Brotli, gzip and Zstandard. Quality values in
Accept-Encodingare respected, andq=0means the client refuses that encoding. Compressed responses carryVary: Accept-Encodingand a weak ETag. Event streams are never compressed. - RFC 9110,
Retry-After: sent with429(the time left in the rate-limit window), with503while the server drains or shuts down, and with529when it is overloaded. - RFC 6585,
429 Too Many Requests: per-IP rate limiting answers429withRetry-After. - RFC 9110, HTTP dates: dates in headers are read and written in the standard format.
- RFC 10008, the
QUERYmethod: accepted with its body, which PHP reads the same way it reads a POST body. It has its own body size limit. AQUERYrequest withoutContent-Typegets400. - RFC 7578,
multipart/form-data: file uploads work the way they do in any PHP setup. - RFC 9113 and RFC 6265, split
Cookieheaders: when a client sendsCookieas several header lines, which HTTP/2 clients often do, OxPHP joins them into one before PHP sees them. - RFC 8615, well-known URIs: paths under
/.well-known/are served even though other dot paths are blocked. PHP files inside that directory are never executed. - RFC 1918, RFC 4193 and RFC 3927, private networks: the
privatekeyword in the trusted proxy list covers all of these ranges. Carrier-grade NAT (RFC 6598) is not included and has to be listed by hand. - RFC 7034,
X-Frame-Options:SAMEORIGINby default, sent together with a matching CSPframe-ancestorsandX-Content-Type-Options: nosniff. Headers set by your application take priority.
A few standards outside the RFC series are covered too:
- W3C Trace Context: distributed tracing continues an incoming
traceparentandtracestate, or starts a new trace when there is none. - WHATWG Server-Sent Events: a PHP response with
Content-Type: text/event-streamstreams to the client as it is written. - Prometheus text format 0.0.4: the internal server publishes metrics at
/metricsin this format, next to the health endpoints. You can limit which addresses may read it.
Partially implemented
- RFC 3875, CGI/1.1: the standard CGI variables are in
$_SERVER, and every request header arrives asHTTP_*. Missing: a header sent as several lines is not joined into one value, which the standard requires.$_SERVERkeeps the last line and the request API the first.REMOTE_HOSTis not set. - RFC 7239,
Forwarded: OxPHP readsForwardedandX-Forwarded-*from trusted proxies, andCF-Connecting-IPbehind Cloudflare. Missing: a quoted value that contains a comma or a semicolon is split in the wrong place.X-Real-IPis not supported. - RFC 9110, conditional requests: static files support
If-None-MatchandIf-Modified-Sinceand answer304. Missing:If-Match,If-Unmodified-Sinceand412 Precondition Failed. - RFC 9111, HTTP caching: static files get
Cache-Controlwith a configurablemax-age, plusETag,Last-ModifiedandVary. Missing: there is noimmutable, and every file gets the same policy whatever its type or path. - RFC 9110, request methods: bodies are read for POST, PUT, PATCH, DELETE and QUERY, and a body that is too large is refused with
413before it is read. Missing: a static file answers every method, POST and DELETE included, with200and its content, where nginx answers405with anAllowheader. - RFC 7617, Basic authentication: the
Authorizationheader reaches PHP asHTTP_AUTHORIZATION. Missing:PHP_AUTH_USER,PHP_AUTH_PWandAUTH_TYPEare not filled in. - RFC 3986, URIs: percent-decoding and host parsing work, IPv6 addresses included, and paths with
.or..segments get404. Missing: the trailing slash is dropped before routing, so/dirserves the directory index without a redirect to/dir/, and a file also answers at/file/. - RFC 6265, cookies: cookies that OxPHP's plugins set for themselves carry
SameSite,HttpOnly,SecureandMax-Age. Your application's cookies go through PHP'ssetcookie()as usual. Missing: for a malformed cookie pair, the request API'scookie()and$_COOKIEcan return different values.
Roadmap
These are proposals, sorted by priority. None of them is scheduled for a particular release.
High priority
- RFC 3875: join a header that arrives as several lines into one comma-separated value, so PHP sees the whole
X-Forwarded-Forchain from a series of proxies. - RFC 7617: fill in
PHP_AUTH_USERandPHP_AUTH_PWfrom Basic authentication. Applications that read them get empty values today, with no error. - RFC 6797, HSTS: send
Strict-Transport-Securityon every HTTPS response, static files and error pages included. PHP can't add it to static files, because it never sees those requests.
Medium priority
- RFC 7239: parse quoted values in
Forwardedcorrectly. - RFC 9110: answer
405withAllow: GET, HEADwhen a static file gets any other method, as nginx does. - RFC 3986: redirect
/dirto/dir/with301in Traditional routing, so relative links in older applications resolve. - RateLimit header fields (an IETF draft): send
RateLimitandRateLimit-Policy, so a client sees its limit before it runs into it. Today theX-RateLimit-*headers appear only on a429. - W3C Server-Timing: report queue wait and PHP execution time in a
Server-Timingheader. OxPHP already measures both for metrics and traces.
Low priority
- RFC 9110:
If-Match,If-Unmodified-Sinceand412for static files. Static files are read-only, so this would rarely matter. - RFC 6455 and RFC 8441, WebSocket: WebSocket over HTTP/1.1 and HTTP/2. The design is still being worked out.
- RFC 8297, Early Hints:
103 Early Hints, so browsers can start preloading assets before the response is ready. - RFC 8446, client certificates: mutual TLS for zero-trust environments.
- RFC 9114, HTTP/3, with RFC 7838, Alt-Svc: a large piece of work, and it isn't clear it would pay off.
Considered and rejected
The line runs between handling HTTP, which is the server's job, and content that belongs to a particular site, which is not.
- RFC 9116,
security.txt: the file lists the security contacts of whoever runs the site, and the server doesn't know them. A built-in default would be empty, or would wrongly point at the OxPHP maintainers. Put the file in your document root and OxPHP serves it like any other static file. nginx, Apache and FrankenPHP don't generate one either. - RFC 3875,
PATH_TRANSLATED: the standard recommends it whenPATH_INFOis set. It exists for CGI scripts that process some other file. In OxPHP the script being run is alwaysSCRIPT_FILENAME, and the usual nginx with PHP-FPM setup andphp -Sdon't set the variable either. Its value would be built from thePATH_INFOthe client sent, so older code that includes$_SERVER['PATH_TRANSLATED']would load a file of the client's choosing. - RFC 7540, h2c upgrade: RFC 9113 dropped the
Upgrade: h2chandshake. OxPHP serves HTTP/2 without TLS to clients with prior knowledge, and that is enough.
See also
- TLS: certificates, versions and HTTP/2 negotiation
- Compression: how OxPHP picks Brotli, Zstandard or gzip
- Trusted Proxies: getting the real client address behind a proxy or CDN
- Superglobals: what PHP finds in
$_SERVERand the other superglobals - Architecture Overview: how OxPHP handles a request from start to finish