Linux VPS采用Rsync实现网站文件/服务器数据同步增量备份

无论我们选择虚拟主机,还是选择VPS、服务器,最为关键的就是服务器中的数据。相对而言,选择优质服务商和服务器的意外几率会小一些,但也不是没有。因为服务器的意外不是商家和我们用户控制的,包括服务器中程序的问题,也可能是我们人为的操作问题。

如果网站数据量不大,我们可以采用定期人工备份或者利用定时备份脚本。一键包环境或者是WEB面板都带有自动备份到本地和远程服务器的功能。如果数据量较大,我们其实可以采用Rsync实现同步增量备份。下面的记录是通过Rsync实现文件同步增量备份的。
设置过程却是比较繁琐,如果我们普通项目用户可以使用商家自带的快照定时或者脚本环境带的自动备份功能,以及我们自己的定期备份。(本文来自:https://www.laobuluo.com/1070.html)

第一、准备工作

1、数据备份

如果我们没有把握一次性搞定,我们可以准备两台测试环境服务器实现Rsync同步备份功能之后再用到生产环境。如果用到生产环境,我们可以将服务器快照备份,或者将网站、项目数据备份。

2、服务器准备

这里我们采用的是Rsync同步增量备份,所以我们需要准备主服务器、以及一台备份服务器。鉴于数据备份后的功能,我们可以直接备份到备份服务器某一个目录,或者将备份服务器安装主服务器环境,将需要备份的网站项目备份到对应的同目录中。

3、端口开放

如果我们服务器没有设定iptables防火墙规则,那就不要设置端口。如果我们有设置iptables防火墙,那就需要将873端口添加放行。

vi /etc/sysconfig/iptables

打开iptables规则文档,添加:

-A INPUT -p tcp -m state --state NEW -m tcp --dport 873 -j ACCEPT

未分类

编辑保存之后,然后/etc/init.d/iptables restart重启才能生效。同样的方法,我们需要在主服务器和备份服务器同时设置。

第二、配置备份服务器

1、安装rsync

未分类

2、配置文件

vi /etc/xinetd.d/rsync

将配置文件disable参数从”yes”换成”no”。

3、创建配置文件

vi /etc/rsyncd.conf

创建文件,然后将下面脚本添加:

log file = /var/log/rsyncd.log
pidfile = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
secrets file = /etc/rsync.pass
motd file = /etc/rsyncd.Motd
#创建一个模块名称,后面需要一致
[www.laobuluo.com]
#备份服务器目录地址
path = /home/wwwroot/www.laobuluo.com
#对应上面模块名称
comment = www.laobuluo.com
uid = root
gid = root
port = 873
use chroot = no
read only = no
list = no
max connections = 200
timeout = 600
#创建一个同步用户名,随便取,反正后面出现的时候要一致
auth users = www.laobuluo.com_user
#主服务器IP地址
hosts allow = xxx.xxx.xxx.xxx

根据我们网站项目以及服务器实际信息创建文件贴到配置文件中保存退出。

4、创建密码配对文件

vi /etc/rsync.pass

创建密码配对文件:

www.laobuluo.com_user:1234567890passwd

红色字段需要对应上面的auth users,蓝色部分是我们创建配对的密码。后面主服务器配置的时候也需要用到密码,所以必须一致。

5、开放权限和启动

chmod 600 /etc/rsyncd.conf
chmod 600 /etc/rsync.pass
service xinetd restart

未分类

第三、配置主服务器

1、安装rsync

yum install rsync xinetd -y

未分类

2、配置文件

vi /etc/xinetd.d/rsync

未分类

将配置文件disable参数从”yes”换成”no”。

3、创建密码配对文件

未分类

将我们上面在备份服务器中蓝色的密码丢进来,必须一致。

4、授权和启动

未分类

第四、配置主服务器

这一步我们继续配置主服务器,需要安装和配置inotify-tools来实现同步增量备份。

1、安装环境包

yum install make gcc gcc-c++ -y

未分类

2、下载和安装inotify-tools

cd /usr/local/src
wget https://download.laobuluo.com/tools/inotify-tools-3.14.tar.gz
tar -zxvf inotify-tools-3.14.tar.gz
cd inotify-tools-3.14
./configure --prefix=/usr/local/inotify
make
make install

未分类

3、配置环境变量

echo "PATH=/usr/local/inotify/bin:$PATH" >>/etc/profile.d/inotify.sh
source /etc/profile.d/inotify.sh
echo "/usr/local/inotify/lib" >/etc/ld.so.conf.d/inotify.conf
ln -s /usr/local/inotify/include /usr/include/inotify

4、配置参数

未分类

参考就脚本 – http://soft.laozuo.org/scripts/rsync.sh

修改自行的文件和目录,然后保存退出。

5、创建排除目录列表

vi /usr/local/inotify/exclude.list

创建一个排除目录,这里可以添加不同步的目录,一行一个目录。如果暂时没有可以留空,以后需要用到在添加。

6、授权和设置开机启动

chmod +x /usr/local/inotify/rsync.sh

这里我们授权。

vi /etc/rc.d/rc.local

最后一行添加:

sh /usr/local/inotify/rsync.sh &

未分类

第五、检测以及生效小结

1、检查生效

设置完毕之后,我们可以通过手工检查

sh /usr/local/inotify/rsync.sh &

在主服务器执行脚本,如果看到有目录在进度,说明完美,然后去备份服务器中可以看到已经备份到的文件目录。

未分类

2、自动生效

重启主服务器,然后就会自动生效。如果不放心我们可以在主服务器对应目录丢一个文件看看备份服务器是否有增加。

总结,这篇文章较为详细的将Linux VPS、服务器使用Rsync进行同步增量备份文件。

使用Python和Flask编写Prometheus监控

Installation

pip install flask
pip install prometheus_client

Metrics

Prometheus提供4种类型Metrics:Counter, Gauge, Summary和Histogram

Counter

Counter可以增长,并且在程序重启的时候会被重设为0,常被用于任务个数,总处理时间,错误个数等只增不减的指标。

import prometheus_client
from prometheus_client import Counter
from prometheus_client.core import CollectorRegistry
from flask import Response, Flask
app = Flask(__name__)
requests_total = Counter("request_count", "Total request cout of the host")
@app.route("/metrics")
def requests_count():
    requests_total.inc()
    # requests_total.inc(2)
    return Response(prometheus_client.generate_latest(requests_total),
                    mimetype="text/plain")
@app.route('/')
def index():
    requests_total.inc()
    return "Hello World"
if __name__ == "__main__":
    app.run(host="0.0.0.0")

运行改脚本,访问youhost:5000/metrics

# HELP request_count Total request cout of the host
# TYPE request_count counter
request_count 3.0

Gauge

Gauge与Counter类似,唯一不同的是Gauge数值可以减少,常被用于温度、利用率等指标。

import random
import prometheus_client
from prometheus_client import Gauge
from flask import Response, Flask
app = Flask(__name__)
random_value = Gauge("random_value", "Random value of the request")
@app.route("/metrics")
def r_value():
    random_value.set(random.randint(0, 10))
    return Response(prometheus_client.generate_latest(random_value),
                    mimetype="text/plain")
if __name__ == "__main__":
    app.run(host="0.0.0.0")

运行改脚本,访问youhost:5000/metrics

# HELP random_value Random value of the request
# TYPE random_value gauge
random_value 3.0

Summary/Histogram

Summary/Histogram概念比较复杂,一般exporter很难用到,暂且不说。

PLUS

LABELS

使用labels来区分metric的特征

from prometheus_client import Counter
c = Counter('requests_total', 'HTTP requests total', ['method', 'clientip'])
c.labels('get', '127.0.0.1').inc()
c.labels('post', '192.168.0.1').inc(3)
c.labels(method="get", clientip="192.168.0.1").inc()

REGISTRY

from prometheus_client import Counter, Gauge
from prometheus_client.core import CollectorRegistry
REGISTRY = CollectorRegistry(auto_describe=False)
requests_total = Counter("request_count", "Total request cout of the host", registry=REGISTRY)
random_value = Gauge("random_value", "Random value of the request", registry=REGISTRY)

Python 实现多线程下载器

前言

我为什么会想到要写一个下载器呢,实在是被百度云给逼的没招了,之前用 Axel 配合直链在百度云下载视频能达到满速,结果最近两天 Axel 忽然不能用了,于是我就想着要不干脆自己写一个吧,就开始四处查询资料,这就有了这篇博客。

我假设阅读这篇博客的你已经对以下知识有所了解:

  • Python 的文件操作
  • Python 的多线程
  • Python 的线程池
  • Python 的 requests 库
  • HTTP 报文的首部信息

下载

获取文件采用的是 requests 库,该已经封装好了许多 http 请求,我们只需要发送 get 请求,然后将请求的内容写入文件即可:

import requests

r = requests.get('http://files.smashingmagazine.com/wallpapers/july-17/summer-cannonball/cal/july-17-summer-cannonball-cal-1920x1080.png')
with open('wallpaper.png', 'wb') as f:
    f.write(r.content)

随后看看文件夹,那张名为 wallpaper.png 的图片就是我们刚刚下载的。

但是这个功能太简单了,甚至简陋,我们需要多线程并发执行下载各自的部分,然后再汇总。

拆分

为了拆分,首先得知道数据块的大小,HTTP 报文首部提供了这样的信息:

  • 用 head 方法去获取 http 首部信息,再从获取的信息提取出 Content-Length 字段(上文图片大小为 261258 bytes)
import requests

headers = {'Range': 'bytes={}-{}'.format(0, 100000)}
r = requests.get('http://files.smashingmagazine.com/wallpapers/july-17/summer-cannonball/cal/july-17-summer-cannonball-cal-1920x1080.png', headers = headers)
with open('wallpaper.png', 'wb') as f:
    f.write(r.content)

我们得到了图片的前 100001 个字节(Range 的范围是包括起始和终止的),打开 wallpaper.png 你应该能看到一幅“半残”的图。

这样我们里目标更近了一步,继续:

  • 确认线程数(比如 8 个),261258//8 = 32657,前 7 个线程都取 32657 个 bytes,第八个取剩余的
part = size // nums

for i in range(nums):
        start = part * i
        if i == num_thread - 1:   # 最后一块
            end = file_size
        else:
            end = start + part
  • 每个线程获取到的内容按顺序写入文件(file.seek() 调节文件指针)
def down(start, end):
    headers = {'Range': 'bytes={}-{}'.format(start, end)}
    # 这里最好加上 stream=True,避免下载大文件出现问题
    r = requests.get(self.url, headers=headers, stream=True)
    with open(filename, "wb+") as fp:
        fp.seek(start)
        fp.write(r.content)

嘛,线程多了起来就扔到线程池让它来帮我们调度。

封装

功能复杂了,用对象来封装整理一下:

class Downloader(): 
    def __init__(self, url, num, name):
        self.url = url
        self.num = num
        self.name = name
        r = requests.head(self.url)
        self.size = int(r.headers['Content-Length']) 

    def down(self, start, end):

        headers = {'Range': 'bytes={}-{}'.format(start, end)}
        r = requests.get(self.url, headers=headers, stream=True)

        # 写入文件对应位置
        with open(self.name, "rb+") as f:
            f.seek(start)
            f.write(r.content)


    def run(self):
        f = open(self.name, "wb")
        f.truncate(self.size)
        f.close()

        futures = []
        part = self.size // self.num 
        pool = ThreadPoolExecutor(max_workers = self.num)                                    
        for i in range(self.num):
            start = part * i
            if i == self.num - 1:   
                end = self.size
            else:
                end = start + part - 1
            # 扔进线程池
            futures.append(pool.submit(self.down, start, end))
        wait(futures)

至此,核心功能都完成了,剩下的就是实际体验的优化了。

完整的代码已托管至 GitHub,地址见这里: https://github.com/WincerChan/DAM

结语

很可惜,我写的这个下载器还是不能下载百度云直链,不过嘛,好多人都说结果不重要,都说重要的是过程,不是么?写这个下载器我也确实学到了许多,至于一开始我是出于什么样的目的?管他呢

Python并行计算简单实现

multiprocessing包是Python中的多进程管理包.
Pool(num)类提供一个进程池,然后在多个核中执行这些进程,
其中默认参数num是当前机器CPU的核数.

Pool.map(func, iterable[, chunksize=None])
2个参数, 第一个参数是函数, 第二个参数是需要可迭代的变量, 作为参数传递到func

如果func含有的参数多于一个,可以利用functools.partial 先处理.
以下是一个简单的例子.

from multiprocessing import Pool
from functools import partial 

def somefunc(str_1, str_2, iterable_iterm):
    print("%s %s %d" % (str_1, str_2, iterable_iterm))

def main():
    iterable = [1, 2, 3, 4, 5]
    pool = Pool()
    str_1 = "This"
    str_2 = "is"
    func = partial(somefunc, str_1, str_2)
    pool.map(func, iterable)
    pool.close()
    pool.join()

if __name__ == "__main__":
    main()

php-fpm优化方法 pm.min_spare_servers、pm.max_spare_servers

php-fpm进程池开启进程有两种方式,一种是static,直接开启指定数量的php-fpm进程,不再增加或者减少;
另一种则是dynamic,开始时开启一定数量的php-fpm进程,当请求量变大时,动态的增加php-fpm进程数到上限,

当空闲时自动释放空闲的进程数到一个下限。这两种不同的执行方式,可以根据服务器的实际需求来进行调整。

要用到的一些参数,分别是pm、pm.max_children、pm.start_servers、pm.min_spare_servers和pm.max_spare_servers。

pm表示使用那种方式,有两个值可以选择,就是static(静态)或者dynamic(动态)。

下面4个参数的意思分别为:

  • pm.max_children:静态方式下开启的php-fpm进程数量,在动态方式下他限定php-fpm的最大进程数(这里要注意pm.max_spare_servers的值只能小于等于pm.max_children)
  • pm.start_servers:动态方式下的起始php-fpm进程数量。
  • pm.min_spare_servers:动态方式空闲状态下的最小php-fpm进程数量。
  • pm.max_spare_servers:动态方式空闲状态下的最大php-fpm进程数量。如果dm设置为static,那么其实只有pm.max_children这个参数生效。系统会开启参数设置数量的php-fpm进程。php-fpm一个进程大概会占20m-40m的内存,所以他的数字大小的设置要根据你的物理内存的大小来设置,还要注意到其他的内存占用,如数据库,系统进程等,来确定以上4个参数的设定值!

如果dm设置为dynamic,4个参数都生效。系统会在php-fpm运行开始时启动pm.start_servers个php-fpm进程,

然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数。

参数要求pm.start_servers的值在pm.min_spare_servers和pm.max_spare_servers之间。

例:主机内存为1.6G,默认lnmp设置为20,10,10,20;改为如下25,5,5,25

因为网站访客人数很少,所以初始php-fpm线程5个就好了,不会占用过多内存,当访客突然增加,也会动态调整直到最高的限值25.

[www]
listen = /tmp/php-cgi.sock
listen.backlog = -1
listen.allowed_clients = 127.0.0.1
listen.owner = www
listen.group = www
listen.mode = 0666
user = www
group = www
pm = dynamic
pm.max_children = 25
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 25
request_terminate_timeout = 100
request_slowlog_timeout = 0
slowlog = var/log/slow.log

php-fpm占用内存过高分析及解决

昨天刚买了个vps搭建了一个wordPress博客,刚搭建的时候有一大堆的问题。好不容易搭建完成了发现运行一阵子以后内存几乎用完了。开始以为是服务器的问题,费了好大的劲把Apache服务器换成了Nginx。但是问题还是没有解决,最后用top命令看了一下是php-fpm的问题。问题的解决办法如下:

未分类

1、查看php-fpm的进程个数

ps -fe |grep "php-fpm"|grep "pool"|wc -l

2、查看每个php-fpm占用的内存大小

ps -ylC php-fpm --sort:rss

3、配置php-fpm参数

找到php-fpm的配置文件php-fpm.conf

pm = dynamic #对于专用服务器,pm可以设置为static。#如何控制子进程,选项有static和dynamic。如果选择static,则由pm.max_children指定固定的子进程数。如果选择dynamic,则由下开参数决定:
pm.max_children #子进程最大数
pm.start_servers #启动时的进程数
pm.min_spare_servers #保证空闲进程数最小值,如果空闲进程小于此值,则创建新的子进程
pm.max_spare_servers #保证空闲进程数最大值,如果空闲进程大于此值,此进行清理

对于内存大的服务器(比如8G以上)来说,指定静态的max_children实际上更为妥当,因为这样不需要进行额外的进程数目控制,会提高效率。

对于内存小的服务器,使用动态方式。具体最大数量根据 内存/20M 得到。比如512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服务器的负载情况来设置,比较合适的值在5~10之间。

解决Nginx环境WordPress或Typecho设置固定链接无法打开的问题

做网站搭博客,首选都是自己买个国外VPS,400+一年费用一般比国内的虚拟空间稍贵点,但相比买虚拟空间VPS好太多:

  • 私有独享ip
  • 足够多的存储空间:基本上都是10G起,要放什么文件都可以,数据库也可以随意多个
  • 足够多的流量:一般都是500G起,相比虚拟主机的10G优越不是一点两点,几个朋友一起用都不是什么问题
  • 最重要的是,境外VPS还有其他的用途:比如翻墙梯

当然,国外VPS也有个比不了的,那就是速度没有国内的虚拟主机快,但不需要备案啊!!!而且,就一个小网页,速度差别几乎感受不到。

错误现象

自己的VPS就需要自己维护了,就是一台远程的电脑,你自己能折腾出花来都行。自己不会折腾找卖家装好环境自己用就行。
一般搭网站用LNMP环境,对于静态网站,nginx默认设置就行,但对于WordPress或者Typecho这种动态站点,默认会出现设置带目录的链接时无法打开的情况。

http://xxx.com/blog/
http://xxx.com/2018/
http://xxx.com/%postname%.html

不加设置以上形式的链接都会出现404错误。

解决办法

不支持目录链接是因为缺少伪静态规则,我们只需要按以下方法添加伪静态即可。

添加伪静态规则

Nginx环境下WordPress或者Typecho伪静态规则如下:

location / { 
  index index.html index.php; 
  if (-f $request_filename/index.html) { 
    rewrite (.*) $1/index.html break; 
  } 
  if (-f $request_filename/index.php) { 
    rewrite (.*) $1/index.php; 
  } 
  if (!-f $request_filename) { 
    rewrite (.*) /index.php; 
  } 
}

以上规则在nginx或者vhost配置中修改都可以。保存配置后,重启nginx一般就正常了。

开启PATHINFO

如果Typecho访问内页出现No input file specified错误提示,那么是没有开启pathinfo的支持。

方法:

找到/usr/local/php/etc/php.ini文件,将cgi.fix_pathinfo=0 改成 cgi.fix_pathinfo=1,保存后输入命令:service php-fpm重启php-fpm 即可。

nginx 安装 SSL 证书

昨天在腾讯云那里申请了SSL证书,打算部署到服务器上,但是安装过程遇到了问题:

[root@izuf6hed2mdyv56471078kz sbin]# ./nginx -s reload
nginx: [emerg] unknown directive "ssl" in
/usr/local/nginx/conf/nginx.conf:91

于是查看了下nginx的安装信息,发现ssl模块并未被安装:

[root@izuf6hed2mdyv56471078kz sbin]# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.11.6
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)
configure arguments:

