原创:给博客本地音频做防盗链
我的MP3都是上传到本地服务器调用的,没用CDN之前,面板后台有一个防盗链设置,可以防止外链调用MP3文件,只要把自己的域名写到“许可域名”即可。

后来使用了CDN,不管是腾讯云的EO还是阿里云的ESA,发现这个防盗链系统失效了,原因是MP3文件被缓存到CDN各处的节点上,盗链的URL直接从边缘节点就可以拉去MP3文件。
今天和AI探讨了下,给了一个不错的解决方案分享下,如果你的音乐视频文件也是本地服务器上传调用的,可以参考设置。
先说结论:
最终方案是三件事配合:
- 服务端(WordPress 插件)把媒体地址改写成带时效签名的临时地址;
- CDN 边缘(边缘函数 / 原生 URL 鉴权)校验签名,不通过直接拒绝;
- 受保护目录只走这条通道,原始直链形同废纸(签名一过期就无效)。
服务器端:插件
做一个PHP文件,放在 wp-content/mu-plugins/ (没有这个目录就新建), 好处是跟主题解耦 —— 以后换主题、升级主题都不用动它。
<?php
/**
* Plugin Name: Audio Hotlink Guard
* Description: 为受保护目录下的音视频生成带时效签名的 URL(配合 CDN 边缘验签)
*/
if (!defined('ABSPATH')) exit;
/** 受保护目录(按你的实际情况改) */
const MEDIA_DIR = '/media/';
/**
* 密钥请放 wp-config.php:define('AUDIO_SIGN_SECRET', '一串随机字符');
* 不要硬编码在主题或插件里 —— 那等于把钥匙和锁放在一起。
*/
function my_sign_secret() {
return defined('AUDIO_SIGN_SECRET') ? trim((string) AUDIO_SIGN_SECRET) : '';
}
function my_is_media($path) {
return (bool) preg_match('/\.(mp3|m4a|mp4|wav|ogg|flac|aac)$/i', (string) $path);
}
function my_signed_url($url) {
$secret = my_sign_secret();
if ($secret === '' || !is_string($url) || $url === '') return $url;
$p = wp_parse_url($url);
if (empty($p['path'])) return $url;
$path = $p['path'];
// 只签自己目录下的媒体文件;图片等其它资源原样放行
if (strpos($path, MEDIA_DIR) !== 0) return $url;
if (!my_is_media($path)) return $url;
if (strpos($path, '..') !== false) return $url; // 防路径穿越
if (isset($p['query']) && strpos($p['query'], 'auth_key=') !== false) return $url; // 不重复签
/* 关键:时间戳对齐到整点
* 这样同一小时内生成的 URL 完全一致 → CDN 缓存才能命中
* 若用 time() 原值,每次都不一样,等于让 CDN 全部失效 */
$slot = time() - (time() % 3600);
$sign = md5($path . '-' . $slot . '-0-0-' . $secret);
return 'https://example.com' . $path . '?auth_key=' . $slot . '-0-0-' . $sign;
}
然后把它挂到内容输出上,这样文章里原本写的直链会被自动改写:
// 正文里的直链自动改写
add_filter('the_content', function ($content) {
if (my_sign_secret() === '' || !is_string($content) || strpos($content, MEDIA_DIR) === false) {
return $content;
}
return preg_replace_callback(
'#https?://example\.com' . preg_quote(MEDIA_DIR, '#') . '[^\s"\'<>\)\]]+#i',
function ($m) { return my_signed_url($m[0]); },
$content
);
}, 20);
上面这段是「最小示例」,可以粘贴复制后,放到AI里,然后把自己的网址也放进去,让AI访问你博客的MP3文件后,来帮你写出正常使用的PHP插件文件。
上传插件后,回到站点根目录,找到 wp-config.php 编辑,查找“请不要再继续编辑”,在它上面新起一行,加: define('AUDIO_SIGN_SECRET', '一串随机字符'); PS:这只是示例,请让AI修改后加入真实密匙填入。
下面的JS文件同理,也可以粘贴后交给AI进行本地化处理,然后导出给你。
CDN边缘:验签
如果你的 CDN 提供原生 URL 鉴权配置(比如腾讯云EO),直接在控制台填密钥和有效期就行,连代码都不用写。 如果没有、但提供边缘函数(比如阿里云ESA),就用下面这段(在边缘节点运行的 JS):
import { createHash } from "node:crypto";
// ⚠️ 与 wp-config.php 里的密钥逐字节一致;改一处必须改另一处
const SECRET = "YOUR_RANDOM_SECRET";
// 有效期窗口(秒)。配合服务端"整点对齐",实际有效期 1~2 小时
const DELTA = 7200;
// 只对音视频验签;其它文件(图片等)直接放行,避免误伤
const MEDIA_RE = /\.(mp3|m4a|mp4|wav|ogg|flac|aac)$/i;
// 本站域名,用于下面的"兜底放行"
const OWN_HOSTS = ["example.com", "www.example.com"];
/* 回源时注入一个自定义请求头,供源站校验。
* 用途:防止有人拿到你的源站 IP 后绕过 CDN 直连。
* 不配源站规则时它完全无害,就是个没人看的请求头。 */
const ORIGIN_HEADER = "X-Origin-Token";
function ownReferer(request) {
const ref = request.headers.get("referer") || "";
if (!ref) return false;
try {
return OWN_HOSTS.indexOf(new URL(ref).hostname.toLowerCase()) !== -1;
} catch (_) { return false; }
}
/** 统一回源入口:注入来源标记,并保留原有 headers(Range 必须透传) */
function originFetch(request, urlOverride) {
const h = new Headers(request.headers);
h.set(ORIGIN_HEADER, SECRET);
return fetch(urlOverride || request.url, { method: request.method, headers: h });
}
async function handleRequest(request) {
const url = new URL(request.url);
const path = url.pathname;
// ① 非音视频文件 → 放行(避免误伤图片)
if (!MEDIA_RE.test(path)) return originFetch(request);
const authKey = url.searchParams.get("auth_key");
const now = Math.floor(Date.now() / 1000);
// ② 没有签名:本站来的放行,其它一律 401
if (!authKey) {
if (ownReferer(request)) return originFetch(request);
return new Response("Unauthorized", { status: 401 });
}
const parts = authKey.split("-");
if (parts.length !== 4) {
return ownReferer(request) ? originFetch(request) : new Response("Unauthorized", { status: 401 });
}
const [timestamp, rand, uid, signature] = parts;
const ts = parseInt(timestamp, 10);
// ③ 过期
if (!ts || isNaN(ts) || now > ts + DELTA) {
if (ownReferer(request)) return originFetch(request);
return new Response("Link expired", { status: 403 });
}
// ④ 验签(必须与服务端同一算法、同一密钥)
const signString = [path, timestamp, rand, uid, SECRET].join("-");
const md5 = createHash("md5").update(signString).digest("hex");
if (md5 !== signature) {
if (ownReferer(request)) return originFetch(request);
return new Response("Unauthorized", { status: 401 });
}
// ⑤ 通过 → 回源。注意剥掉 auth_key:
// 缓存 key 才算干净,同一小时内的请求才能命中同一份边缘缓存。
return originFetch(request, url.origin + url.pathname);
}
export default {
async fetch(request) {
try {
return await handleRequest(request);
} catch (e) {
// 函数自身异常时放行:宁可漏拦,也别把整站音频打挂
return originFetch(request);
}
},
};
这个JS文件是用于阿里云ESA配置的,腾讯云EO下面再说,不需要这个JS文件。
阿里云ESA配置
1、进入阿里云ESA,开通“边缘计算和AI”功能后,创建函数,选择“本地上传”,将上面的JS文件拉入到虚线框内。


