搜索全站
性能优化 12 分钟阅读

WordPress 图片防盗链:Nginx、Apache 与 CDN 三层配置方案

别人直接引用你的图片 URL,消耗的是你的带宽。本文讲清 Referer 防盗链的原理与局限、Nginx/Apache 配置代码、CDN 层(阿里云/腾讯云/Cloudflare)的防盗链设置、以及水印和签名 URL 两种更可靠的方案。

U
UFO_PF 2019-09-23 · 阅读 12 分钟
Wo 性能优化
阅读提示
本文约 4,473 字,预计阅读 12 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:流量费暴涨,一半是被盗图

某站月流量突然从 30GB 涨到 300GB,服务器账单翻了十倍。查访问日志发现,大量图片请求的 Referer 是几个陌生站点——对方在文章里直接用了 <img src="https://你的站点/uploads/xxx.jpg">。

图片被”盗链”(hotlinking)的代价很实际:带宽费、CDN 流量费、服务器负载,全由你承担,而流量带来的是别人的页面浏览。

防盗链的几种方案各有适用场景,选错会有副作用(比如 RSS 阅读器里的图片全裂)。

原理分析:Referer 防盗链的原理与三个局限

浏览器请求图片时,会在 HTTP 头里带上 Referer,表示”我是从哪个页面发起这个请求的”。

防盗链逻辑:如果 Referer 不在白名单里,就拒绝返回图片。

valid_referers none blocked server_names *.example.com example.com;
if ($invalid_referer) {
    return 403;
}

三个必须知道的局限:

  1. Referer 可以伪造。对普通盗链者有效,对有意绕过的人无效(用 curl 加个 Referer 就过了)。所以它防的是”顺手盗用”,不是”蓄意盗用”。
  2. 直接访问和部分场景没有 Referer。要放行 none(比如用户直接打开图片 URL、某些 App、邮件客户端)。
  3. HTTPS → HTTP 会丢失 Referer,而 HTTP → HTTPS 也会。混用协议时容易误伤。

所以防盗链要留后路:要么放行 none,要么给被盗链的图片返回一张”提示图”而不是 403,避免误伤正常场景(尤其是 RSS 订阅和邮件)。

保姆级实操

方案一:Nginx Referer 防盗链

location ~* \.(gif|jpg|jpeg|png|webp|bmp|swf|flv|mp4)$ {
    valid_referers none blocked server_names
                   *.example.com example.com
                   *.baidu.com *.google.com *.bing.com
                   *.feedly.com *.zhihu.com *.weibo.com;

    if ($invalid_referer) {
        # 方式 A:直接拒绝
        # return 403;

        # 方式 B:返回提示图(更友好)
        rewrite ^/ https://example.com/wp-content/uploads/nohotlink.png last;
    }

    expires 30d;
    access_log off;
}