原因大概是:在安装nginx的时候,没有将ssl模块编译进nginx

解决办法是:重装nginx,并在执行configure命令的时候加得上 –with-http_ssl_module 参数。

由于新的nginx版本已经出来了,就借此机会顺便升级一下吧。

步骤一、下载nginx解压

wget http://nginx.org/download/nginx-1.13.8.tar.gz

# 默认下载到root
cd /root

# 解压
tar xzvf nginx-1.13.8.tar.gz

# 进入解压后的目录
cd nginx-1.13.8

步骤二、安装nginx,并添加ssl模块

./configure --with-http_ssl_module

好吧,又报错了:

./configure: error: SSL modules require the OpenSSL library.
You can either do not enable the modules, or install the OpenSSL library
into the system, or build the OpenSSL library statically from the source
with nginx by using --with-openssl=<path> option.

提示我装openssl,好吧,我装。

步骤一点五、安装OpenSSL

这是centos 的安装方法:

yum -y install openssl openssl-devel

好,继续执行步骤二。

步骤二(续)、安装nginx,并添加ssl模块

cd /root/nginx-1.13.8
 ./configure --with-http_ssl_module

请先备份好nginx相关的文件!

请先备份好nginx相关的文件!

请先备份好nginx相关的文件!

然后是安装:

