WebP Cloud Services Blog

WebP Server Go 0.16.0 发布:更可靠的图片格式协商与 CDN 缓存

各位好久不见!

过去的时间中我们一直在忙着推进 WebP Cloud 和 Public Services 内部基础设施的迭代和优化,少有文章发布,甚至一度有些朋友以为我们产品已经“凉了” 😆,好在我们没有,此外,现在终于有时间来更新一下 WebP Server Go 的进展了。

我们相信开源软件是自由的根基,我们在努力推进 SaaS 产品 WebP CloudPublic Services 的同时,也一直在维护和改进开源版本,让用户始终拥有更多的选择。

最近我们发布了 WebP Server Go 0.16.0。这个版本看上去只是在浏览器识别和响应头上做了一些调整,但它解决了一个伴随项目很久的问题:同一张图片应该如何可靠地输出为浏览器真正支持的格式,以及这些不同格式的响应应该如何被 CDN 正确缓存。

WebP Server Go 是什么

WebP Server Go 是一个采用 GPLv3 协议、代码托管在 https://github.com/webp-sh/webp_server_go 上的开源图片优化程序。

它可以在不修改原始图片 URL 的情况下,动态(On the fly)将 JPG、PNG 等图片优化为 WebP、AVIF 或 JPEG XL,并从浏览器支持的格式中选择体积更小的版本输出。

例如,网页中的图片地址依然可以是:

https://example.com/pics/photo.jpg

但是支持 AVIF 的浏览器可能收到 image/avif,支持 WebP 的浏览器可能收到 image/webp,不支持这些现代格式的客户端则进入原图回退分支。整个过程不需要提前批量处理图片,也不需要修改网页中的图片地址。

为什么我们要重新设计浏览器能力判断

过去 WebP Server Go 会同时参考 Accept 请求头和 User-Agent,尝试从浏览器名称、操作系统与版本号推测它支持 WebP、AVIF 还是 JPEG XL。

这种方式在早期可以覆盖一些浏览器没有完整发送 Accept 的场景,但随着浏览器版本和图片格式支持不断变化,User-Agent 推测也越来越容易出现偏差。

JPEG XL 是一个很典型的例子。Chrome 曾经移除实验性支持,Safari 随后正式支持,而最近 MozillaChromium 又分别推进了默认启用 JPEG XL 的工作。如果继续维护一套“某个浏览器从某个版本开始支持某种格式”的规则,我们就需要不断追赶浏览器的发布节奏,而且一旦判断错误,就可能向客户端发送一张它无法解码的图片。

事实上 HTTP 已经提供了专门解决这个问题的机制:客户端通过 Accept 明确告诉服务器自己能接受什么,服务器再通过 Content-Type 告诉客户端最终返回了什么。

所以从 0.16.0 开始,我们删除了基于 User-Agent 的图片格式推测,统一使用 Accept 请求头进行内容协商。

更严格地解析 Accept

移除 User-Agent 判断并不意味着简单搜索一下请求头中有没有 image/webpimage/avifimage/jxl 就结束了,因为 Accept 还包含质量值(q value)。

例如下面这个请求:

Accept: image/webp,image/jxl;q=0

其中 image/jxl;q=0 的意思不是“支持 JPEG XL 但优先级最低”,而是“JPEG XL 对这个请求不可接受”。根据 RFC 9110 的定义,质量值为 0 的内容会被认为不可接受。

旧版本只要看到字符串中包含 image/jxl 就可能把它判断为支持(我们被骗啦!)。

if strings.Contains(accept, "image/jxl") {
    supported["jxl"] = true
}

而 0.16.0 会解析完整的媒体类型和质量值,仅在格式被明确列出且 q>0 时启用对应的现代图片格式。

新的判断规则可以概括为:

  • image/webp:支持 WebP
  • image/avif;q=0.8:支持 AVIF
  • image/jxl;q=0:不支持 JPEG XL
  • image/**/*:不据此推断支持任何现代图片格式
  • 缺少或无法解析的 Accept:进入原图回退分支

我们有意不把通配符当作 WebP、AVIF 或 JPEG XL 支持信号。通配符在语义上可能接受许多内容,但它并不能证明客户端真的具备对应格式的解码能力;在不确定的时候返回原图,比乐观地发送一张无法显示的图片更安全。

例如,假设服务器启用了 AVIF/WebP 转换,并且转换后的 AVIF 体积最小:

curl -I \
  -H 'Accept: image/avif,image/webp,image/*;q=0.8' \
  http://127.0.0.1:3333/DSC05955.jpg

响应中会看到类似:

HTTP/1.1 200 OK
Content-Type: image/avif
Vary: Accept
X-Compression-Rate: 0.13

如果改成明确拒绝 JPEG XL:

