Nginx 启用 Brotli 压缩

Brotli 是 Google 开发的一种压缩格式,它通过内置分析大量网页得出的字典,实现了更高的压缩比率,同时几乎不影响压缩 / 解压速度。

本站通过 ngx_brotli 模块来让 Nginx 支持 Brotli 压缩方式。本文介绍其配置方式。

安装模块

若要启用 ngx_brotli 模块,需要在编译 Nginx 时,加入相应模块:

# get source

git clone https://github.com/google/ngx_brotli.git

cd ngx_brotli

git submodule update --init

cd ..

# configure

./configure ... --add-module=../ngx_brotli

配置文件

安装完成 ngx_brotli 模块后,你就可以在配置文件里启用它了:

# 配置段: http, server, location

# 开启 ngx_brotli 压缩

brotli on;

# 指定压缩数据的最小长度,只有大于或等于最小长度才会对其压缩。这里指定 20 字节

brotli_min_length 20;

# Brotli 请求缓冲区的数量和大小

brotli_buffers 16 10k;

# Brotli 使用的窗口值。默认值为 512k

brotli_window 512k;

# 压缩水平可以是 0 到 11,默认值是 6。太高的压缩水平对性能提升并没有太大好处,因为这需要更多的 CPU 时间

brotli_comp_level 6;

# 指定允许进行压缩的回复类型

brotli_types text/html text/xml text/plain application/json text/css image/svg application/font-woff application/vnd.ms-fontobject application/vnd.apple.mpegurl application/javascript image/x-icon image/jpeg image/gif image/png;

# 是否允许查找预处理好的、以 .br 结尾的压缩文件。可选值为 on、off、always

brotli_static always;

未分类

Nginx+SSL+Tomcat+CDN部署总结

之前在度娘搜索资料,无意间看到一些个人站点的博客都用了https协议,在浏览器地址栏中被标记为绿色的“安全”,前些天特地给自己负责的小项目升级成https协议,其优点这里不再赘述,小伙伴们可以自行百度,今天把整合部署分享在这里,希望小伙伴们少走弯路~

效果如下:

未分类

软件版本如下:

未分类

一、生成SSL证书

首先我们创建一个用来存放letsencrypt生成证书项目的路径并进入:

cd /usr/local/letsencrypt

接下来我们克隆letsencrypt项目:

git clone https://github.com/letsencrypt/letsencrypt

开始生成SSL证书:

./letsencrypt-auto certonly --standalone --email [email protected] -d www.test1.com -d www.test2.com --agree-tos

这里一定注意:

(1). 域名绑定在国内DNS服务器无法生成,需要先将DNS服务器切换到DNS服务商,例如ClouldFlare、Godaddy、Dnsever后才能正常生成!
(2). web服务需要处于关闭状态,注意关闭nginx和80端口的占用!(不间断服务方式生成可以自行百度)
(3). -d 代表domain 可以同时生成多个域名对应证书,生成后我们可以在默认目录中看到:

/etc/letsencrypt/live/www.test.com/
cert.pem(用户证书) 
chain.pem(中间证书) 
fullchain.pem(证书链) 
privkey.pem(证书私钥)

最后我们生成Perfect Forward Security(PFS)键值,具体作用可以自行百度:

mkdir /etc/ssl/private/ -p
cd /etc/ssl/private/
openssl dhparam 2048 -out dhparam.pem

二、Nginx配置SSL证书及Tomcat代理

    #Tomcat 8080端口
    upstream tomcat_8080{
        server    127.0.0.1:8080  weight=1;
    }

    #将所有http协议内容重定向到https协议
    server {
        listen 80;
        server_name www.test.com;
        rewrite ^ https://$server_name$request_uri? permanent;
    }

    #https协议
    server {
        listen 443;
        server_name www.test.com;

        # letsencrypt生成的文件
        ssl on;
        ssl_certificate /etc/letsencrypt/live/www.test.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/www.test.com/privkey.pem;

        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:50m;
        ssl_session_tickets on;

        ssl_dhparam /etc/ssl/private/dhparam.pem;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        # 一般推荐使用的ssl_ciphers值: https://wiki.mozilla.org/Security/Server_Side_TLS
        ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK';
        ssl_prefer_server_ciphers on;

        # 代理tomcat
        location / {   
            proxy_set_header    Host                $http_host;             
            proxy_set_header    X-Real-IP           $remote_addr;     
            proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;     
            proxy_set_header    Cookie              $http_cookie;
            proxy_pass          http://tomcat_8080;
            #proxy_redirect     default;
        }

        access_log /home/wwwlogs/www.test.com_access.log;
        error_log  /home/wwwlogs/www.test.com_error.log;
    }