# 网上说这么做会覆盖之前的版本,我测试了下,似乎没有什么大问题...
# nginx.conf也没有变化,若心存疑虑,可以参考其他教程
make install

# 查看版本是否升级
/usr/local/nginx/sbin/nginx -V

步骤三、配置

编辑/usr/local/nginx/conf/nginx.conf 添加配置:

server {
        listen 443 ssl;
        server_name www.zxxblog.cn;
        ssl on;
        ssl_certificate /home/key/1_www.zxxblog.cn_bundle.crt;
        ssl_certificate_key /home/key/2_www.zxxblog.cn.key;
        ssl_session_timeout  5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 
        ssl_ciphers  HIGH:!aNULL:!MD5;
        #ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;   
        ssl_prefer_server_ciphers   on;   

        # !! 这下面的是我自己的配置,不同的人可能有所不同 !!
        location / {
            proxy_pass http://localhost:8080;
        }
}

步骤四、重启nginx

# 如果不成功,就kill掉nginx,然后再打开
/usr/local/nginx/sbin/nginx -s reload

然后,我在阿里云的安全组里添加了443端口,搞定!

效果:

未分类

nginx+php-fpm出现502 bad gateway错误解决方法

1. php-fpm进程数不够用

使用 netstat -napo |grep “php-fpm” | wc -l 查看一下当前fastcgi进程个数,如果个数接近conf里配置的上限,就需要调高进程数。

