本文详解利用HTTP Referer头实现图片防盗链的原理,通过NestJS全局中间件拦截非白名单域名请求并返回403。深入解析Referer拼写历史、Referrer-Policy策略(如no-referrer)及其在Meta标签、HTML属性和HTTP头中的优先级,帮助开发者解决跨站图片加载导致的流量费用激增问题。
前言
上周突然发现个人站点的所有图片均返回403状态码,此前一直正常。由于发布文章时习惯先发布至掘金平台,随后直接复制内容至个人站点,从而省去了在个人站点单独上传图片的步骤,这种方式一直非常便捷。

并未进行任何操作,图片却突然无法访问。推测可能是平台方采取了“开源节流、降本增效”的措施,即通过限制跨站图片引用以控制流量成本。当大量站点直接引用其图片导致流量费用大幅上升时,平台方通过设置防盗链机制,对非授权域名的访问统一返回403错误。
防盗原理
许多读者可能好奇,这类图片防盗机制具体是如何实现的?
我们可以模拟这一场景:配置服务器,使所有来自不受信任域名的图片资源请求均返回403状态码。
准备几个域名
即使没有真实域名也不必担心,可以直接在本地进行模拟。例如,这里使用SwitchHosts工具为本地环境添加了三个域名,并将它们全部指向本地IP地址。

配置完成后,这三个域名均可以正常访问本地服务。
服务端逻辑
静态资源目录
此处沿用之前的nest服务进行演示,该服务此前已配置了静态资源目录路径。
app.useStaticAssets(join(__dirname, '../static'), {
prefix: '/static',
}); // 静态资源前端访问图片
<img class="my_img" src="http://nanjiu.com:3000/static/sy.jpg" />当前使用nanjiu.com域名进行代理访问,图片可以正常加载显示。

防盗中间件
我们可以实现一个全局中间件来处理图片访问请求。若访问域名不在预设的信任白名单内,则直接返回403状态码。
// 白名单
const whiteList = ['nanjiu.com', 'fenanjiu.com']
// 图片防盗中间件
function imgMiddleware(req, res, next) {
console.log('--req', req.headers)
// 获取资源类型
const type = req.headers.accept || ''
if(!type.includes('image')) {
// 不是图片资源,直接放行
next()
return
}
const referer = req.headers.referer || ''
// 获取referer的域名
const { hostname } = url.parse(referer, true)
if(referer && whiteList.includes(hostname) || !referer) {
// 访问域名在白名单内,放行 !referer表示直接访问图片(比如浏览器地址栏输入图片地址)
res.status(200)
next()
}else {
// 访问域名不在白名单内,返回403
res.status(403)
res.send('逮到你了,又来偷我图片是吧!')
}
}需要注意的是,全局中间件的注册顺序必须位于useStaticAssets配置之前。
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.setGlobalPrefix('api'); // 全局路由前缀
app.use(cors()); // 允许跨域
app.use(json({ limit: '10mb' })); // 允许上传大文件
app.use(urlencoded({ extended: true, limit: '10mb' })); // 允许上传大文件
app.use(imgMiddleware) // 图片防盗中间件
app.useStaticAssets(join(__dirname, '../static'), {
prefix: '/static',
}); // 静态资源
await app.listen(3000);
console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();从上述代码可见,当前受信任的域名仅包含nanjiu.com和fenanjiu.com。
当使用sy.com域名访问nanjiu.com域名下的图片时,请求将触发防盗逻辑,返回403状态码。

并返回提示信息:“逮到你了,又来偷我图片是吧!”

Referer
从上述防盗原理的实现来看,核心关键在于referer。那么,referer具体指什么?为何它能用于图片防盗?
Referer是什么
MDN官方解释如下:
Referer 请求头包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入的。服务端一般使用 Referer 请求头识别访问来源,可能会以此进行统计分析、日志记录以及缓存优化等。
由此可知图片防盗的基本原理:服务端通过解析请求头中的Referer来识别访问来源,进而判断是否返回图片资源。
Referer这个单词实际上是Referrer的错误拼写,这源于历史原因。在早期HTTP规范中即存在此拼写错误,为了保持向后兼容,最终将错就错沿用至今。
拼写错误仅出现在 Request Headers 的 Referer 中,而在其他领域如General Headers、 JavaScript 及DOM中,均使用正确的拼写形式。
Referrer-Policy
Referrer-Policy 首部用来监管哪些访问来源信息——会在 Referer 中发送——应该被包含在生成的请求当中。
该策略用于控制 Referer 头返回的具体内容。
它包含以下属性值:
- no-referrer: 整个 Referer 首部会被移除。访问来源信息不随着请求一起发送。
- no-referrer-when-downgrade(默认值): 在没有指定任何策略的情况下用户代理的默认行为。在同等安全级别的情况下,引用页面的地址会被发送 (HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP)。
- origin: 在任何情况下,仅发送文件的源作为引用地址。例如
https://example.com/page.html会将https://example.com/ 作为引用地址。 - origin-when-cross-origin: 对于同源的请求,会发送完整的 URL 作为引用地址,但是对于非同源请求仅发送文件的源。
- same-origin: 对于同源的请求会发送引用地址,但是对于非同源请求则不发送引用地址信息。
- strict-origin: 在同等安全级别的情况下,发送文件的源作为引用地址 (HTTPS->HTTPS),但是在降级的情况下不会发送 (HTTPS->HTTP)。
- strict-origin-when-cross-origin: 对于同源的请求,会发送完整的 URL 作为引用地址;在同等安全级别的情况下,发送文件的源作为引用地址 (HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。
- unsafe-url: 无论是同源请求还是非同源请求,都发送完整的 URL(移除参数信息之后)作为引用地址。
面对这么多referrer策略,实际应用中该如何选择和使用?
meta标签使用
我们可以创建一个 name 为 referrer 的meta元素,从而为整个文档设置 referrer 策略。
<meta name="referrer" content="no-referrer">我的个人站点正是采用此方法解决图片访问403问题的。但需注意,若为页面设置了no-referrer策略,会导致页面上所有请求均不发送referer,因此使用时需权衡利弊。
rel属性
可以在a、area、link标签上通过rel属性来单独指定referrer的策略。
<a href="xxx" rel="external nofollow" rel="noreferrer">新地址</a>referrerpolicy属性
可以在a、area、link、img、iframe、script标签上通过referrerpolicy属性来单独指定referrer策略。
<img class="my_img" referrerpolicy="no-referrer" src="http://nanjiu.com:3000/static/sy.jpg" />例如,在上述例子中的图片加上referrerpolicy="no-referrer"属性后再进行访问,页面依然显示在sy.com域名下。

观察请求头发现其中未携带referer,因此该请求成功避开了图片防盗逻辑。
Headers请求头
也可以直接修改 HTTP 头信息中的 Referer-Policy 值。例如使用 Nginx 时,可通过设置 add\_headers 指令来配置请求头。
add_header Referrer-Policy "no-referrer";当 Headers 请求头与页面元素属性同时存在时,确定有效策略的优先顺序如下:
- 元素级策略
- 页面级策略
- 浏览器默认
以上即为利用HTTP Referer实现图片防盗的详细图文示例,更多关于HTTP Referer图片防盗的资料请关注本站其它相关文章!