511遇见全站替换WebP实战记录教程

前言:为什么要折腾 WebP

WebP 是 Google 推出的图片格式,同样画质下体积比 JPEG 小 25%~35%,比 PNG 小 50%~70%。

对网站来说,图片体积直接决定两件事:页面打开速度和服务器带宽消耗。一个图片占大头的站点,全站换 WebP 之后,流量能省掉三分之一到一半。

但WordPress 站点动辄上千篇文章,正文里的 <img src="xxx.jpg"> 早就写死了。一篇篇去改链接,显然不现实。

本文记录的方法是:服务器上多存一份 .webp 文件,由 Apache 在响应时自动替换,网页代码一个字不改。

一、核心原理:URL 只是门牌号,服务器说了算

很多人不理解"链接不改,怎么能返回另一种格式"。关键在于一个容易被忽略的事实:

浏览器判断图片格式,靠的是响应头里的 Content-Type,不是 URL 后缀。

后缀(.jpg / .png)只是给人看的,MIME 类型才是给机器看的。

完整流程

  1. 浏览器读到 <img src="/images/demo.jpg">,发起请求,请求头里带上 Accept: image/webp,image/*(等于自我介绍"我支持 webp")
  2. Apache 收到请求,逐条检查重写规则
  3. 发现浏览器支持 webp,且磁盘上确实存在 demo.jpg.webp
  4. 内部重写:原本要读 demo.jpg,实际去读 demo.jpg.webp
  5. 同时把响应头改成 Content-Type: image/webp
  6. 浏览器看到 Content-Type 是 webp,用 webp 解码器正常显示 —— 它从头到尾不知道 URL 后缀是 .jpg

这里的第 4 步是内部重写,不是 301 跳转,所以浏览器完全无感知,地址栏也不会变。

二、准备工作:下载 cwebp 转换工具

cwebp 是 Google 官方提供的免费命令行工具,负责把 jpg/png 转成 webp。

下载地址

https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.6.0-windows-x64.zip

如果官方地址下载慢,可以访问版本列表页选择其他版本:

https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html

安装建议

解压之后,把 bin 目录里的 cwebp.exe 单独复制到一个短路径,例如:

C:\cwebp\cwebp.exe

这样做的好处是后面写批处理脚本时路径短,不容易出错。

bin 目录里还有很多其他 exe(dwebp、gif2webp、webpmux 等),它们都用不到,只需要 cwebp 一个。

验证安装

C:\cwebp\cwebp.exe -version

能显示版本号即安装成功。注意 cwebp 是命令行工具,双击没有反应是正常的,必须在 CMD 里执行。

三、批量转换:只在原目录新增文件

这一步的目标:把每个 demo.jpg 转成 demo.jpg.webp,放在同一个目录。

为什么要命名为 .jpg.webp 而不是 .webp

因为 Apache 拼接路径时只需要做最简单的操作:

请求的 URL + ".webp" = webp 文件路径

写成 demo.jpg.webp,规则里直接追加后缀即可。如果写成 demo.webp,Apache 还得先把 .jpg 砍掉再换,规则复杂且容易出错。

转换脚本

保存为 convert.bat,双击运行:

@echo off
set CWEBP=C:\cwebp\cwebp.exe
set OPT=-q 80 -alpha_q 100 -mt

for /R "D:\webroot\wp-content\uploads" %%f in (*.jpg *.jpeg *.png) do (
    if not exist "%%f.webp" (
        "%CWEBP%" %OPT% "%%f" -o "%%f.webp" >nul 2>&1
        echo   ok %%~nxf
    )
)
echo ===== 全部完成 =====
pause

参数说明

参数 作用
-q 80 画质 80,肉眼几乎无差别,体积控制最佳。建议 75~85 之间
-alpha_q 100 透明通道无损,避免 PNG 转完后边缘出现毛边
-mt 多线程,加快转换速度
if not exist 已转换的自动跳过,脚本可以重复运行,中断后重跑无副作用

安全性提示:脚本只新增 .webp 文件,原有的 jpg/png 一张都不会删除,随时可以删掉 webp 回滚。

注意:如果服务器装了防篡改类安全软件,需要先把图片目录加入例外名单,否则转换出来的文件写不进去。

四、配置 Apache 自动改写

这是最关键的一步。规则要放在图片所在目录的 .htaccess里。

<IfModule mod_mime.c>
    AddType image/webp .webp
</IfModule>

<IfModule mod_rewrite.c>
    RewriteEngine On

    # 条件1:浏览器支持 webp
    RewriteCond %{HTTP_ACCEPT} image/webp
    # 条件2:对应的 .webp 文件确实存在
    RewriteCond %{REQUEST_FILENAME}.webp -f
    # 满足则内部重写,并强制 MIME 为 image/webp
    RewriteRule ^(.+\.(jpe?g|png))$ $1.webp [NC,T=image/webp,L]
</IfModule>

# 必须:同一 URL 可能返回不同格式,按 Accept 分开缓存
<IfModule mod_headers.c>
    <FilesMatch "\.(jpe?g|png)$">
        Header append Vary Accept
    </FilesMatch>
</IfModule>

Vary: Accept 为什么不能省

这是最容易漏掉、后果又最隐蔽的一条。

同一个 URL /images/demo.jpg:

  • 支持 webp 的浏览器访问 → 服务器返回 webp → 中途某层缓存记住"这个 URL = webp"
  • 不支持 webp 的老浏览器访问 → 缓存直接给它 webp → 图片裂开

Vary: Accept 就是告诉所有缓存:这个 URL 返回什么取决于 Accept 头,请按 Accept 分开存。

兼容性说明

不支持 webp 的浏览器发的 Accept 头里没有 image/webp,条件 1 不满足,规则不执行,自动返回原图。这正是原图必须保留的原因 —— 它是降级方案。

五、验证:三条命令定生死

验证时必须模拟浏览器的 Accept 头,否则测不出真实效果。

curl -s -D - -o nul -H "Referer: https://www.example.com/" -H "Accept: image/webp,*/*" https://www.example.com/images/demo.jpg | findstr /i "content-type"

