Caddy2 支持 WebP 和 AVIF

一、方案选型

目前对于 WebP 和 AVIF 格式支持大致有两种方案, 一种是动态转换, 另一种是静态转换.

  • 动态转换: 即在流量的代理层进行处理, 实现对用户的透明化, 用户无需进行任何更改, 由负载均衡器或者中间件进行动态转换处理.
  • 静态转换: 通过工具预先转换好, 然后通过请求匹配分析来选择返回的图片格式.

二、动态转换

懒得放代码了, 自己搓到一半放弃了.

对于 WebP 格式来说, 在 Caddy2 上动态转换比较简单, 基本上就是添加一个插件即可; 例如 caddy-webp 这个插件. 有关于 Caddy 的插件开发可以参考官方的 Extending Caddy 文档.

动态转换的核心思想是在接收到请求后, 判断请求中的 Accept 头, 如果包含 image/webp 则说明浏览器可以识别 WebP 格式图片(AVIF 同理); 此时可以将请求先转发给后续的 HTTP 处理逻辑, 待返回响应后将其暂时 Cache 住, 然后执行转换逻辑, 改变其格式后重新设置 Content-Type 头然后把新的格式数据返回给前端.

针对这种操作可以在负载均衡器层(例如 Caddy2 Plugin)完成, 也可以通过单独的中间件完成; 但无论哪种其涉及到一些缺陷(可优化):

  • 每次响应需要先完成 load 到内存进行转换(内存消耗大)
  • 转换过程会浪费 CPU 资源(CPU 一样消耗很大)
  • 转换过程可能导致请求失败

针对这些情况, 个人认为在生产环境资源充足的情况下完全可以解决, 核心思想还是一个: 缓存为王; 想办法在第一次转换后将其存储到缓存位置, 避免每次都转换; 如果资源足够甚至可以考虑内存级的缓存方案配合 LRU 等失效策略.

三、静态转换

经过一波搓代码从入门到放弃, 最终我选择了目前我这个小破站能承担得起的方案; 即先在本地执行转换然后直接扔到服务器上; 由于网站本身就是静态站点, 所以可以在 Caddyfile 中基于动态转换的套路处理请求头返回不同文件.

本地转换目前采用 optimizt 这个工具完成:

# 安装
npm i -g optimizt

# 转换
optimizt --force --webp --avif public/img

这样在我的图片目录下就会生成同名的 WebP 和 AVIF 格式图片

最后只需要在 Caddyfile 中增加以下请求头匹配的配置即可:

# avif support
@acceptsAVIF {
    header Accept *image/avif*
    path_regexp avif ^(.+)\.(jpg|jpeg|png)$
}
handle @acceptsAVIF {
    @hasAVIF file {re.avif.1}.avif
    rewrite @hasAVIF {re.avif.1}.avif
}

# webp support
@acceptsWebp {
    header Accept *image/webp*
    path_regexp webp ^(.+)\.(jpg|jpeg|png)$
}
handle @acceptsWebp {
    @hasWebp file {re.webp.1}.webp
    rewrite @hasWebp {re.webp.1}.webp
}

四、总结

动态转换比价适合商业化模式, 在资源支撑足够的情况下可以延迟随机、预热、高性能缓存等方式配合起来将图片透明转换完成, 对用户无感且友好. 小破站资源匮乏等情况比较适合静态转换完后逻辑匹配规则返回不同物理文件. 说实话我想弄成高大上的动态转换写点代码的, 但是弄到一半发现全是问题又没资源解决, 只能在打打嘴炮了.


Caddy2 支持 WebP 和 AVIF
https://mritd.com/2022/03/24/caddy2-support-webp/
作者
bleem
发布于
2022年3月24日
许可协议