valid_referers 参数说明:

  • none:允许没有 Referer 的请求;
  • blocked:允许 Referer 被防火墙或代理去掉的请求(如 Referer: http://);
  • server_names:允许当前 server 块里的 server_name;
  • 后面可以跟域名,支持 *.example.com 通配。

搜索引擎和 RSS 阅读器务必加白名单,否则:

  • 百度图片搜索抓取不到你的图(影响图片收录);
  • 百度快照、Google 图片搜索结果里图片裂开。

方案二:Apache(.htaccess)

RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?example\.com/ [NC]
RewriteCond %{HTTP_REFERER} !^https?://(.*\.)?baidu\.com/ [NC]
RewriteCond %{HTTP_REFERER} !^https?://(.*\.)?google\.com/ [NC]
RewriteCond %{HTTP_REFERER} !^https?://(.*\.)?feedly\.com/ [NC]
RewriteRule \.(gif|jpg|jpeg|png|webp)$ https://example.com/nohotlink.png [R=302,L]

方案三:CDN 层防盗链(推荐,不消耗源站资源)

阿里云 CDN:控制台 → 域名管理 → 访问控制 → Referer 防盗链 → 开启,填白名单域名,勾选”允许空 Referer”。

腾讯云 CDN:域名管理 → 访问控制 → Referer 配置 → 黑白名单。

Cloudflare:WAF → 自定义规则:

(http.request.uri.path contains "/wp-content/uploads/")
and (not http.referer contains "example.com")
and (http.referer ne "")
→ 阻断

CDN 层做的好处是请求根本不回源,节省的是源站带宽和 CDN 流量费。

方案四:签名 URL(更可靠,适合付费内容)

CDN 鉴权(阿里云叫”URL 鉴权”,腾讯云叫”鉴权配置”)会给图片 URL 加一个带时间戳和 MD5 签名的参数:

https://cdn.example.com/uploads/a.jpg?auth_key=1739123456-0-0-abcdef123456

过期后 URL 失效。缺点是需要插件配合生成签名 URL,WordPress 侧要装对应 CDN 的插件(如 WPJAM、CDN Enabler 的鉴权版本)。

适合:付费课程图片、付费下载资源、会员专属图片。

方案五:水印(最朴素也最有效)

防盗链防的是带宽消耗,水印防的是”内容被盗用”。图片上传时自动加水印:

装插件 “Easy Watermark”,或用代码在上传时自动叠加:

add_filter( 'wp_handle_upload', function( $file ) {
    if ( false === strpos( $file['type'], 'image/' ) ) {
        return $file;
    }
    $editor = wp_get_image_editor( $file['file'] );
    if ( is_wp_error( $editor ) ) {
        return $file;
    }
    // 右下角叠加水印(需要 GD 或 Imagick)
    $watermark = get_template_directory() . '/assets/images/watermark.png';
    if ( file_exists( $watermark ) ) {
        $size = $editor->get_size();
        $editor->image_default_size = array( $size['width'], $size['height'] );
        // WP_Image_Editor 没有内置水印 API,需要用 GD 直接处理
    }
    return $file;
});

实际上 WordPress 的 WP_Image_Editor 没有水印 API,纯代码做水印比较麻烦,推荐用插件(Easy Watermark、Watermark my images)。

方案六:按 Referer 替换图片(对盗链站点”反击”)

location ~* \.(jpg|jpeg|png)$ {
    valid_referers none blocked server_names *.example.com;
    if ($invalid_referer) {
        rewrite ^/ https://example.com/wp-content/uploads/nohotlink.jpg last;
    }
}

做一张写着”本图片来自 mimangfei.com”的 nohotlink.jpg,盗链站点上显示的就是你的宣传图——这既是防护也是引流。

常见踩坑与失败案例

  1. 案例一:配了防盗链后,百度图片搜索里的图全裂,图片流量掉光。原因是没把 *.baidu.com 加白名单。搜索引擎和 RSS 阅读器必须放行。
  2. 案例二:微信内置浏览器打开页面图片全裂。微信的 Referer 有时为空或被改写,需要放行 none 和 *.weixin.qq.com。
  3. valid_referers none 没加:手机 App、邮件客户端、直接访问图片全部 403。
  4. 防盗链规则写在了错误的 location:Nginx 的 location 优先级是”精确 > 前缀 > 正则”,如果有更精确的 location 先匹配了,防盗链规则不生效。用 nginx -T 看完整配置顺序。
  5. 案例三:站点用了对象存储(OSS/COS),防盗链配在 Nginx 上完全无效——图片请求根本不经过你的服务器。必须去对象存储控制台配 Referer 白名单。
  6. HTTPS 站点 Referer 策略影响:如果页面设了 Referrer-Policy: no-referrer,自己站点的图片请求也不带 Referer。用 strict-origin-when-cross-origin(推荐)而不是 no-referrer。
  7. 403 返回给搜索引擎爬虫:百度图片抓取会失败,导致图片不收录。宁可返回提示图,也不要直接 403。

总结

防盗链的务实顺序:优先在 CDN / 对象存储层做(请求不回源,省的是真金白银),Nginx/Apache 层作为兜底(适合没上 CDN 的站点),付费内容用签名 URL,内容版权靠水印。配置时三个必加白名单:none(直接访问)、搜索引擎域名(baidu/google/bing,影响图片收录)、RSS 阅读器域名(feedly/inoreader)。不要直接返回 403,返回一张带站点信息的提示图更友好,还能反向引流。最后注意:如果图片已经上了 OSS/COS,防盗链必须去云厂商控制台配,配在 Nginx 上毫无作用。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。