本文约 4,371 字,预计阅读 11 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:图片被抄走,账单是你付
内容站最烦的一件事:文章被人整篇采集,图片直接 <img src="https://你的域名/..."> 引用。结果:
- 别人的页面加载你的图片,消耗的是你的带宽和 CDN 流量费;
- 采集站排名可能还比你高;
- 更恶劣的做成图床,被大量引用,一天跑掉几十 GB 流量。
防盗链(Hotlink Protection)就是解决这个问题的:只放行来自自己站点的图片请求,其他来源返回 403 或一张占位图。
原理分析:Referer 是怎么工作的
浏览器加载 <img> 时会带上 Referer 头,标明”这个请求是从哪个页面发起的”。
GET /wp-content/uploads/2025/01/demo.jpg HTTP/1.1
Host: mimangfei.com
Referer: https://mimangfei.com/some-article/
盗链场景下:
Referer: https://spam-site.com/copy-article/
服务器检查 Referer 是否在白名单里,不在就拒绝。这就是 Referer 防盗链。
但 Referer 有几个天然缺陷,必须知道:
- 可伪造:用 curl 随便加个
-e参数就能绕过。所以 Referer 防盗链只能防”浏览器直接引用”,防不了刻意盗用。 -
可能为空:
– 用户在地址栏直接打开图片 URL;
– HTTPS 页面引用 HTTP 图片(Referer 被降级策略剥离);
–Referrer-Policy: no-referrer;
– 部分浏览器隐私模式、部分邮件客户端。
所以必须放行空 Referer,否则正常访客也看不到图。 -
爬虫不带 Referer:搜索引擎图片抓取通常不带或有自己的标识,误封会影响收录。要把百度、Google 的爬虫 UA 或域名加白。
保姆级实操
方案一:Nginx 配置(valid_referers)
在站点的 server 块里加:
location ~* \.(jpg|jpeg|png|gif|webp|bmp|ico|svg)$ {
# 定义合法 Referer
valid_referers none blocked
server_names
*.mimangfei.com mimangfei.com
*.baidu.com *.baiducontent.com
*.google.com *.googleusercontent.com
*.bing.com
*.so.com *.sogou.com;
# 不在白名单内 → 返回 403
if ($invalid_referer) {
return 403;
}
expires 30d;
access_log off;
}
参数解释:
none:允许 Referer 为空(必加,否则地址栏直接访问图片会 403);blocked:允许 Referer 被防火墙/代理去掉了(不是完整 URL 的情况);server_names:允许 server_name 里定义的所有域名;- 后面的是显式白名单。
更友好的做法:返回占位图而不是 403
location ~* \.(jpg|jpeg|png|gif|webp)$ {
valid_referers none blocked server_names *.mimangfei.com;
if ($invalid_referer) {
# 注意:rewrite 后要用 break,避免循环
rewrite ^/.*$ /no-hotlink.png break;
}
expires 30d;
}
准备一张 /no-hotlink.png(可以写”图片来自 mimangfei.com”),这样盗链者的页面上会出现你的水印图,比 403 更有宣传效果。
针对特定目录(只保护 uploads)
location ^~ /wp-content/uploads/ {
valid_referers none blocked server_names *.mimangfei.com;
if ($invalid_referer) {
return 403;
}
expires 365d;
access_log off;
}
配置后重载:
sudo nginx -t && sudo systemctl reload nginx
验证:
# 正常 Referer → 200
curl -I -e "https://mimangfei.com/" https://mimangfei.com/wp-content/uploads/2025/01/a.jpg
# 盗链 Referer → 403
curl -I -e "https://spam.com/" https://mimangfei.com/wp-content/uploads/2025/01/a.jpg
# 空 Referer → 200(因为配了 none)
curl -I https://mimangfei.com/wp-content/uploads/2025/01/a.jpg
方案二:CDN 控制台配置(推荐,更省事)
主流 CDN 都有”防盗链”或”Referer 防盗链”功能:
- 阿里云 CDN:域名管理 → 访问控制 → Referer 防盗链 → 白名单,勾上”允许空 Referer”;
- 腾讯云 CDN:域名管理 → 访问控制 → Referer 配置;
- Cloudflare:免费版没有原生 Referer 防盗链,需要用 WAF 规则或 Firewall Rules:
(http.request.uri.path contains "/wp-content/uploads/")
and (not http.referer contains "mimangfei.com")
→ Block
- 又拍云 / 七牛:控制台都有对应开关。
CDN 层的优势:拦在边缘节点,不消耗源站资源,配置也不需要动服务器。
方案三:签名鉴权(更可靠,防伪造)
如果图片价值高(付费课程配图、素材站),Referer 不够用,要用 URL 签名 / 鉴权:
- 阿里云 CDN:URL 鉴权(A/B/C 三种方式);
- 腾讯云:鉴权配置 TypeA/TypeB/TypeC;
- 原理:URL 带一个
?auth_key=timestamp-rand-md5hash参数,服务端校验签名和有效期,过期或篡改即拒绝; - 代价:需要在 WordPress 里动态生成签名 URL,缓存时间不能长于签名有效期。
WordPress 侧生成签名 URL 的例子(阿里云 TypeA):
function mmf_sign_url( $url, $ttl = 3600 ) {
$key = '你的鉴权key';
$path = parse_url( $url, PHP_URL_PATH );
$timestamp = time() + $ttl;
$rand = '0';
$raw = $path . '-' . $timestamp . '-' . $rand . '-0-' . $key;
$hash = md5( $raw );
return $url . '?auth_key=' . $timestamp . '-' . $rand . '-0-' . $hash;
}
方案四:用插件(最省事但不推荐)
有一些”防盗链”WordPress 插件,本质是通过 .htaccess 或 PHP 输出图片。用 PHP 输出图片性能极差(每次都要启动 WordPress),除非流量极小,否则不建议。
常见踩坑
- 没加
none导致正常访客看不到图:最常见的错误。地址栏直接访问、微信内置浏览器、部分 App 内嵌 WebView 都可能不带 Referer。 - 误封搜索引擎:百度图片、Google 图片的抓取可能不带 Referer,或带上自己的域名。误封会导致图片无法被收录。一定要把搜索引擎域名加白名单,并去百度站长平台的”抓取诊断”里验证。
- HTTPS 页面引用 HTTP 图:Referer 被浏览器剥离,变成空。这就是必须放行
none的又一个原因。 - 案例:某站配置了防盗链后,微信里分享的文章图片全部裂开。排查发现微信的 Referer 是
https://mp.weixin.qq.com/,不在白名单。加上*.weixin.qq.com后解决。社会化分享平台(微信、微博、QQ、知乎)都要加白。 if ($invalid_referer)里的 rewrite 死循环:rewrite ^/.*$ /no-hotlink.png会再次匹配到同一个 location。必须加break,或者把占位图放到不被保护的路径。- CDN 缓存了 403 响应:如果 CDN 把 403 也缓存了,白名单调整后要刷新 CDN。
- RSS 阅读器 / 邮件订阅里的图片:这些场景通常无 Referer,放行
none即可覆盖。
总结
防盗链的落地顺序:先用 CDN 控制台的 Referer 白名单(最省事、拦在边缘),必勾”允许空 Referer”,把搜索引擎和社交平台域名加白;源站 Nginx 的 valid_referers 作为第二道防线;图片真正值钱再上 URL 签名鉴权。记住 Referer 防盗链只能防君子,图片上打水印(品牌露出)才是长期有效的手段。防盗链省下的流量费,配合 WordPress CDN 回源配置与缓存规则 的命中率优化,能把 CDN 账单压下一大截。