但也不能无休止调高,可以根据服务器内存情况,可以把php-fpm子进程数调到100或以上,在4G内存的服务器上200就可以。

2. 调高调高linux内核打开文件数量

可以使用这些命令(必须是root帐号)

echo ‘ulimit -HSn 65536’ >> /etc/profile

echo ‘ulimit -HSn 65536’ >> /etc/rc.local

source /etc/profile

3. 脚本执行时间超时

如果脚本因为某种原因长时间等待不返回 ,导致新来的请求不能得到处理,可以适当调小如下配置。

nginx.conf里面主要是如下

fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;

php-fpm.conf里如要是如下

request_terminate_timeout = 10s

4. 缓存设置比较小

修改或增加配置到nginx.conf

proxy_buffer_size 64k;
proxy_buffers  512k;
proxy_busy_buffers_size 128k;

5. recv() failed (104: Connection reset by peer) while reading response header from upstream

可能的原因机房网络丢包或者机房有硬件防火墙禁止访问该域名

但最重要的是程序里要设置好超时,不要使用php-fpm的request_terminate_timeout,

最好设成request_terminate_timeout=0;

因为这个参数会直接杀掉php进程,然后重启php进程,这样前端nginx就会返回104: Connection reset by peer。这个过程是很慢,总体感觉就是网站很卡。