三、Tomcat的SSL配置

1、Connector节点将redirectPort=”8443″修改为 redirectPort=”443″ proxyPort=”443″最终为:

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="443" proxyPort="443" />

2、找到Engine节点,在最后一个Host标签后加入:

<Host name="www.test.com" debug="0" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
    <Valve className="org.apache.catalina.valves.RemoteIpValve"
        remoteIpHeader="x-forwarded-for"
        remoteIpProxiesHeader="x-forwarded-by"
        protocolHeader="x-forwarded-proto"/>
    <Context docBase="/www/java/projectName" path="" crossContext="true" debug="3" privileged="true"  reloadable="false" deubt="true" />
</Host>

四、CloudFlare CDN设置(这里很重要)

我们将域名解析到自己服务器后,点击Crypto选项卡,将SSL状态修改为Full(strict)模式,在这种模式下会使用你服务器中的ssl证书,否则会导致页面无限301跳转,导致chrome提示重定向次数过多,请求失败!

php nginx 实时输出

PHP 里开启实时输出方法是ob_implicit_flush(),
但它大部分情况下都不管用,

因为php.ini配置里output_buffering输出缓冲大部分是On开启的,
还有zlib.output_compression也经常会被开启,

除了 PHP 这一层,还有 Nginx 的缓冲设置proxy_buffering,和压缩gzip也大都是开启的。
为了一两个页面的需求,修改整个服务器的网站配置,恐怕没有人会做这种选择。

这里推荐一下简单的方法:

set_time_limit(0);
ob_end_clean();
ob_implicit_flush();

header('X-Accel-Buffering: no'); // 关键是加了这一行。

echo '现在是:'.date('H:i:s').'<br>';
sleep(5);
echo '五秒后:'.date('H:i:s');

使用 Nginx 过滤网络爬虫

现在有许多初学者学习网络爬虫,但他们不懂得控制速度,导致服务器资源浪费。通过 Nginx 的简单配置,能过滤一小部分这类爬虫。

方法一:通过 User-Agent 过滤

Nginx 参考配置如下:

    location / {
        if ($http_user_agent ~* "scrapy|python|curl|java|wget|httpclient|okhttp") {
            return 503;
        }
        # 正常请求
    }

这里只列出了部分爬虫的 User-Agent,需要更多请参考:GitHub – JayBizzle/Crawler-Detect

注意:User-Agent 很容易修改

方法二:block IP

通过禁止某个 IP 或者某个 IP 段访问,也能起到一定效果。 Nginx 示例配置如下:

deny 178.238.234.1;
deny 1.32.128.0/18;

方法三:rate limit

通过限制某个 IP 的访问频率,避免一部分 CC (Challenge Collapsar)攻击。

Nginx 示例配置如下:

http{ 
    #定义一个名为allips的limit_req_zone用来存储session,大小是10M内存,
    #以$binary_remote_addr 为key,限制平均每秒的请求为20个,
    #1M能存储16000个状态,rete的值必须为整数,
    #如果限制两秒钟一个请求,可以设置成30r/m
    limit_req_zone $binary_remote_addr zone=allips:10m rate=20r/s;
    ...
    server{
        ...
        location {
            ...
            #限制每ip每秒不超过20个请求,漏桶数burst为5
            #brust的意思就是,如果第1秒、2,3,4秒请求为19个,
            #第5秒的请求为25个是被允许的。
            #但是如果你第1秒就25个请求,第2秒超过20的请求返回503错误。
            #nodelay,如果不设置该选项,严格使用平均速率限制请求数,
            #第1秒25个请求时,5个请求放到第2秒执行,
            #设置nodelay,25个请求将在第1秒执行。

            limit_req zone=allips burst=5 nodelay;
            ...
        }
        ...
    }
    ...
}

当然,攻击者也可以使用代理IP来破除频率限制。建议在网站前面加一层 CDN。

使用systemctl设置Nginx、PHP、Mysql开机启动