2、部署好后,进入这个函数,点击“域名”,页面拉到底部,可以看到“添加路由”的按钮(不要去设置上面的域名,是设置路由):


3、对路由信息进行设置:

完成所有部署工作,防盗链正式生效。
| 谁在请求 | 结果 |
|---|---|
| 你页面里带票的正常播放 | 正常播放 ✓ |
| 别人网站嵌你的直链 | 403 拒绝 ✓ |
| 脚本/下载器裸拉 | 401 拒绝 ✓ |
| MP3目录下的图片文件 | 正常显示 ✓(不查票) |
| 站点其它所有路径(文章页、图片、JS…) | 完全不受影响 ✓(这正是选「路由」而非「域名绑定」的意义) |
腾讯云EO配置
如果你使用的是腾讯云EO,那配置更加简单,首先将 服务器端:插件 的PHP文件上传到 wp-content/mu-plugins/ 后,JS文件都不需要去做。
1、打开腾讯云EO,进入站点,点击左侧导航里的“站点加速”–“规则引擎”,并创建规则:

2、填写相关参数,MP3所在的文件夹用“正则匹配”,需要从这个文件里剔除的图片等文件,就用“正则不匹配”,然后填写主鉴权密匙和加密串参数即可,可参考下图:

保存发布后,防盗链生效。无论是ESA还是EO,都是在CDN上进行验证防盗链,可以完美解决MP3/MP4文件被缓存到CDN各处的节点上,导致面板后台防盗链失效。




