Referer防盗链需严格校验白名单域名,解析URL提取host后精确匹配;空或无效Referer应拒绝;响应前禁止输出内容,用header+exit或readfile输出提示图;伪静态规则仅转发请求,核心逻辑在PHP脚本中实现。

Referer为空或不匹配时直接拒绝访问
PHP处理图片防盗链,核心逻辑是检查 $_SERVER['HTTP_REFERER'] 是否来自白名单域名。但要注意:部分浏览器、HTTPS跳转、隐私模式下 Referer 可能为空;移动端 App 内嵌 WebView 也可能不带 Referer。不能只做“包含”判断,更不能用 strpos() 粗暴匹配子串(比如匹配 'example.com' 会误放行 badexample.com)。
推荐做法:
- 先用
filter_var()解析 Referer URL,提取 host 部分,避免协议/路径干扰 - 用
in_array($host, $white_list, true)严格比对完整域名 - 若
$_SERVER['HTTP_REFERER']为空或解析失败,按业务策略决定是否放行(多数场景应拒绝) - 注意 Apache + PHP-FPM 下,
HTTP_REFERER可能被重写模块过滤,需确认mod_headers未屏蔽该头
用 header() 重定向到 403 或自定义提示图
防盗链不是隐藏图片路径,而是控制响应行为。常见错误是 echo 一段文字后试图 header 重定向——PHP 已输出内容,header 失效,浏览器仍会加载原图。
关键操作顺序:
立即学习“PHP免费学习笔记(深入)”;
- 在读取任何图片文件前,完成 Referer 判断和响应决策
- 若拒绝,立即调用
header('HTTP/1.1 403 Forbidden'),再exit(不要 echo) - 若要显示“禁止盗链”图片,用
readfile('/path/to/forbidden.png')输出二进制内容,并配对设置header('Content-Type: image/png') - 避免使用
header('Location: ...')跳转到提示图——这仍算一次有效请求,且可能被缓存或绕过
伪静态规则必须配合 PHP 脚本接管图片请求
Apache 的 RewriteRule ^img/(.*)$ /check.php?file=$1 [L] 这类规则本身不防盗链,只是把请求转发给 PHP。真正起作用的是 check.php 中的 Referer 判断逻辑。
容易出问题的地方:
-
.htaccess中 RewriteRule 必须加[L]终止后续规则,否则可能被其他规则覆盖 - Nginx 用户需用
try_files $uri @php将未命中物理文件的图片请求交由 PHP 处理,不能只依赖location ~ \.png$直接返回静态文件 - PHP 脚本中获取原始请求路径要用
$_GET['file'](伪静态传参),而不是$_SERVER['REQUEST_URI'](含 query string,易被篡改) - 务必对
$_GET['file']做白名单校验或路径规范化(如realpath()+str_starts_with()),防止../../etc/passwd类路径遍历
CDN 和代理环境下 Referer 可能被污染或丢失
如果网站用了 CDN(如 Cloudflare、阿里云 DCDN),真实 Referer 往往被覆盖为 CDN 节点 IP 或域名,$_SERVER['HTTP_REFERER'] 不再可靠。此时不能直接依赖它做判断。
可行方案:
- 开启 CDN 的 “传递原始 Referer” 功能(Cloudflare 叫
Origin Header,阿里云叫透传 Referer),并确认 PHP 能收到HTTP_X_ORIGINAL_REFERER或类似头 - 改用 Token 验证:前端请求图片时,服务端生成带时效签名的 URL(如
/img/photo.jpg?t=171xxxx&sign=abc123),PHP 校验签名和时间戳,彻底脱离 Referer - 对高敏感资源,放弃 Referer 方案,改用登录态 Session 校验或 JWT 鉴权,图片接口走动态路由
Referer 防盗链本质是“尽力而为”,它防不住主动构造请求的爬虫或调试工具。真正需要强保护的图片,应该从资源分发方式上重构,而不是在 PHP 层反复修补判断逻辑。



















