FrankenPHP 原生支持 HTTP 103 Early Hints:从 PHP 侧提前推送关键资源 📅 发布时间:2026/9/15 15:30:42 👁 浏览次数: FrankenPHP 原生支持 HTTP 103 Early Hints从 PHP 侧提前推送关键资源【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphpEarly HintsHTTP 103 状态码允许服务器在最终响应生成之前先向浏览器发送Link预加载提示让浏览器提前下载 CSS、JS 等关键资源从而显著缩短页面可交互时间。FrankenPHP 在 Caddy 层原生实现了对 103 Early Hints 的支持PHP 开发者只需调用一个原生函数即可启用且普通模式与 worker 模式 均可使用。本文将基于 docs/fr/early-hints.md 展开结合仓库源码与测试用例讲清其用法、底层实现原理与注意事项。Early Hints 是什么为什么值得用HTTP 103 Early Hints 是 HTTP 的 1xx 信息性状态码之一。它的典型工作流程是服务器收到请求后在尚未开始执行耗时逻辑如慢算法、复杂 SQL 查询之前立即返回103 Early Hints响应并在其中携带Link头浏览器收到 103 后不等最终 200 响应立刻开始预加载Link中声明的资源服务器完成耗时计算后再发送包含完整 HTML 的最终响应。由于资源下载与后端计算并行进行页面整体加载时间可以大幅缩短。FrankenPHP 的文档明确指出使用 Early Hints 可以将网页加载时间最多提升约 30%该数字来自官方文档表述实际收益取决于资源数量与后端耗时占比。FrankenPHP 的用法一条原生函数搞定FrankenPHP 将 103 Early Hints 的支持内置到了 PHP 运行时中开发者无需配置 Caddyfile、无需引入第三方库只需要在 PHP 代码里调用原生函数headers_send()。官方文档给出的最小可运行示例见 docs/fr/early-hints.md?php header(Link: /style.css; relpreload; asstyle); headers_send(103); // vos algorithmes lents et requêtes SQL // 这里放你的慢算法和 SQL 查询 echo HTML !DOCTYPE html titleHello FrankenPHP/title link relstylesheet hrefstyle.css HTML;代码逻辑拆解如下header(Link: ...)声明要预加载的资源。Link头是 Early Hints 的核心载体格式遵循 HTTP 规范relpreload表示预加载asstyle声明资源类型帮助浏览器按正确优先级和缓存语义处理headers_send(103)FrankenPHP 提供的原生函数将当前已设置的响应头以103 Early Hints状态码先行发送给客户端后续业务逻辑headers_send(103)之后继续执行慢算法、SQL 查询等耗时操作最终输出的 HTML 会在head或正文中引用同名资源此处为style.css与预加载的 URL 一一对应。参数与返回值headers_send()的原生签名在仓库的 frankenphp.stub.php 与 frankenphp_arginfo.h 中定义了该函数的签名headers_send(int $response_code 200): int。也就是说参数$response_code可选默认值为200返回值为int表示发送结果。当传入的状态码落在100 $response_code 200区间时函数按信息性响应处理发送后不会改变后续最终响应的状态码传入其他状态码如默认的 200时则按普通响应头发送逻辑处理。底层原理从 C 扩展到 Go 层的完整调用链Early Hints 并非魔法它在 FrankenPHP 中有清晰的两层实现PHP 侧C 扩展负责接收并转交状态码Go 侧Caddy 模块负责真正写入 HTTP 响应。理解这条调用链有助于排查头丢失、状态码异常等问题。PHP 侧headers_send()的 C 实现原生函数headers_send实现在 frankenphp.c 中核心逻辑是读取可选的response_code参数默认200暂存当前的http_response_code并把 SAPI 层响应的状态码临时改为目标值若状态码在100 ~ 199之间即 1xx 信息性响应调用sapi_module.send_headers()立即发送头部随后恢复之前的最终状态码——这正是 103 不会“污染”最终 200 响应的原因其他状态码则走常规的sapi_send_headers()。Go 侧写入 1xx 响应并清理头C 扩展最终会把头部写入动作转发给 Go 侧的go_sapi_send_headers回调见 frankenphp.go。Go 侧的关键处理包括对非法状态码小于 100 或大于 999做防御性兜底记录警告日志并回退为 500调用标准库ResponseWriter.WriteHeader()发送头部对 1xx 响应主动清空响应头集合——因为 Go 的net/http不会为 1xx 响应自动清理头而 103 之后还会继续发送最终 200 响应头必须重置否则会导致后续响应携带重复或过期的头部例如本例中的Link。这正是测试中header_remove(Link)也能生效的机制来源103 发送后头集合已被清空最终响应默认不含Link除非 PHP 代码在最后显式重新设置。仓库中的验证测试与示例脚本FrankenPHP 为 Early Hints 提供了完整的自动化测试与可运行的示例脚本是理解其行为边界的最佳素材。示例脚本 testdata/early-hints.php?php require_once __DIR__./_executor.php; return function () { header(Link: /style.css; relpreload; asstyle); header(Request: {$_GET[i]}); headers_send(103); header_remove(Link); echo Hello; };与文档示例相比这个测试脚本多了两个细节header_remove(Link)在 103 发送后移除Link头验证最终 200 响应不会重复携带预加载声明在真实场景中这通常是期望行为动态请求头通过$_GET[i]注入每次请求的唯一标识便于测试断言。测试用例 frankenphp_test.go测试代码testEarlyHints分别以普通模式TestEarlyHints_module和 worker 模式TestEarlyHints_worker加载early-hints.php作为 worker 脚本运行同一套断言通过httptrace.ClientTrace.Got1xxResponse回调捕获 103 响应断言其Link头精确等于/style.css; relpreload; asstyle且Request头携带正确序号断言最终响应的Request头正常、Link头为空——验证 103 与 200 两段响应互不干扰。这两组测试直接印证了官方文档的结论Early Hints 在普通模式与 worker 模式下均受支持。在 worker 模式下使用 Early Hints文档特别强调Early Hints 对普通模式和 worker 模式 一视同仁。worker 模式将 PHP 应用常驻内存请求由常驻 worker 循环处理此时在业务代码中调用headers_send(103)同样有效——frankenphp_test.go 中TestEarlyHints_worker正是对这一能力的回归保障。在 worker 模式下有两点值得注意由于 worker 常驻header()/headers_send()等每次请求都会重置请勿在请求间遗留静态头状态FrankenPHP 的 worker 机制本身会处理请求级的状态清理若使用 热重载 等功能103 发送逻辑照常工作不受影响。实践建议与注意事项结合文档、示例脚本与源码行为给出如下落地建议只在真正耗时的请求中使用103 的价值在于“早发”若页面本身就是毫秒级响应额外发送 103 反而增加开销与复杂度Link头内容必须与最终 HTML 一致预加载的 URL、rel、as必须与最终页面实际引用保持一致否则会造成重复下载或资源浪费尽早调用headers_send(103)位置越靠前浏览器获得提示越早并行收益越大——最佳位置是在 PHP 脚本入口处、执行任何耗时逻辑之前按需清理头若不想让Link出现在最终 200 响应中可在 103 发送后调用header_remove(Link)参考 testdata/early-hints.php关注浏览器支持103 Early Hints 需要现代浏览器配合旧浏览器会将其忽略1xx 属信息性响应不影响最终响应因此该特性是渐进增强而非破坏性变更。延伸阅读完整早期提示英文原文docs/early-hints.md官方文档其他语言版本简体中文、日语、西班牙语、意大利语、土耳其语 等相关技术主题worker 模式、热重载、性能优化、配置文件说明【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考