防盗链不能仅依赖$_server['http_referer'],因其易伪造且常为空;应结合带签名的临时url、nginx secure_link模块及range请求支持实现多层防护。

直接用 $_SERVER['HTTP_REFERER'] 做防盗链?别信
Referer 头根本不可靠——浏览器能伪造,HTTPS 页面跳到 HTTP 时会清空,书签直连、微信内嵌页也常为空。单独依赖它等于没防。真要加 Referer 检查,只适合做第一层快速过滤,且必须配合 parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) 提取 host 后精确比对,不能用模糊正则(比如 example.com 会被 attacker-example.com 绕过)。
更关键的是:空 Referer 怎么处理?放行?拒绝?还是走备用 token 校验?这个策略必须明确写进逻辑,不能留默认分支。
生成带签名的临时 URL 是最有效手段
核心是让真实视频路径从不暴露在前端,每次播放都生成含时间戳和密钥签名的一次性链接。PHP 侧生成示例:
function generateVideoUrl($path, $expires = 300) {
$secret = 'your_secret_key_here'; // 必须保密,不能进 Git
$expire_ts = time() + $expires;
$signature = hash_hmac('sha256', $path . '|' . $expire_ts, $secret);
return '/video.php?file=' . urlencode($path) . '&exp=' . $expire_ts . '&sig=' . $signature;
}
注意三点:
-
$path应为预设别名(如demo_2024),服务端再映射到真实文件路径,避免泄露服务器结构 - 校验时所有参数必须先
urldecode()再参与签名计算,否则编码差异会导致验签失败 -
$_GET['exp']必须严格大于time(),不能只判断是否相等或忽略时区
PHP 代理输出视频流时必须支持 Range 请求
用户拖动进度条、暂停续播都依赖 HTTP Range 头。如果 video.php 只用 readfile() 全量输出,播放器会卡顿甚至报错 Failed to load resource: net::ERR_CONTENT_LENGTH_MISMATCH。
正确做法是手动解析 $_SERVER['HTTP_RANGE'],设置 206 Partial Content 响应头,并分块 fread() 输出。关键点:
- 必须设置
Accept-Ranges: bytes响应头,否则播放器不会发 Range 请求 - 响应体长度(
Content-Length或Content-Range)必须与实际输出字节数完全一致 - 大文件别用
file_get_contents(),会吃光内存;用fopen()+fseek()+ 循环fread()
Nginx 层面不配 secure_link 就等于裸奔
PHP 脚本校验再严,也挡不住高频恶意请求打穿 PHP 进程。Nginx 的 secure_link 模块能在请求进入 PHP 前就完成 URL 签名校验,性能高、开销低。
配置要点:
- location 块需同时覆盖
.m3u8和.ts路径,且签名逻辑必须统一(比如都用$uri?$args参与哈希) -
secure_link_md5的密钥变量不能硬写在配置里,要用set $key "xxx";或从环境变量读取 - 前端 HLS 播放器(如
hls.js)拿到的.m3u8URL 必须带st=xxx&exp=xxx参数,否则 Nginx 直接 403
真正难的是密钥轮换和过期策略——exp 时间太短影响用户体验,太长又增加泄露风险;st 签名若复用同一密钥,攻击者可能批量爆破。这些细节没人提,但线上出问题往往就卡在这儿。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











