压缩
OxPHP 默认使用 Brotli 编码压缩 HTTP 响应。只要客户端支持,它就会自动应用于基于文本的内容类型,因此无需改动应用代码,传输体积就会下降。
工作原理
在 OxPHP 决定是否压缩某个响应之前,每个响应都会按顺序经过以下相同的检查:
- Accept-Encoding 检查。 解析客户端的
Accept-Encoding请求头,判断是否支持br(Brotli)。该请求头中不含br的请求永远不会被压缩。 - 内容类型检查。 对照可压缩类型列表核对响应的 MIME 类型。
- 已编码检查。 已带有
Content-Encoding请求头的响应会被跳过,以避免二次压缩。 - 大小范围检查。 只有介于 256 字节和 3 MB 之间的响应才会被压缩。更小的响应收益甚微;更大的响应则以流式方式发送,不做缓冲。
- 压缩。 应用 Brotli 编码。如果压缩后的输出并不比原始内容更小,则改为发送未压缩的响应。
压缩发生在 PHP 执行之后、静态文件服务之后。整个压缩后的响应体会短暂驻留在内存中,这也是为什么超过 3 MB 的响应会被排除在外。
配置
| 变量 | 默认值 | 描述 |
|---|---|---|
COMPRESSION_LEVEL |
4 |
Brotli 质量级别(0–11)。数值越高,输出越小,但会消耗更多 CPU 时间。设为 0 可完全禁用压缩 |
默认级别 4 在压缩率与 CPU 占用之间取得了平衡,适合 Web 服务场景。级别 9–11 更适合离线或构建期的压缩。
可压缩的内容类型
压缩适用于以下 MIME 类型:
Text 类型:
text/htmltext/csstext/plaintext/xmltext/javascript
Application 类型:
application/javascriptapplication/jsonapplication/xmlapplication/xhtml+xmlapplication/rss+xmlapplication/atom+xmlapplication/manifest+jsonapplication/ld+jsonapplication/wasm
其他类型:
image/svg+xmlfont/ttffont/otfapplication/x-font-ttfapplication/x-font-opentypeapplication/vnd.ms-fontobject
不会被压缩的情况
当满足以下任一条件时,响应将不经压缩直接发送:
- 客户端未在
Accept-Encoding请求头中声明br - 响应已带有
Content-Encoding请求头(例如预压缩内容) - 响应体小于 256 字节或大于 3 MB
- 内容类型不在可压缩列表中(例如
image/png、image/jpeg、font/woff2、application/zip——这些格式本身已使用内部压缩) - 响应以流式方式发送——在发送请求头时其长度未知(使用
oxphp_stream_flush()的 PHP 脚本、Server-Sent Events)。压缩一个流需要将其完整缓冲到内存中,会破坏首字节到达时间,因此流式响应始终原样不压缩地通过
响应头
应用压缩时,OxPHP 会设置以下请求头:
| 请求头 | 值 |
|---|---|
Content-Encoding |
br |
Content-Length |
更新为压缩后的响应体大小 |
Vary |
追加 Accept-Encoding,确保 HTTP 缓存为支持和不支持 Brotli 的客户端分别存储不同版本 |
疑难排查
响应没有被压缩
确认客户端发送了 Accept-Encoding: br。大多数现代浏览器都会发送,但某些 HTTP 测试工具默认不包含它。
使用 curl 检查:
curl -H "Accept-Encoding: br" -I http://localhost/在响应头中查找 Content-Encoding: br。如果缺失,请检查:
COMPRESSION_LEVEL未被设为0- 响应体至少有 256 字节
- 响应的
Content-Type在上述可压缩列表中
压缩反而让响应变大了
对于非常小的响应(几百字节以内),Brotli 的开销偶尔会产生比原始内容更大的输出。OxPHP 会检测到这一点并自动发送未压缩的响应——无需更改任何配置。
压缩导致 CPU 占用过高
更高的质量级别(8–11)压缩效果明显更好,但会消耗多得多的 CPU。如果你观察到压缩带来的高 CPU 消耗:
修复方法: 将 COMPRESSION_LEVEL 降到 4 或 5。这些级别能以极小的 CPU 代价,达到最高质量级别 80–90% 的体积缩减效果。
预压缩的资源被再次压缩
如果你的构建流程生成了 .br 文件并为这些文件设置了 Content-Encoding: br 请求头,OxPHP 会自动跳过重复压缩。如果你的预压缩内容仍被再次压缩,请确认在压缩运行之前,原始响应中已存在 Content-Encoding 请求头。
Docker 示例
services:
app:
image: ghcr.io/oxphp/oxphp:0.10.0
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
environment:
- DOCUMENT_ROOT=/var/www/html/public
- ENTRY_FILE=index.php
- COMPRESSION_LEVEL=6