Nginx配置图片防盗链

为了防止其他站点直接从我们网站引用图片等链接,消耗了我们服务器资源和网络流量,我们一般会对图片等资源做一些限制,比如打水印,防盗链设置等,本文主要结合Nginx来讲解如何设置图片防盗链。

我们所说的防盗链功能是都是基于 HTTP 协议支持的 Referer 机制,通过 referer 跟踪来源,对来源进行识别和判断。 利用这个策略,我们基本可以防止其他站点直接链接我们站上的图片。 举个例子,如果a.com网站的页面调用了我站的图片:https://www.helloweba.net/p.jpg,我们通过Nginx来判断它的来源域,不属于www.helloweba.net过来的图片都返回403,即禁止访问。

打开对应站点的conf配置文件,有关Nginx站点配置文件可以参考: https://www.helloweba.net/server/504.html ,主要配置代码如下:

location ~*.(gif|jpg|jpeg|png|bmp|swf)$ { 
    valid_referers none blocked www.helloweba.net m.helloweba.net; 
    if ($invalid_referer) { 
        return 403; 
        #rewrite ^/ http://www.baidu.com/error.jpg; 
    } 
} 

以上代码解释如下:

1、location中指定要防篡改的文件类型,多个后缀用“|”符号分开。

2、valid_referers指定资源访问是通过以下几种方式为合法,即白名单,允许文件链出的域名白名单。