CentOS 7继承了RHEL 7的新的特性,例如强大的systemctl,而systemctl的使用也使得以往系统服务的/etc/init.d的启动脚本的方式就此改变,也大幅提高了系统服务的运行效率。但服务的配置和以往也发生了极大的不同,说实在的,变的简单而易用了许多。

CentOS 7的服务systemctl脚本存放在:/usr/lib/systemd/,有系统(system)和用户(user)之分,像需要开机不登陆就能运行的程序,最好还是存在系统服务里面,即:/usr/lib/systemd/system目录下,每一个服务以.service结尾,一般会分为3部分:[Unit]、[Service]和[Install]

我们可以使用systemctl -a来查看所有服务,如果列表里面没有Nginx,PHP、Mysql,又想借助于systemctl来进行统一管理的话,就到上述所说的/usr/lib/systemd/system目录下面创建以下文件吧

Nginx之nginx.service文件[自定义]

[Unit]
Description=nginx
After=network.target
[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target

PHP之php.service文件[自定义]

[Unit]
Description=php
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/php/sbin/php-fpm
ExecStop=/bin/pkill -9 php-fpm
PrivateTmp=true
[Install]
WantedBy=multi-user.target

Mysql之mysqld.service文件[安装的时候软件自动生成]

[Unit]
Description=MySQL Community Server
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
Alias=mysql.service
[Service]
User=mysql
Group=mysql
#systemctl status就是根据pid来判断服务的运行状态的
PIDFile=/var/run/mysqld/mysqld.pid
# 以root权限来启动程序
PermissionsStartOnly=true
# 设置程序启动前的必要操作。例如初始化相关目录等等
ExecStartPre=/usr/bin/mysql-systemd-start pre
# 启动服务
ExecStart=/usr/bin/mysqld_safe
# Don't signal startup success before a ping works
ExecStartPost=/usr/bin/mysql-systemd-start post
# Give up if ping don't get an answer
TimeoutSec=600
#Restart配置可以在进程被kill掉之后,让systemctl产生新的进程,避免服务挂掉
Restart=always
PrivateTmp=false

上述文件创建完成后,只要使用systemctl enable 命令就可以将所编写的服务添加至开机启动了。例如:

#将php服务添加至开机启动。执行enable命令后,会自动创建一个软链接/etc/systemd/system/multi-user.target.wants/php.service指向此文件。
systemctl enable php.service
#查看php是否已设置为开机启动
systemctl is-enabled php.service

注意

[Unit]部分主要是对这个服务的说明,内容包括Description和After,Description用于描述服务,After用于描述服务类别

[Service]部分是服务的关键,是服务的一些具体运行参数的设置,这里 Type=forking是后台运行的形式,PIDFile为存放PID的文件路径,
ExecStart为服务的具体运行命令,ExecReload为重启命令,ExecStop为停止命令,PrivateTmp=True表示给服务分配独立的临时空间。[Service]部分的启动、重启、停止命令全部要求使用绝对路径,使用相对路径则会报错!

[Install]部分是服务安装的相关设置,可设置为多用户的

服务脚本按照上面编写完成后,以754的权限保存在/usr/lib/systemd/system目录下,这时就可以利用systemctl进行配置了

使用systemctl start [服务名(也是文件名)]可以测试服务是否可以成功运行,如果不能运行则可以使用systemctl status [服务名(也是文件名)]查看错误信息和其他服务信息。然后根据报错进行修改,直到可以start,如果不放心还可以测试restart和stop命令。

Nginx支持HTTPS并且支持反爬虫

自己写了若干爬虫, 但是自己的网站也有人爬, 呵呵, 这里介绍一种Nginx反爬.我在阿里云只开放80端口, 所有一般端口都通过Nginx进行反向代理. 通过Nginx, 我们还可以拦截大部分爬虫.

然后我们再给自己的网站加上HTTPS支持.

Nginx安装

我的系统如下:

jinhan@jinhan-chen-110:~/book/Obiwan/bin$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.3 LTS
Release:    16.04
Codename:   xenial

安装(如果有apache服务器, 建议卸载了, 或者改Nginx的默认端口):

sudo apt-get install nginx

此时已经开启了80端口, 并且配置处在etc/nginx

lsof -i:80

cd /etc/nginx

Nginx服务一般配置

将配置放于conf.d/*

PHP配置(可忽视)

server{
    listen 80;
    server_name php.lenggirl.com;
    charset utf-8;
    access_log /data/logs/nginx/www.lenggirl.com.log;
    #error_log /data/logs/nginx/www.lenggirl.com.err;

    location / {
            root   /data/www/php/blog;
        index index.html index.php;
        #访问路径的文件不存在则重写URL转交给ThinkPHP处理
        if (!-e $request_filename) {
            rewrite  ^/(.*)$  /index.php/$1  last;
            break;
        }
    }

    ## Images and static content is treated different
    location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {

        access_log        off;
        expires           30d;
        root /data/www/php/blog;
     }

    location ~.php/?.*$ {
    root        /data/www/php/blog;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        #加载Nginx默认"服务器环境变量"配置
        include        fastcgi.conf;

        #设置PATH_INFO并改写SCRIPT_FILENAME,SCRIPT_NAME服务器环境变量
        set $fastcgi_script_name2 $fastcgi_script_name;
        if ($fastcgi_script_name ~ "^(.+.php)(/.+)$") {
            set $fastcgi_script_name2 $1;
            set $path_info $2;
        }
        fastcgi_param   PATH_INFO $path_info;
        fastcgi_param   SCRIPT_FILENAME   $document_root$fastcgi_script_name2;
        fastcgi_param   SCRIPT_NAME   $fastcgi_script_name2;        
    }
}

Go配置

通过server_name, 用域名访问, 全部会到80端口, 根据域名会转发到8080

域名请A记录到该机器IP地址.

vim /etc/nginx/conf.d/www.lenggirl.com.conf

server{
    listen 80;
    # 本地测试时可以将域名改为: 127.0.0.1
    server_name www.lenggirl.com;
    charset utf-8;
    access_log /root/logs/nginx/www.lenggirl.com.log;
    #error_log /data/logs/nginx/www.lenggirl.com.err;
    location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://localhost:8080;

    # 这个就是反爬虫文件了
    include /etc/nginx/anti_spider.conf;
    }   
}

日志文件要先建立:

sudo mkdir -p /root/logs/nginx

查看配置是否无误, 并重启:

sudo nginx -t
sudo service nginx restart
sudo nginx -s reload

访问127.0.0.1会发现502错误, 因为8080端口我们没开! 此时访问localhost会发现, 这时Nginx欢迎页面出来了, 这是默认80端口页面!

反爬虫配置

增加反爬虫配额文件:

sudo vim /etc/nginx/anti_spider.conf

#禁止Scrapy等工具的抓取  
if ($http_user_agent ~* (Scrapy|Curl|HttpClient)) {  
     return 403;  
}  

#禁止指定UA及UA为空的访问  
if ($http_user_agent ~ "WinHttp|WebZIP|FetchURL|node-superagent|java/|FeedDemon|Jullo|JikeSpider|Indy Library|Alexa Toolbar|AskTbFXTV|AhrefsBot|CrawlDaddy|Java|Feedly|Apache-HttpAsyncClient|UniversalFeedParser|ApacheBench|Microsoft URL Control|Swiftbot|ZmEu|oBot|jaunty|Python-urllib|lightDeckReports Bot|YYSpider|DigExt|HttpClient|MJ12bot|heritrix|EasouSpider|Ezooms|BOT/0.1|YandexBot|FlightDeckReports|Linguee Bot|^$" ) {  
     return 403;               
}  

#禁止非GET|HEAD|POST方式的抓取  
if ($request_method !~ ^(GET|HEAD|POST)$) {  
    return 403;  
}  

#屏蔽单个IP的命令是
#deny 123.45.6.7
#封整个段即从123.0.0.1到123.255.255.254的命令
#deny 123.0.0.0/8
#封IP段即从123.45.0.1到123.45.255.254的命令
#deny 124.45.0.0/16
#封IP段即从123.45.6.1到123.45.6.254的命令是
#deny 123.45.6.0/24

# 以下IP皆为流氓
deny 58.95.66.0/24;

在网站配置server段中都插入include /etc/nginx/anti_spider.conf, 见上文. 你可以在默认的80端口配置上加上此句:sudo vim sites-available/default

重启:

sudo nginx -s reload

爬虫UA常见:

FeedDemon             内容采集  
BOT/0.1 (BOT for JCE) sql注入  
CrawlDaddy            sql注入  
Java                  内容采集  
Jullo                 内容采集  
Feedly                内容采集  
UniversalFeedParser   内容采集  
ApacheBench           cc攻击器  
Swiftbot              无用爬虫  
YandexBot             无用爬虫  
AhrefsBot             无用爬虫  
YisouSpider           无用爬虫(已被UC神马搜索收购,此蜘蛛可以放开!)  
jikeSpider            无用爬虫  
MJ12bot               无用爬虫  
ZmEu phpmyadmin       漏洞扫描  
WinHttp               采集cc攻击  
EasouSpider           无用爬虫  
HttpClient            tcp攻击  
Microsoft URL Control 扫描  
YYSpider              无用爬虫  
jaunty                wordpress爆破扫描器  
oBot                  无用爬虫  
Python-urllib         内容采集  
Indy Library          扫描  
FlightDeckReports Bot 无用爬虫  
Linguee Bot           无用爬虫  

使用curl -A 模拟抓取即可,比如:

# -A表示User-Agent
# -X表示方法: POST/GET
# -I表示只显示响应头部
curl -X GET -I -A 'YYSpider' localhost

HTTP/1.1 403 Forbidden
Server: nginx/1.10.3 (Ubuntu)
Date: Fri, 08 Dec 2017 10:07:15 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive

模拟UA为空的抓取:

curl -I -A ' ' localhost

模拟百度蜘蛛的抓取:

curl -I -A 'Baiduspider' localhost

支持https

见: https://certbot.eff.org/#ubuntuxenial-other

首先执行:

apt-get update
apt-get install software-properties-common
add-apt-repository ppa:certbot/certbot
apt-get update
apt-get install python-certbot-nginx 

然后执行这个按说明操作:

certbot --authenticator standalone --installer nginx --pre-hook "nginx -s stop" --post-hook "nginx"

之前/etc/nginx/conf.d/www.lenggirl.com.conf文件变更如下:

server{
        listen 80;
        # 本地测试时可以将域名改为: 127.0.0.1
        server_name www.lenggirl.com;
        charset utf-8;
        access_log /root/logs/nginx/www.lenggirl.com.log;
        #error_log /data/logs/nginx/www.lenggirl.com.err;
        location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://localhost:4000;

        # 这个就是反爬虫文件了
        # include /etc/nginx/anti_spider.conf;
        #
}

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/www.lenggirl.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/www.lenggirl.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    } # managed by Certbot

}

那些实用的Nginx规则

一、概述

大家都知道Nginx有很多功能模块,比如反向代理、缓存等,这篇文章总结下我们这些年实际环境中那些有用的Nginx规则和模块,大部分是用法的概括及介绍,具体细节在实际配置时再自行google。

二、内置语法

先介绍Nginx默认已支持的内置功能,靠这些基本就满足大部分的web服务需求。

2.1 proxy代理

proxy常用于两类应用场景,一类是中转,如异地科学的上网方式,另外一类是到后端服务的负载均衡方案。

用反向代理时候,需要特别注意里面的域名默认是在nginx启动时候就解析了,除非reload否则一直用的是当初解析的域名,也就是说不能动态解析。

但这个问题是可以通过别的模块或者用内置字典变量方式来解决。

resolver 114.114.114.114;
server {
    location / {
        set $servers github.com;
        proxy_pass http://$servers;
    }
}

2.1.1 中转

针对某个域名进行中转:

server {
listen 172.16.10.1:80;
    server_name pypi.python.org;
    location ~ /simple {
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://pypi.python.org;
    }
}

注意如果是前后端域名不一样的话需要处理proxy_redirect的301跳转之类的显示,否则在跳转时候会跳转到proxy_pass的域名。

另外可以直接代理所有80端口的http流量:

server {
    listen 80;
    server_name _;
    resolver 114.114.114.114;
    set $URL $host;
    location / {
        proxy_pass http://$URL;
    }
}

如果是想代理https的站点也不是不可能,只是需要自行处理CA证书导入即可,而且经过https中转的流量对nginx是透明的,也就是有证书的时候做窃听和劫持的情况。

2.1.2 负载均衡

这是代理的另外一个常见用法,通过upstream到多个后端,可以通过weight来调节权重或者backup关键词来指定备份用的后端,通常默认就可以 了,或者可以指定类似ip_hash这样的方式来均衡,配置很简单,先在http区域添加upstream定义:

upstream backend {
    ip_hash;
    server backend1.example.com weight=5;
    server backend2.example.com weight=5;;
}

然后在server里面添加proxy_pass:

location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

做负载均衡的时候可以智能识别后端服务器状态,虽然可以智能地proxy_next_upstream到另外的后端,但还是会定期损失一些正常的“尝试性”的连接,比如过了max_fails 次尝试之后,休息fail_timeout时间,过了这个时间之后又会去尝试,这个时候可以使用第三方的upstream_check模块来在后台定期地自动探索,类似这样:

check interval=3000 rise=2 fall=5 timeout=2000 type=http;

这样替代用户正常的连接来进行尝试的方式进一步保障了高可用的特性。

还有就是在做前端代理的时候也是这样的方式,直接proxy_pass到后端即可,比如CDN的场景。

2.2 防盗链

普通的防盗链是通过referer来做,比如:

location ~* .(gif|jpg|png|bmp)$ {
    valid_referers none blocked *.example.com server_names ~.google. ~.baidu.;
    if ($invalid_referer) {
        return 403;
    }
}

再精细一点的就是URL加密,针对一些用户IP之类的变量生成一个加密URL通常是针对文件下载时候用到,可以通过openresty来写lua脚本或者是accesskey之类的模块来实现。

2.3 变量

nginx里面支持正则匹配和变量配置,默认的变量比如remote_addr、request_filename、query_string、server_name之类的,这些组合在一起可以做很多规则,或者还有日志里面status、http_cookie等。

还有在进行多域名配置时候可以用通配符,比如:

server_name ~^(www.)?(.+)$;
root /data/web/$2;

这样就实现了自动进行域名的目录指派。

变量方面,比如配置变量a=1:

set $a 1;

下面这个案例配合if判断来做有更大的用处。

2.4 if判断

nginx里面支持一些简单的if判断,但是没有多重逻辑的语法,多个判断条件用起来需要结合变量的方式来实现,比如允许ip地址为10.10.61段和和192.168.100段的用户访问,其余的拒绝,返回405状态码:

set $err 0;
    if ( $remote_addr ~ 10.10.61.){
        set $err 0;
    }
    if ( $remote_addr ~ 192.168.100.){
        set $err 0;
    }
    if ( $err = 1){
        return 405;
    }

这样通过一个err变量比较巧妙实现了需求。

2.5 error_page

有用到后端proxy的地方需要加上这句话才可以传到状态码到nginx:

fastcgi_intercept_errors on;

具体配置一般是配置到具体的错误URL页面,比如:

#返回具体状态码
error_page 404 403 /4xx.html
#返回200状态码
error_page 404 403 =200  /error.html

或者采用callback的方式统一做处理:

error_page 404 403 = @fallback; 
location @fallback {
    proxy_pass http://backend;
    access_log /data/logs/404_error.log access;
}

这样在重定向时不会改变URL,然后把404页面直接返回。

2.6 rewrite

rewrite做一些301、302之类的跳转,同时也可以在CDN前端做“去问号”缓存的效果。

location /db.txt {
    rewrite (.*) $1? break;
    include proxy.conf;
}

另外最常见的跳转写法:

rewrite ^/game/(.*) /$1;

把/game/test跳转为/test的效果,注意这样是没有状态码的,如果访问正常就直接返回200状态码。

可以在后面加个permanent参数,就变为了301 Moved Permanently,或者添加redirect改为302跳转。

同理,还可以进行多个正则匹配进行URL重组,比如:

rewrite ^/download/(.*)/lastest/(.*)$ /file/$1?ver=$2 break;

2.7 日志字段

想针对每个连接进行日志留档,可以在nginx日志那里配置好字段,比如记录cookie之类的数据。

在log_format字段里面加入$http_cookie变量即可。

另外post的数据可以永久保留在文件里面,比如用来做http的日志备份,包括get和post的原始数据,把这个值开启即可:

client_body_in_file_only  on;

然后post的数据就会保存在nginx/client_body_temp文件夹里面。

2.8 internal关键词

这个关键词很少见,但有时候是很有用的,比如在有很多规则时候,突然需要针对某个目录转为nginx内部处理。

location ^~ /upload/down/ {
alias /data/web/dts/dtsfile/down/;
internal;
}

2.9 try_files

字面意思是尝试,后面可以接多个目录或者文件,比如kohana框架:

try_files $uri /index.php?$query_string;

先看是否有URL这个文件,没有的话再调用index.php来处理,或者支持状态码处理:

try_files /foo /bar/ =404;

没有这两个文件的话返回404状态。

2.10 auth认证

可以做简单的用户登录认证方式,其中的passwd_file得通过apache的htpasswd命令来生成。

auth_basic "Restricted";
auth_basic_user_file passwd_file;

认证通过之后每次访问会在头部添加Authorization字段包含用户名密码的base64加密密文给服务端。

2.11 gzip

普通的线上web站点gzip压缩是必须要开的,压缩一些文本类型的文件再返回给用户。

注意必须手动指定全需要压缩的类型,比如css、js之类的,线上配置如下:

gzip on;
gzip_min_length  2048;
gzip_buffers     4 16k;
gzip_vary   on;
gzip_http_version 1.1;
gzip_types  text/plain  text/css text/xml application/xml application/javascript application/x-javascript ;

2.12 mime配置

很久以前基本是忽略这个配置,但手游流行之后就发现异常了,需要让手机浏览器知道返回的apk后缀是什么类型,否则类似IE浏览器会以zip后缀返回,需要加上:

application/vnd.android.package-archive apk;
application/iphone pxl ipa;

2.13 限速

限速包括限制请求的并发数和请求的下载速度。

简单的限制某个线程的下载速度就直接加上一句话就可以了:

limit_rate 1024k;

要限制某个IP的并发数之类的就需要用ngx_http_limit_req_module和ngx_http_limit_conn_module模块了,不过是默认就编译好的。

比如使用一个 10M 大小的状态缓存区,针对每个IP每秒只接受20次的请求:

limit_req_zone $binary_remote_addr zone=NAME:10m rate=20r/s;

2.14 location匹配

location匹配有多种方式,常见的比如

location  = / 
location  / 
location ^~ /test{

是有优先级的,直接 ”=” 的优先级是最高的,一般就用”~”这个符号来匹配php就好了,不过是区分了大小写的:

location ~ .*.php$

2.15 文件缓存

返回给用户的文件一般都配置了过期时间,让浏览器缓存起来。

比如缓存14天:

expires 14d;

针对某些特殊的文件就需要location匹配之后进行禁止缓存配置:

add_header Cache-Control no-cache;
add_header Cache-Control no-store;
expires off;

2.16 缓存文件

nginx可以作为ATS这样的缓存服务器来缓存文件,配置也比较简单,不过我们很少用,除非一些特殊的场合,参考配置:

#先在全局下面定义好缓存存放的目录
proxy_cache_path  /data/cache/ levels=1:2 keys_zone=cache_one:10m inactive=7d max_size=10g;
proxy_temp_path   /data/cache/proxy_temp_path;
proxy_cache_key   $host$uri$is_args$args;
#然后在server里面的location匹配好目的文件,加入下一段即可
proxy_cache cache_one;
proxy_cache_valid 200 304 24h;
proxy_cache_valid any 10m;
proxy_pass https://$host;
proxy_cache_key $host$uri$is_args$args;
add_header  Nginx-Cache "$upstream_cache_status"; 3. 内置模块

三、内置模块

nginx含有大量的模块可以支持多种复杂的需求,比如源码目录src/http/modules里面就有很多c模块的代码,或者直接通过./configure –help|grep module来查看有哪些内置模块,编译时候直接加上就可以了。

除了nginx内置的模块,网络上还有很多第三方的模块,可以通过编译时候加参数–add-module=PATH指定模块源码来编译。

下面介绍一些我们线上用过而且比较赞的内置模块。

3.1 stream

端口转发的模块,从nginx1.9版本才开始支持,包含tcp和udp的支持,和IPTABLES相比这个虽然是应用层,会监听端口,但是配置起来很方便,比IPTABLES灵活,在tcp模块下面添加类似vhost的server就可以了,方便自动化管理,参考配置:

server {
    listen PORT;
    proxy_pass IP:PORT;
    access_log /data/logs/tcp/PORT.log;
}

3.2 http_realip_module

nginx反向代理之后,如何让后端web直接获取到的IP不是反向代理的iP,而是直接获取到用户的真实IP呢,就需要这个模块了,不需要代码那里再做类似X-Real-IP的变量特殊判断。

3.3 http_slice_module

在做CDN时候可以用到,让一个大文件分片,分成多个小文件通过206断点续传到后端,然后再组合起来,避免大文件直接回源导致多副本和多次回源的问题。

3.4 http_secure_link_module

前面说到的防盗链可以用这个来做,但是这个一般是针对那种文件下载时候用到的,比如从网页下载时候,服务端生成一个加密URL给用户,然后这个URL有过期时间之类的,避免此URL被多次分享出去,不过普通的素材加载还是用普通的防盗链即可。

3.5 http_sub_module

替换响应给用户的内容,相对于sed之后再返回,比如可以在需要临时全局修改网站背景或者title时候可以一次性处理好。

四、扩展项目

简单介绍下大名鼎鼎的两个基于nginx的扩展项目,也是我们线上有很多地方用到的。

4.1 openresty

集成lua脚本,几乎可以完成任何普通web相关的需求。

比如URL加密进行防劫持和防盗链,服务端动态生成一串aes加密的URL给CDN,CDN的openresty解密之后用普通的URL转发到后端,然后再返回给用户正确的内容。

4.2 tengine

淘宝的nginx修改版,实现了很多nginx的收费功能或者是特殊功能,比如动态加载、concat合并请求,动态解析等。

我们python开发的后台基本都是用的这个版本,主要是利用了concat的合并素材的功能。

五、结语

Nginx是个非常实用软件,部分功能已经超越了普通的web服务定位,同时它具备开源、轻量、自动化等特性,能有效解决实际工作中很多特殊场景的需求,祝Nginx在全球的份额持续攀升~

使用awk分析nginx访问日志access.log的ip

access.log为nginx的访问日志,默认路径在

/var/log/nginx/access.log

分析access.log的ip命令如下:

awk '{print $1}' access.log |sort|uniq -c|sort -n
  1. 命令里使用awk过滤出访问的ip

  2. 使用sort对ip排序

  3. 对排序后的ip进行统计,统计每一个ip访问数

  4. 最后使用sort 对访问数进行排序,排序为升序。

NGINX 使用HTTP2

部署

使用HTTP2 首先必须部署SSL,走HTTPS协议 可参考NGINX服务器网站升级HTTPS

首先查看下nginx支持不支持http2,我是使用yum 安装的默认已经安装了模块,使用下面命令查看

nginx -V

查看下是否有下面的模块

--with-http_v2_module

修改虚拟主机配置

server {
    # listen 80;
    listen  443 ssl http2;
    #....
}

重启服务器,restart 而不是reload

service nginx restart

验证

第一种方法

在谷歌浏览器上打开你使用http2的站点,在浏览器地址栏输入chrome://net-internals/#http2,看下HTTP/2 sessions 里面有没有你的主机地址

第二种方法

在谷歌浏览器上打开你使用http2的站点,打开调试工具(F12),进入Network,刷新页面,然后在右键导航栏,勾选下’Protocol’,就可以看到在那一栏,显示h2

NGINX服务器网站升级HTTPS

要让网站升级成HTTPS,首先要申请或者购买ssl证书.

证书的获得

证书可以自己在服务器生成,但是这种证书的兼容性以及安全性都会存在比较大的问题.我们可以从专门的证书机构获取比较好的证书,比如可以通过阿里云去申请证书,但是这种证书有要求限制,只能免费使用一年.

NGINX配置

server {
        listen 443;
        server_name www.vstary.com
        ssl on;
        root html;
        index index.html index.htm;
        ssl_certificate   /home/test/cert/test.pem;
        ssl_certificate_key  /home/test/cert/test.key;
        ssl_session_timeout 5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        location / {
                root html;
                index index.html index.htm;
        }
}

重启服务器.

测试下你的域名,https://www.vstary.com,是否可以访问.

在证书正确的情况下,如果无法访问,可以查看下防火墙是否开放了端口,ssl貌似只能监听443接口.

修改允许端口参考: https://www.vstary.com/article/198

强制HTTP的也走HTTPS

修改服务器配置:

server {
        listen  80;
        server_name  www.vstary.com;
        rewrite ^(.*)$  https://$host$1 permanent;
}

同时允许HTTP跟HTTPS协议访问

    server {
            listen 80
            listen 443 ssl;
            server_name www.vstary.com
            ssl on;
            root html;
            index index.html index.htm;
            ssl_certificate   /home/test/cert/test.pem;
            ssl_certificate_key  /home/test/cert/test.key;
            ssl_session_timeout 5m;
            ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
            ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
            ssl_prefer_server_ciphers on;
            location / {
                    root html;
                    index index.html index.htm;
            }
    }