curl -I \
  -H 'Accept: image/jxl;q=0,image/webp' \
  http://127.0.0.1:3333/DSC05955.jpg

WebP Server Go 就不会返回 image/jxl

Vary: Accept 与 CDN 缓存

内容协商解决了源站应该输出什么格式的问题,但前面如果还有一层 CDN,就会遇到另一个问题。

对于 CDN 来说,下面两个请求访问的是同一个 URL:

GET /pics/photo.jpg
Accept: image/avif,image/webp
GET /pics/photo.jpg
Accept: image/jpeg,image/png

如果 CDN 只使用 URL 作为缓存键,那么第一个访客触发并缓存的 AVIF 图片,就有可能被直接发送给后面不支持 AVIF 的访客。过去由于 WebP Server Go 没有完整表达这个响应差异,我们在文档中一直建议用户禁用 CDN 图片缓存,这虽然安全,但也放弃了 CDN 最重要的性能优势。

从 0.16.0 开始,我们决定解决这个问题,所有经过图片格式协商的响应都会带上:

Vary: Accept

这个响应头告诉共享缓存:同一个 URL 的响应会随着请求中的 Accept 而变化,不能把一个格式的缓存直接用于另一种能力不同的客户端。

不过需要注意,源站返回 Vary: Accept 并不代表所有 CDN 都会自动按它拆分缓存:

  • Cloudflare 需要在 Cache Rules 中启用 Vary,并建议将 Accept 归一化为 image/avifimage/webpimage/jxl 等实际使用的能力
  • Fastly 会按照 HTTP 规范处理 Vary,但建议对 Accept 做归一化,避免等价请求产生过多缓存条目
  • Amazon CloudFront 需要在 Cache Policy 中把 Accept 加入缓存键
  • 不支持 Vary 或自定义缓存键的 CDN,仍然应该对 WebP Server Go 的图片路径禁用缓存

我们已经重写了相关文档,具体配置与验证方法可以参考:WebP Server Go 与 CDN 配合使用

如果你使用了 widthheightmax_widthmax_height 等图片处理参数,也需要确保这些查询参数仍然包含在 CDN 缓存键中。启用新的变体缓存规则后,别忘了清理此前已经生成的旧缓存。

对现有用户有什么影响

对于正常通过 <img>、CSS 或其他图片上下文加载资源的现代浏览器,它们通常会在 Accept 中明确声明支持的图片格式,因此升级后不需要修改网页代码。

行为变化主要出现在以下场景:

  • 过去仅依赖 User-Agent 被识别为支持现代格式,但请求中没有明确声明对应格式
  • 使用只发送 Accept: */* 的脚本、爬虫或命令行客户端
  • 自建反向代理或 CDN 在转发时删除或重写了浏览器的 Accept 请求头

这些请求在 0.16.0 中会进入更保守的原图回退分支。如果你使用 cURL 或程序化客户端测试某种格式,请显式发送对应的 Accept,例如:

curl -I -H 'Accept: image/webp' https://example.com/pics/photo.jpg

这次升级没有新增必须修改的配置项,现有的 CONVERT_TYPES、质量设置和图片路径配置可以继续使用。同时,由于不再解析 User-Agent,我们也移除了 github.com/mileusna/useragent 依赖,让格式协商逻辑变得更简单、更容易测试。

如何升级

如果你使用 Docker Compose 部署,可以执行:

docker compose pull
docker compose up -d

升级后建议用不同的 Accept 请求头检查同一张图片,确认响应中包含 Vary: Accept,并根据你使用的 CDN 配置对应的缓存规则。

0.16.0 并没有加入一个醒目的图片处理功能,但它让 WebP Server Go 回到了更清晰的 HTTP 语义上:浏览器明确声明能力,源站选择输出格式,缓存也能够知道响应为什么不同。随着 JPEG XL 等格式逐渐进入更多浏览器,这套机制也比持续维护浏览器版本表更能适应未来的变化。

希望大家能喜欢这个版本。如果你在升级或配置 CDN 时遇到了问题,或者有什么新的想法,欢迎前往 Issues · webp-sh/webp_server_go 提交 Issue 反馈!


WebP Cloud Services 团队是一个来自上海和马尔默的三人小团队,由于我们不融资,且没有盈利压力 ,所以我们会坚持做我们认为正确的事情,力求在我们的资源和能力允许范围内尽量把事情做到最好, 同时也会在不影响对外提供的服务的情况下整更多的活,并在我们产品上实践各种新奇的东西。

如果你觉得我们的这个服务有意思或者对我们服务感兴趣,欢迎登录 WebP Cloud Dashboard 来体验,如果你好奇它还有哪些神奇的功能,可以来看看我们的文档 WebP Cloud Services Docs,希望大家玩的开心~


Discuss on Hacker News