期望结果:

Content-Type: image/webp

再测降级(模拟不支持 webp 的老浏览器):

curl -s -D - -o nul -H "Referer: https://www.example.com/" -H "Accept: image/jpeg,*/*" https://www.example.com/images/demo.jpg | findstr /i "content-type"

期望结果:Content-Type: image/jpeg —— 说明自动降级正常。

六、三个真实踩坑记录

下面这三条是实操中真正卡住的地方,网上教程基本不会提。

坑一:防盗链导致 curl 测出假结果

如果站点开了防盗链,curl 不带 Referer 头时,服务器会返回 HTML 提示页,状态码还是 200,但 Content-Type 是 text/html。

现象:看起来"请求成功了",但返回的不是图片,让人误以为 WebP 没生效、白白折腾半天。

结论:测试图片务必带上 -H "Referer: https://你自己的域名/"。

坑二:子目录写了 RewriteEngine On,父目录规则不再继承

这是 Apache 的一个默认机制:per-directory 上下文中,子目录的 .htaccess 一旦写了 RewriteEngine On,父目录的 rewrite 规则在该子目录就不会生效(除非显式写 RewriteOptions Inherit)。

典型症状:把 WebP 规则写在网站根目录,图片目录里因为有其他规则(比如防盗链)写了 RewriteEngine On,结果根目录规则被"顶掉",规则明明写了却毫无效果。

解决办法(二选一):

  • 在子目录的 rewrite 块里加一行 RewriteOptions Inherit
  • 或者干脆把 WebP 规则分别写进每个图片目录自己的 .htaccess(本文推荐,排查时不用到处找)

坑三:只生成了 .webp 文件,不等于改写生效

直接访问 demo.jpg.webp 能在浏览器里看到图,这只能证明文件生成成功了。

真正的成功标志是:访问 .jpg 原地址,返回的却是 image/webp。

这两件事完全不同,前者是转换,后者是改写,缺一不可。

七、以后上传新图片怎么办

WebP 是预先生成的文件,新上传的图片不会自动有 .webp。有三个方案:

方案 A:手动重跑脚本

每次批量传图后双击运行一次 convert.bat,if not exist 会跳过已转换的,只处理新图。最简单,但靠自觉。

方案 B:WordPress 自动生成(推荐)

在主题的 functions.php 末尾加入:

// 上传图片时自动生成 WebP
add_filter('wp_generate_attachment_metadata', 'auto_make_webp', 10, 2);
function auto_make_webp($metadata, $attachment_id) {
    $file = get_attached_file($attachment_id);
    if (!$file || !preg_match('/\.(jpe?g|png)$/i', $file)) return $metadata;

    $webp = $file . '.webp';
    if (file_exists($webp)) return $metadata;

    @exec('C:\cwebp\cwebp.exe -q 80 -alpha_q 100 -mt '
          . escapeshellarg($file) . ' -o ' . escapeshellarg($webp));

    return $metadata;
}

上传一张转一张,永不遗忘。WordPress 生成的多个尺寸缩略图,这个钩子对每个尺寸都会触发。

方案 C:Windows 计划任务

设定每周自动跑一次 convert.bat,适合不常更新的站点。

八、收尾工作

  1. 确认网站正常:改完 .htaccess 立刻访问首页,返回 200 才对。如果是 500,说明有语法错误,马上恢复备份
  2. 清理缓存:缓存插件、Redis 各清一次,浏览器 Ctrl+F5 强制刷新
  3. 安全软件例外保留:图片目录的写入例外不要撤掉,以后还要传图、还要生成 webp
  4. 改 .htaccess 不需要重启 Apache,保存即时生效

总结

整套方案的核心价值在于零侵入:

  • 文章里的图片链接一个字不用改
  • 原图全部保留,随时可回滚
  • 不支持 webp 的浏览器自动降级
  • 搜索引擎爬虫拿到的是原图(它们通常不带 image/webp),SEO 完全不受影响

唯一需要动手的,就是转换文件 + 写一条 rewrite 规则。搞清楚原理之后,整个配置过程半小时以内可以完成。


发布日期:

所属分类: Wordpress 综合 标签:



下一篇:

没有了,已经是最新文章