本文约 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;
}
三个必须知道的局限:
Referer可以伪造。对普通盗链者有效,对有意绕过的人无效(用 curl 加个 Referer 就过了)。所以它防的是”顺手盗用”,不是”蓄意盗用”。- 直接访问和部分场景没有 Referer。要放行
none(比如用户直接打开图片 URL、某些 App、邮件客户端)。 - 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,盗链站点上显示的就是你的宣传图——这既是防护也是引流。
常见踩坑与失败案例
- 案例一:配了防盗链后,百度图片搜索里的图全裂,图片流量掉光。原因是没把
*.baidu.com加白名单。搜索引擎和 RSS 阅读器必须放行。 - 案例二:微信内置浏览器打开页面图片全裂。微信的 Referer 有时为空或被改写,需要放行
none和*.weixin.qq.com。 valid_referers none没加:手机 App、邮件客户端、直接访问图片全部 403。- 防盗链规则写在了错误的 location:Nginx 的 location 优先级是”精确 > 前缀 > 正则”,如果有更精确的 location 先匹配了,防盗链规则不生效。用
nginx -T看完整配置顺序。 - 案例三:站点用了对象存储(OSS/COS),防盗链配在 Nginx 上完全无效——图片请求根本不经过你的服务器。必须去对象存储控制台配 Referer 白名单。
- HTTPS 站点 Referer 策略影响:如果页面设了
Referrer-Policy: no-referrer,自己站点的图片请求也不带 Referer。用strict-origin-when-cross-origin(推荐)而不是no-referrer。 - 403 返回给搜索引擎爬虫:百度图片抓取会失败,导致图片不收录。宁可返回提示图,也不要直接 403。
总结
防盗链的务实顺序:优先在 CDN / 对象存储层做(请求不回源,省的是真金白银),Nginx/Apache 层作为兜底(适合没上 CDN 的站点),付费内容用签名 URL,内容版权靠水印。配置时三个必加白名单:none(直接访问)、搜索引擎域名(baidu/google/bing,影响图片收录)、RSS 阅读器域名(feedly/inoreader)。不要直接返回 403,返回一张带站点信息的提示图更友好,还能反向引流。最后注意:如果图片已经上了 OSS/COS,防盗链必须去云厂商控制台配,配在 Nginx 上毫无作用。
