本文目录导读:

在PHP项目中配置防盗链,主要目的是防止其他网站直接引用你的图片、CSS、JS等静态资源,从而节省带宽和服务器资源。
通常有两种主流方式:服务器层面(推荐,最有效) 和 PHP代码层面。
服务器层面配置(最高效、最推荐)
这种方式在资源被发送给用户之前就被拦截了,性能开销最小。
Nginx 配置防盗链
Nginx 通过 valid_referers 指令实现,在对应的 server 块或 location 块中添加:
server {
listen 80;
server_name yourdomain.com;
# 对图片、CSS、JS等静态资源进行防盗链
location ~* \.(gif|jpg|jpeg|png|webp|bmp|ico|svg|css|js|woff|ttf|mp4|zip|rar)$ {
# 允许的来源域名列表(务必包含你自己的域名)
valid_referers none blocked server_names
*.yourdomain.com # 允许所有子域名
yourdomain.com
*.trusted-partner.com; # 允许合作伙伴域名
if ($invalid_referer) {
# 如果来源不在白名单中,返回 403 或重定向到指定图片
return 403;
# 或者重定向到你设置的防盗链图片(注意:这个图片不能在防盗链范围内)
# rewrite ^/.*$ /path/to/deny.jpg break;
}
}
# 或者更简单的写法(推荐,逻辑更清晰)
location /uploads/ {
valid_referers none blocked server_names *.yourdomain.com yourdomain.com;
if ($invalid_referer) {
return 403;
}
}
}
配置解释:
none:允许直接通过浏览器地址栏访问(Referer 为空)。blocked:允许 Referer 被隐藏的请求(如某些防火墙)。server_names:允许来自本服务器配置的所有域名。$invalid_referer:如果来源不匹配,则为 true。
生效后需重启/重载 Nginx:
nginx -s reload
Apache 配置防盗链
Apache 使用 mod_rewrite 模块,在 .htaccess 文件或虚拟主机配置中添加:
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourdomain.com [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourpartner.com [NC]
RewriteRule \.(jpg|jpeg|png|gif|bmp|webp|css|js)$ - [F,NC,L]
# 或者返回一张指定的防盗链图(该图片不能在规则范围内)
# RewriteRule \.(jpg|jpeg|png|gif)$ /path/to/deny.jpg [R,L]
注意:需要确保 Apache 开启了 mod_rewrite 且目录允许覆盖(AllowOverride All)。
PHP 代码层面配置(适合无法控制服务器的场景)
如果你的项目托管在虚拟主机或无法修改服务器配置(如 SAE、BAE),可以在 PHP 入口文件(如 index.php)或具体下载文件的 PHP 脚本中处理。
代码示例:通过读取 HTTP_REFERER 头判断
<?php
// 放在需要防盗链的图片/文件处理脚本的最开头,或者全局入口文件
function checkReferer() {
// 允许的域名列表(务必包含你自己的所有域名,不含 http://)
$allowed_domains = [
'yourdomain.com',
'www.yourdomain.com',
'cdn.yourdomain.com',
'localhost', // 本地测试
];
// 获取来源 Referer
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
// 情况1:没有来源(直接访问,例如手机端或命令行直接下载)
// 如果要求必须携带来源才能访问,可以返回 false,但会导致直接访问被拒
// 如果需要允许直接访问,解除下面这行的注释(配合图片防盗链通常建议允许直接访问)
// if (empty($referer)) return true;
$allowed = false;
if (!empty($referer)) {
// 解析 Referer 的域名部分(只保留主机名)
$referer_host = parse_url($referer, PHP_URL_HOST);
foreach ($allowed_domains as $domain) {
// 检查是否完全匹配或属于子域名
if ($referer_host === $domain ||
stripos($referer_host, '.' . $domain) !== false) {
$allowed = true;
break;
}
}
} else {
// Referer 为空:根据业务需要决定
// 某些爬虫或软件不发送 Referer,通常建议返回 true 允许直接访问
$allowed = true;
}
if (!$allowed) {
// 阻止访问:显示 403 或跳转到一张警告图片
header('HTTP/1.1 403 Forbidden');
// 可选:返回一张指定的防盗链图片(这张图片必须可以被直接访问)
// header('Location: http://yourdomain.com/deny.jpg');
// 或者直接输出一段文字
die('Access Denied: Hotlinking is not allowed.');
}
}
// 在文件下载脚本或图片展示页面调用
checkReferer();
// --- 以下代码是你的正常业务逻辑 ---
//
// $file = $_GET['file'];
// readfile($file);
注意事项(重要)
- HTTP_REFERER 不可伪造?:实际上很容易伪造,所以代码层面防君子不防小人,对于重要的资源(如图片、视频),务必使用服务器层面的配置。
- 对 CDN 的影响:如果使用了 CDN(如 Cloudflare、阿里云 CDN),需要在 CDN 层面配置防盗链,而不是仅仅在源站配置,否则 CDN 缓存会绕过 Referer 检查。
- 空 Referer 的处理:浏览器地址栏直接输入、微信/QQ 内置浏览器、某些下载工具(wget/curl 不加 --referer)会发送空 Referer,如果允许这些情况访问,代码里要给
$allowed = true,否则用户无法直接打开你的图片。 - 性能考虑:如果是高频访问的静态资源(如首页图片),在 PHP 里判断 Referer 会导致服务器产生大量不必要的 PHP 进程开销(即使没在读取图片前就拦截了,PHP 解释器也需要先跑起来)。强烈建议优先使用服务器层面配置。
总结建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 自己有服务器权限 | Nginx/Apache 配置 | 零开销,直接由 Web 服务器拦截,不影响 PHP 性能 |
| 虚拟主机或 PHP 环境 | PHP 代码 + Web服务器配置 | 先用 PHP 做基础拦截,如果支持 .htaccess 就加上 |
| 使用 CDN | CDN 防盗链设置 | 在 CDN 边缘节点拦截,比源站拦截更省流量 |
| 仅防百度/谷歌等爬虫 | robots.txt + User-Agent 过滤 | 不需要判断 Referer,robots.txt 可以禁止爬虫直接爬取资源 |
最后的忠告:防盗链的 HTTP_REFERER 极易被伪造,如果你需要非常强的资源保护(如付费视频、原创高清图),请考虑以下方案:
- 加时间戳签名(Nginx 的 secure_link 模块 或 自己写签名逻辑)。
- 使用 OSS/CDN 的 Referer 黑白名单 + 空 Referer 拦截。
- 验证码或登录鉴权(最严格,但用户体验稍差)。