导读:作为站长,基本都遇到过网站被人镜像的烦恼吧?最典型的代表就是谷歌搜索,大家都懂的。很多时候反代我们网站的人可能就是拿你的网站练下手,学习下反向代理。当遇到网站被反代,而且排名还比你好的时候,有没有要暴走的冲动…本文分享一种简单有效网站防镜像的方案,适合任何 html 页面。
一、前人分享
挺早之前,看到 boke112 转载过一篇网站防镜像教程,分享了从.htaccess、php 以及 js 三个方向禁止他人恶意反向代理我们的网站。当时看完觉得三个方法都不完善:
先分析下原理:
.htaccess 方案是禁止从代理 IP 过来的请求
js 方案如果发现浏览器 url 地址不是预期的,那么直接跳转到我们规定的域名。
php 方案的原理和 js 方案类似,通过 $_SERVER[‘SERVER_NAME’] 变量判断域名判断请求是否符合预期,不是就跳转走。
再分析下缺憾:
.htaccess 方案,只要请求中含有代理 IP(HTTP_X_FORWARDED_FOR 不为空)就禁止访问,那如果用 CDN 的就全部 GG 了,而且这个值是可以在做反向代理的时候置空的,比如 Nginx 中可以这样做:
#清空 x-Forward-For
proxy_set_header X-Forward-For ”;
php 方案中,$_SERVER[‘SERVER_NAME’]的值同样可以在反向代理时伪造,比如:
# 反向代理时自定义 host 值
proxy_set_header Host ‘www.google.com.hk’;
二、优化版本
已推出最终版,所以,此优化版本可以不用了
js 方案,这个也是我今天要分享的方案,之前在 boke112 我也留言分享了张戈博客的做法,不过好像留言被删除了。文章中的 js 方案可是可以,但是是写死的跳转。也就是说不管在哪个页面,最终跳转都是首页!显然,这个方案还不够精细化,我们可以做得更细致!
所以,网站防镜像最简单有效的做法就是在<head>部分插入如下 js 代码即可:
<script type=”text/javascript”>
/* 如果浏览器域名不是 zhang.ge 将跳转到 zhang.ge 对应的页面*/
if (document.location.host != “zhang.ge”) {
location.href = location.href.replace(document.location.host,’zhang.ge’);
}
</script>
这里对文章 js 方案做了更细致的改善,也就是跳转之前想将当前 url 做一次替换,把当前 url 中的域名换成我们规定的域名,确保跳转后就是用户想要的页面,而不是强硬的跳到首页!
js 方案相对于其他方案来说,它的优势在于无法在反代时伪造,浏览器反馈的就是真实的访问情况,直接粗暴。当然,用 Nginx 的第三方内容过滤模块 ngx_http_subs_filter_module 也可以对反代的页面内容进行过滤,当然这是更高级的手法了,这里就不深入介绍了(请注意这段话,本文分享的只是一个方案,并非绝对有效的方法!!)。
三、最终版本
①、WordPress 专用版
龙笑天下很好的整理总结了目前几种防镜像的 js 方案,我看到最后一个借助了 img 的 onerror 事件,想法不错,就重新写了一个更简洁,兼容性更好的代码:
add_action(‘wp_footer’,’deny_mirrored_websites’);
function deny_mirrored_websites(){
$currentDomain = ‘zhangge.” + “net’; //此处自行拆分一下自己的域名即可
echo ‘<img style=”display:none” src=”https://zhang.ge/” onerror=\’this.onerror=null;var str1=”‘.$currentDomain.'”;str2=”docu”+”ment.loca”+”tion.host”;str3=eval(str2);if( str1!=str3 ){ do_action = “loca” + “tion.” + “href = loca” + “tion.href” + “.rep” + “lace(docu” +”ment”+”.loca”+”tion.ho”+”st,” + “\”‘ . $currentDomain .’\”” + “)”;eval(do_action) }\’ />’;
}
将此代码添加到主题 functions.php 文件当中即可。其他类似 js 可以不用上了,不过也不会冲突。
Ps:本来是丢到 wp_head 的,经过测试发现图片放到 head,浏览器会自动进行错误调整,导致一些本来在 head 的元素被丢到了 body 当中,比如 style.css,估计网页标准中 head 里面就不应该放置图片,所以移到了 footer 当中。
2017 年 10 月 21 日补充:这段代码会因为 onerror 死循环造成浏览网页的电脑高负载(CPU 飙升),因此在代码 onerror 触发事件中加入 onerror 清空机制,即加入 this.onerror=null【相关文章】。
②、HTML 通用版
既然是 js 代码,那么肯定可以用于任何符合 html 规范的页面了。要不是为了可以放到 wp 的 functions.php,都没必要写成 php 的模式,直接用 html 代码即可:
<img style=”display:none” src=”https://zhang.ge/” onerror=”this.onerror=null;var currentDomain=”zhangge.” + “net”; var str1=currentDomain; str2=”docu”+”ment.loca”+”tion.host”; str3=eval(str2) ;if( str1!=str3 ){ do_action = “loca” + “tion.” + “href = loca” + “tion.href” + “.rep” + “lace(docu” +”ment”+”.loca”+”tion.ho”+”st,” + “currentDomain” + “)”;eval(do_action) }” />
将以上代码中的
var currentDomain=”zhangge.” + “net”;
自行拆分成自己的域名,避免被镜像代码替换掉,比如:
var currentDomain=”www.” + “baidu” + “.com”;
然后将代码添加到网站的<body>之后即可(不建议放置到<head>里面,具体原因上文已说明),WP 一般为 header.php 文件,其他建站程序请自行搞定,这个版本适合任何网页。
③、通过 UA 禁止
JS 版本效果确实可以,但是有一个小弊端,大部分搜索引擎不能识别 js,所以蜘蛛还是能正常抓取镜像网站,有可能会影响 SEO。要彻底解决镜像站问题,就得直接禁止镜像网站服务器抓取我们的网页。
有网站已经分享了通过获取镜像网站的服务器 IP 来禁止抓取,但是镜像网站换一个 IP,或者还有其他镜像网站,都无法一劳永逸。所以,我们可以研究镜像服务器抓取时的特征,然后通过禁止特征来解决镜像问题,当然这个方法也不能绝对,因为特征很多时候都是可以伪造的,这里就不多说了。
14 年张戈博客就就已经整理分享过网站反爬虫攻略:《服务器反爬虫攻略:Apache/Nginx/PHP 禁止某些 User Agent 抓取网站》,其实这种镜像站和采集基本类似,所以我们需要先分析某一类镜像站的 UA 特征是什么。
抽空对此次站长朋友纷纷“讨伐”的几个镜像站进行了分析,其实就是在访问镜像网页的时候去查看我们的网站日志,我发现全部请求 UA 都是 PHP/5.4.4:
211.104.158.56 – – [22/Aug/2016:22:03:11 +0800] “GET / HTTP/1.1” 200 64410 “-“https://zhang.ge/”PHP/5.4.45” 211.104.158.56 http
想来也就明白了,这些镜像站点基本都是用的一套程序,甚至环境都是一致的!这让人很容易联想是不是一个人在搞事。。。
好了,废话不多说,既然知道他们的 UA 了,那么就很好解决了。直接将 PHP 这个关键词加入到《服务器反爬虫攻略:Apache/Nginx/PHP 禁止某些 User Agent 抓取网站》 这篇文章的 UA 清单中即可!
这里,只简单分享一下 PHP 代码和 Nginx 代码,其他的请参考前文。
PHP 通用版:
//禁止 UA 为空或含有 PHP 的请求
$ua = $_SERVER[‘HTTP_USER_AGENT’];
if(!$ua || preg_match(‘/PHP/i’, $ua)) {
header(“Content-type: text/html; charset=utf-8”);
die(‘请勿采集本站,因为采集的站长木有小 JJ!’);
}
将以上代码加入到 PHP 网站根目录的 index.php 的<?php 之后即可。
WP 适用版:
如果使用上面的 php 版本,WordPress 每次更新就会需要操作 index.php,比较麻烦,因此弄个专版:
/**
* WordPress 禁止 UA 为空或含有 PHP 的请求 By 张戈博客
* 原文地址:https://zhang.ge/5101.html
**/
if(!is_admin()) {
add_action(‘init’, ‘deny_mirrored_request’, 0);
}
function deny_mirrored_request()
{
$ua = $_SERVER[‘HTTP_USER_AGENT’];
if(!$ua || preg_match(‘/PHP/i’, $ua)) {
header(“Content-type: text/html; charset=utf-8”);
wp_die(‘请勿采集本站,因为采集的站长木有小 JJ!’);
}
}
将以上代码添加到 WordPress 主题的 functions.php 中即可。
Nginx 版本:
if ($http_user_agent ~* “PHP”) {
return 403;
}
将以上规则加入到 nginx 的 vhost 当中,比如添加到第一个 location 之前,然后重载 Nginx 即可。
我看到有同学使用了 htaccess 来判断 UA,但最后却返回了一个 301 跳转到首页,虽然也可以,但是有时候镜像程序也是可以抓取 301 的目标内容的,至少我之前就写过支持 301 跳转的 php 代码。
好了,关于镜像网站的问题就整理分享这么多,大家自行选择适合自己的方案即可!
四、拓展延伸
另外,如果是使用 https 的网站,想将 http 的访问都跳转到 https 又不想弄个 301 跳转(可能影响 SEO),那么上述 js 代码稍微改改就能完美跳转了:
<script type=”text/javascript”>
/* 如果当前是 http 访问,那么跳转到对于的 https 页面 */
if (document.location.protocol != “https:”) {
location.href = location.href.replace(/^http:/,”https:”);
}
</script>
看到这,你应该体会到了 js 的妙用吧?后续应该可以举一反三,多多利用了!
五、七牛镜像
用了七牛的网站,可以试试直接访问我们自定义的七牛静态域名,是不是和我们现在的网站一模一样呢?只是它不会更新而已。很多人肯定下意识的试过张戈博客,发现居然会跳转到对应的博客页面!
比如访问:http://static.zhang.ge/5100.html 会跳转到 https://zhang.ge/5100.html
于是,有不少朋友留言问我,怎么实现 301 跳转的??
好吧,除非七牛帮忙在 CDN 节点做设置,将非静态资源请求都跳转到源站,否则张戈也是没办法做 301 跳转的。因此,你看到的跳转也不是 301 了,而是 js 的跳转!
实现原理就是上文介绍的 js 方案咯!七牛就类似于一个镜像站,而且是静态存储到了七牛节点,因此只能用 js 方案,在静态页面中实现判断和跳转。
所以,上文分享的 js 防镜像代码,同样适用于七牛静态页面的自动跳转。只是美中不足的是,大部分搜索引擎并不能识别这个跳转,为了 SEO,那你还得继续使用七牛的 robots 设置了。
当然,如果你添加 js 代码之前就已经在使用七牛了,那么必须清空七牛中的缓存文件才行,否则是不会跳转的了!因为缓存的代码中没有这段 js 咯!
最新补充:有人留言说了更好的方案,在 Nginx 中判断七牛的 UA 以及抓取的路径就能杜绝七牛缓存不改缓存的页面,要实现也很简单,在 Nginx 配置中加入如下规则即可:
#匹配请求 UA 是不是七牛
if ( $http_user_agent ~* ‘qiniu-imgstg-spider’){
set $deny_qn ‘y’;
}
#匹配请求页面是不是 hml 或 php 结尾
if ( $request_uri ~* ‘\.(html|php)’) {
set $deny_qn ‘${deny_qn}es’;
}
#匹配首页
if ( $request_uri ~ ‘^/’ ) {
set $deny_qn ‘${deny_qn}es’;
}
#若上述 2 个条件成立,就拒绝访问
if ( $deny_qn = ‘yes’ ) {
return 403;
}
最后,再啰嗦一句,本文分享的只是小白入门级方案,并非高深技术,专家请绕道。