自建站或者图片站跑一段时间,可能会遇到一种账单上的意外:访问量没怎么涨,带宽和流量却明显上去了。查日志才发现,图片被大量请求,来源却不是你的页面——有人直接把你的图片地址贴到了他自己的站、论坛或者聊天群里。图片在谁的页面上展示,用户看到的是谁的内容,占用的却是你的带宽和服务器资源,这种消耗在账单上不会标注来源。
这个行为通常叫盗链,也叫热链接。它和一般的转载不同:转载是把内容复制走,盗链是直接引用你的资源地址,每一张图都实打实从你的服务器上取。对访问量不大的站点,这点损耗可能无感;但如果你的站靠图片内容撑着,或者图片体积大、单价高,这条链路会持续地、无声地消耗你的成本,而且它不会带来任何对应的访问或者转化。更麻烦的是它往往在你不知情的情况下持续发生,等你在账单上看到异常,损失已经累积了一段时间,而你连该找谁都不知道。
热链接省的是别人的带宽,花的是你的
挡这件事的思路很直接:判断一次图片请求到底是从哪里发起的,如果来源不是自己的站点,就拒绝。这个判断依据通常用请求头里的来源字段,也就是浏览器在请求资源时带上的那个“我是从这个页面点到这里的”。服务器在返回图片之前看一眼这个字段,不在白名单里就不给。这个字段是浏览器自动带上的,用户看不见也改不了,所以对普通浏览行为来说,它是可靠的来源证明,只有刻意造假才会绕过它。
Nginx自带的模块就把这件事做成了几条配置。官方文档里给的指令是列出允许的来源,包括几种特殊取值:请求头里根本没有这个字段的、字段被防火墙或代理抹掉的、以及命中了本站服务器名的。除了这几种,你还可以写具体的域名,支持用通配符匹配子域名,也支持用正则匹配,比如放行搜索引擎的收录请求。判断结果落在一个内置变量上,来源不合法时它会被置位,你据此返回一个拒绝状态码就行。
但官方在文档里有两句提醒,必须一起看。第一句是:伪造一个带合法来源字段的请求非常容易,所以这个模块的目的不是彻底阻止这类请求,而是挡住普通浏览器造成的大批量请求。第二句是:正规浏览器在合法请求时,也可能不发这个字段。这两句话合起来说明一个事实——它是一道降低损耗的筛子,不是一道安全边界。理解这一点很重要,它决定了你应该把预期放在哪里:指望它挡掉所有恶意请求会失望,但用它挡掉大部分顺手盗链的站点,效果是立竿见影的。
第二句提醒对应的是现实里最常见的误伤。如果来源字段被主动裁剪了(现在不少站点为了隐私会这样做),或者是从一个加密页面跳到一个非加密页面(这种情况浏览器本来就不带来源字段),又或者用户装了会拦截这类信息的插件,那么一个完全正常的访问也会被判成不合法,图片当场打不开。自己的用户看到一堆裂图,比被盗链更伤,因为它伤的是你自己的访客。
挡完之后,记得留个正常出口
所以规则要从宽到严地调。起步阶段,把“没有来源字段”和“来源被抹掉”这两种情况一起放行,先只挡明确来自其他域名的请求;观察一段时间,看拒绝的量是否合理、有没有正常用户被误伤。有些图片服务还会提供更细的办法,比如给图片地址加上带时效的签名,只有从你页面发出的请求才带得到对得上的签名,这样连伪造来源都绕不过去,代价是图片地址不能直接复制分享。签名方案适合对成本敏感的图片站,缺点是图片地址会变长,而且失效之后需要重新生成,对内容型站点来说,维护成本不一定划算。
还有一件事值得想清楚:是不是所有外链都要挡。有些站点其实乐意被人引用,因为图片上带着自己的水印或者品牌,传播本身就是宣传。这时候可以只对体积大的图片做限制,或者对超量的来源返回一张占位图,而不是直接断掉。判断标准不是“能不能挡”,而是“这次引用对我有没有好处”——能想清这一点,规则就不必一刀切。有品牌水印的图,被人转发反而是免费的曝光;纯素材图、大尺寸原图,被人盗链就是纯粹的成本,按这个区别分档处理,比统一阻拦更合理。
防盗链的本质,是夺回“谁在为这次流量付钱”的决定权。它不复杂,几行配置就能生效,但真正值得花时间的不是配置本身,而是配套的判断:哪些来源该放、哪些该拒、拒的时候是断掉还是给一张提示图,以及怎么确认没伤到自己人。把这几件事想清楚再动手,你会得到一道既有用又不添乱的规则。上线之后留意两件事:一是被拒请求的量是否稳定,突然暴涨往往意味着某个大站开始引用你的图;二是自己的用户有没有反馈裂图,一旦出现就说明规则收得太紧了。
申请创业报道,分享创业好点子。点击此处,共同探讨创业新机遇!