最后那个 “函数异常时放行,宁可漏拦也别把整站音频打挂” 的降级策略好评,做运维的都懂。不过有个小建议:密钥别放 wp-config 里其实更好,环境变量或者专门的配置文件,不然 wp-config 被备份走等于钥匙和锁一起丢了。整体方案可以直接抄。
高手啊,有理有据
我用eo加速,设置了一个源站缓存的判断,也挺好。
这个可以,想到了年初我被刷的经历。
我也写过,服务器加密,切片加密,流加密。但是在现在的AI面前毫无抵抗力。辛辛苦苦设计的,让AI测试下,基本上几分钟就能破解。随意,直接给真实地址了。
用了后CDN侧流量大约下降了27%左右。。。略有效果。。。为了几首歌去破解加密,感觉不太划算哈哈哈哈
方法很不错,可惜皮皮学不来。不过我网站音乐也叫AI写了个防盗链,直接写在主题里,算一个假防盗功能吧。平时比较喜欢收藏一些自己喜欢的音乐,直接打开音乐路径跳到404。
这个的确得开,之前没开,图片被刷了。
多少年没下载过mp3了,都快忘记还有这类音频。
现在都要收费了,一点没有以前的互联网免费精神 ❗
有必要,但设定阈值更重要。
没事,用的都是免费版,免费版顶了天限你流,不会收你钱 😆
这个专业,实用。
我用了EO之后图床的防盗链也失效了,后来看到别的订阅能直接显示我的文章图片,赶紧找AI问的,就是你写的最后这一种EO的边缘函数,挺好用的
我cdn就放了几张图片意思一下,没注意防盗链,不过cdn如果不默认防盗还是很危险的。
图片我都不防盗的,没几个流量,音乐和视频防一手
大流量才考虑,我草根无所谓的啦
一个小博客,cdn一天2个G,你敢信??
我以前也做防盗链,现在不做了。
一是因为站上媒体资源比较少,二是其实懂的人,你防不住。不懂的人,防不防几乎没什么差别。不过防盗链做了肯定是比没做好的,你的博客每天CDN流量大,说明你的站人气比较高哈。
对于音乐,其实走网易云流量也是个不错的选择。
呃,p的人气高,就是被刷😡,纯纯的刷
盗刷确实要防一下,我的服务器性能也垃圾,我是什么都不放,就没有那么多后续麻烦,
放着不会丢,我喜欢日志里放音乐🎵
如果cdn不限制流量,也不用太在意
现在蹭阿里云ESA免费版 就怕后期出个每月限流量。。。
聊胜于无,真要盗,防不胜防啊。
是啊,就算被盗也是刷 CDN 流量,我就是好心做个防御😏
哈哈,不一定能完全防住,还是可以破解的,只是麻烦一点
那是,永远只有破解更技高一筹,聊胜于无
防盗链的主要目的是怕刷流量,我之前哪怕设置了防盗链,结果流量还是被刷了很多。后来客服说是我还需要加入其他一些列防范设置,结合起来一起用才行。
但是如果你用免费版的 EO/ESA做了长期缓存,流量就不怕刷了,盗不盗也无所谓了。
哎 就这个博客 每天CND 要2个G流量。。。
我是直接调用网易云的歌单,也做了防刷处理!和你的也算是各有千秋吧!
本地调用呢图一个省心稳定,你调用网易云音乐更省流量,的确是各有优缺点,老张,这主题我不折腾了,定稿了,以后没有bug不改了,太费神。。。
把自己想要的功能加上,没有BUG的情况下,真的不需要太多的折腾!我的也停了,不折腾了!