none:直接通过url访问,无referer值的情况

blocked:referer值被防火墙修改

servername:指定资源在合法的域名白名单中可以被引用,支持*通配符,多个域名使用空格符分开

3、if判断如果用户请求的资源不符合上述配置,那么rewrite重定向到你想指定的url上,也可以配置403权限错误。

以上设置差不多就可以起到防盗链作用了,但是,这样并不是彻底地实现真正意义上的防盗链!

我们应该注意设置:

location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$
        {
            expires      30d;
            valid_referers m.helloweba.net www.helloweba.net;
            if ($invalid_referer) {
                #rewrite ^/ http://www.baidu.com/a.html; 
                return 403;
            }
        }

expires 30d;属于配置文件中location作用域中原有的图片缓存时间配置,这里我们把两个location合并在一起。

接着,我们去掉none blocked两个关键词,目的是直接在浏览器地址栏中输入对应的图片地址也会被拒绝访问。

如果匹配到不属于设定的referer来源域,则返回403,或者重置到一个url地址上去,这样可以避免右键另存为的方式下载图片。

当然,话又说回来,如果人家真想获得你的图片还是有办法的,比如各种伪造referer来源等方法。

还有一种情况,如果我们站点使用CDN,那么在nginx上的防盗链配置似乎不起作用了,别担心,找CDN厂商,他们有一整套资源防盗链方法,大多在CDN管理平台直接设置即可,比如阿里云CDN,其原理也是判断referer。