Web架构之Nginx全方位优化

目录

1、Nginx 安全优化

1.1、隐藏Nginx版本号

一般来说,软件的漏洞都和版本有关,这个很像汽车的缺陷,同一批次的要有问题就都有问题,别的批次可能就都是好的,Ngnx基于server_tokens关键字实现隐藏nginx版本号。

官方资料

server_tokens语法:

syntax:     server_tokens on|off;    #此行为参数语法,on为开启状态,off为关闭状态
default:    server_tokens on;        #此行意思是不配置该参数,软件默认情况的结果
context:    http,server,location     #此行为server_tokens参数可以放置的位置参数作用:激活或禁止Nginx的版本信息显示在报错信息和Server的响应首部位置中。

默认情况下,访问nginx会显示其版本号

$ curl -I 127.0.0.1
HTTP/1.1 200 OK
Server: nginx/1.16.1  # 这里清晰的暴露了Web版本号(1.6.1)及软件名称(nginx)
Date: Sat, 14 Mar 2020 06:43:31 GMT
Content-Type: application/octet-stream
Content-Length: 20
Connection: keep-alive

隐藏版本号实现:

Nginx主配置文件nginx.conf中的http标签段内加入"server_tokens off"

http
{
...............
server_tokens off;
...............
}
# 此参数放置在http标签内,作用是控制http response header内的Web服务版本信息的限制,以及错误信息中Web服务版本信息的显示

修改后重启nginx,使用curl -I查看结果

$ curl -I 127.0.0.1
HTTP/1.1 200 OK
Server: nginx  # 可以看到nginx版本号已经被隐藏了
Date: Sat, 14 Mar 2020 06:45:15 GMT
Content-Type: application/octet-stream
Content-Length: 20
Connection: keep-alive

1.2、Nginx更改默认用户

为了让Web服务更安全,要尽可能地改掉软件默认的所有配置,包括端口,用户等。

那么现在基于yum安装的nginx默认用户都为nginx

1.查看nginx进程

$ ps -ef | grep nginx | grep -v grep
root       2126      1  0 14:05 ?        00:00:00 nginx: master process nginx
nginx      3574   2126  0 14:45 ?        00:00:00 nginx: worker process

可以看到nginx worker进程是由nginx用户运行,当然,Nginx的主进程还是以root身份运行的。

2.查看nginx用户(默认已创建)

$ id nginx
uid=998(nginx) gid=996(nginx) groups=996(nginx)

3.nginx配置中指定用户启动

$ vim /etc/nginx/nginx.conf
user  nginx;   # 指定用户启动nginx

2、优化Nginx服务性能

2.1、Nginx指定进程数

在高并发,高访问量的Web服务场景,需要事先启动好更多的Nginx进程,以保证快速响应并处理大量并发用户的请求。

Nginx服务就相当于饭店,网站用户就相当于顾客,Nginx的进程就相当于服务员,下面就来优化Nginx进程的个数。

worker_processes语法:

syntax:         worker_processes number;    #此行为参数语法,number为数量
default:        worker_processes 1;     #此行意思是不匹配该参数,软件默认情况数量为1
context:        main;   #此行为worker_processes参数可以放置的位置

上述参数调整的是Nginx服务的worker进程数,Nginx有Master进程和worker进程之分,Master为管理进程,真正接待“顾客”的是worker进程。

优化Nginx进程个数的策略:

worker进程数最开始的设置可以等于CPU的核数,且worker进程数要多一些,这样起始提供服务时就不会出现因为访问量快速增加而临时启动新进程提供服务的问题,缩短了系统的瞬时开销和提供服务的时间,提升了服务用户的速度。高流量高并发场合也可以考虑将进程数提高至CPU核数*2,具体情况要根据实际的业务来选择,因为这个参数除了要和CPU核数匹配外,也和硬盘存储的数据及系统的负载有关,设置为CPU的核数是一个好的起始配置,这也是官方的建议。

实践修改Nginx配置:

假设服务器的CPU颗数为2颗,核数为4核,则初始的配置可通过查看默认的nginx.conf里的worker_processes数来了解,

$ vim /etc/nginx/nginx.conf
user  nginx;
worker_processes  4; # 这里修改参数值为CPU的总核数4,然后重新加载Nginx服务

现在检查修改后的worker进程数量:

$ ps -ef | grep nginx | grep -v grep
root       2126      1  0 11:02 ?        00:00:00 nginx: master process nginx
nginx      7039   2126  0 15:02 ?        00:00:00 nginx: worker process
nginx      7040   2126  0 15:02 ?        00:00:00 nginx: worker process
nginx      7041   2126  0 15:02 ?        00:00:00 nginx: worker process
nginx      7042   2126  0 15:02 ?        00:00:00 nginx: worker process

2.2、Nginx 进程优化

默认情况下,Nginx的多个进程有可能跑在某一个CPU或CPU的某一核上,导致Nginx进程使用硬件的资源不均,本节的优化是尽可能地分配不同的Nginx进程给不同的CPU处理,达到充分有效利用硬件的多CPU多核资源的目的。

在优化不同的Nginx进程对应不同的CPU配置时,四核CPU服务器的参数配置参考如下:

worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;
# worker_cpu_affinity就是配置Nginx进程与CPU亲和力的参数,即把不同的进程分给不同的CPU处理。这里0001 0010 0100 1000是掩码,分别代表第1,2,3,4核CPU,由于worker_processes进程数为4,因此,上述配置会把每个进程分配一核CPU处理,默认情况下进程不会绑定任何CPU,参数位置为main段。

四核和八核CPU服务器的参数配置参考如下:

# 八核掩码
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
# 四核掩码
worker_cpu_affinity 0001 0010 0100 1000;
# worker_cpu_affinity 的作用是绑定不同的worker进程数到一组CPU上。通过设置bitmask控制进程允许使用的CPU,默认worker进程不会绑定到任何CPU(自动平均分配。)

下面是绑定的实例配置:

worker_processes  4;
worker_cpu_affinity 0001 0010 0100 1000;
worker_processes  2;
worker_cpu_affinity 0101 1010;

# 最佳方式绑定方式 
worker_processes auto;
worker_cpu_affinity auto;

命令方式分配CPU

taskset -retrieve or ser a process’s CPU affinity 命令本身也有分配CPU的功能

例如:taskset -c 1,2,3 /etc/init.d/mysqld_start

       -c, --cpu-list
              specify  a  numerical  list  of processors instead of a bitmask.  The list may contain multiple items,
              separated by comma, and ranges.  For example, 0,5,7,9-11.

2.3、Nginx 事件模型优化

Nginx的连接处理机制在不同的操作系统会采用不同的额I/O模型,在Linux下,Nginx使用epoll的I/O多路复用模型,在Freebsd中使用kqueue的I/O多路复用模型,在Solaris中使用/dev/poll方式的I/O多路复用模型,在Windows中使用的是icop,等等。要根据系统类型选择不同的事件处理模型,可供使用的选择有“use [kqueue|rtsig|epoll|/dev/poll|select|poll];”。

事件模型语法:

synstax:  use method;  # 络模型配置,method选择模型之一
default:   --                # 默认没有设置
context:  events        # 网络模型配置放置events区块内

实践修改Nginx配置:

$ vi /etc/nginx/nginx.conf
# events 区块是一个用来设置连接进程的区块,例如:设置Nginx的网络I/O模型,以及连接数等。
events   { 
  use epoll;
}

根据Nginx官方文档建议,也可以不指定事件处理模型,Nginx会自动选择最佳的事件处理模型服务。

2.4、Nginx 最大连接数

调整Nginx单个进程允许的客户端最大连接数,这个控制连接数的参数为work_connections,worker_connections的值要根据具体服务器性能和程序的内存使用量来指定(一个进程启动使用的内存根据程序确定)

worker_connections语法:

synstax: worker_connections number
default: worker_connections 512
context: events

实践修改Nginx配置:

$ vi /etc/nginx/nginx.conf
events {
  worker_connections 20480;
}

说明:

worker_connections用来设置一个worker process支持的最大并发连接数,这个连接数包括了所有链接,例如:代理服务器的连接,客户端的连接等,实际的并发连接数除了受worker_connections参数控制外,还和最大打开文件数worker_rlimit_nofile有关(见下文),Nginx总并发连接=worker数量*worker_connections。

参考资料:http://nginx.org/en/docs/ngx_core_module.html

2.5、Nginx 文件描述符

调整配置Nginx worker进程的最大打开文件数,这个控制连接数的参数为worker_rlimit_nofile。

worker_rlimit_nofile语法:

synstax: worker_rlimit_nofile number
default: 无
context: main

实践修改Nginx配置:

$ vi /etc/nginx/nginx.conf
# 最大打开文件数,可设置为系统优化后的ulimit-HSn的结果
worker_rlimit_nofile 65535;

说明:

1.此参数的作用是改变worker processes能打开的最大文件数

2.Linux系统文件最大打开数设置:ulimit -n 65535

参考资料:http://nginx.org/en/docs/ngx_core_module.html

2.6、Nginx高效传输

1.设置参数:sendfile on;

sendfile参数用于开启文件的高效传输模式。同时将tcp_nopush和tcp_nodelay两个指令设置为on,可防止网络及磁盘I/O阻塞,提升Nginx工作效率。

syntax:     sendfile on | off;  
default:    sendfile off;     
context:    http,server,location,if in location  

参数作用:激活或禁用sendfile()功能功能。sendfile()是作用于两个文件描述符之间的数据拷贝函数,这个拷贝操作是在内核之中的,被称为“零拷贝”,sendfile()比read和write函数要高效很多,因为,read和write函数要把数据拷贝到应用层再进行操作。相关控制参数还有sendfile_max_chunk。

细节见http://nginx.org/en/docs/http/ngx_http_core_module.html#sendfile

2.设置参数:tcp_nopush on;

提高网络的“传输效率”

syntax:     tcp_nopush on | off;   
default:    tcp_nopush off;     
context:    http,server,location     

参数作用:激活或禁用Linux上的TCP_CORK socket选项,此选项仅仅当开启sendfile时才生效,激活这个tcp_nopush参数,数据包不会马上转发出去,而是等数据包最大时,一次性转发出去,有效解决网络堵塞。

细节见http://nginx.org/en/docs/http/ngx_http_core_module.html。

3.设置参数:tcp_nodelay on;

在keeplive连接下,提高网络的传输“实时性”

用于激活tcp_ondelay功能,提高I/O性能。

syntax:     tcp_nodelay on | off   
default:    tcp_nodelay on;        
context:    http,server,location

参数作用:默认情况下当数据发送时,内核并不会马上发送,可能会等待更多的字节组成一个数据包,这样可以提高I/O性能。但是,在每次只发送很少字节的业务场景中,使用 tcp_nodelay功能能够让数据包立刻转发出去。

参数生效条件:

激活或禁用TCP_NODELAY选项,当一个连接进入keep-alive状态时生效。

细节见http://nginx.org/en/docs/http/ngx_http_core_module.html。

3、Nginx连接参数优化

1.keepalive_timeout

用于设置客户端连接保持会话的超时时间为60秒。超过这个时间,服务器会关闭该连接,此数值为参考值。

syntax: keepalive_timeout  timeout [header_timeout] 
default: keepalive_timeout 75s;  
context: http,serverr,location 

参数作用:keep-alive可以使客户端到服务器端已经建立的连接一直工作不退出,当服务器有持续请求时,keep-alive会使用已经建立的连接提供服务,从而避免服务器重新建立新连接处理请求。

此参数设置一个keep-alive(客户端连接在服务器端保持多久后退出),其单位是秒,和HTTP响应header域的“Keep-Alive:timeout=time”参数有关,这些header信息也会被客户端浏览器识别并处理,不过有些客户端并不能按照服务器端的设置来处理,例如:MSIE大约60秒后会关闭keep-alive连接。

细节见:http://nginx.org/en/docs/http/ngx_http_core_module.html

2.client_header_timeout

用于设置读取客户端请求头数据的超时时间。此处的数值15,其单位是秒,为经验参考值。

syntax:     client_header_timeout time; #<==参数语法
default:    client_header_timeout 60s;  #<==参数默认大小
context:    http,server         #<==可以放置的标签段

参数作用:设置读取客户端请求头数据的超时时间。如果超过这个时间,客户端还没有发送完整的header数据,服务器端将返回“Request time out (408)”错误,可指定一个超时时间,防止客户端利用http协议进行攻击。

细节见:http://nginx.org/en/docs/http/ngx_http_core_module.html。

3.client_body_timeout

用于设置读取客户端请求主体的超时时间,默认值60

syntax:     client_body_timeout time;  
default:    client_body_timeout 60s;  
context:    http,server,location    

参数作用:设置读取客户端请求主体的超时时间。这个超时仅仅为两次成功的读取操作之间的一个超时,非请求整个主体数据的超时时间,如果在这个超时时间内,客户端没有发送任何数据,Nginx将返回“Request time out(408)”错误,默认值60,

细节见:http://nginx.org/en/docs/http/ngx_http_core_module.html

4.send_timeout 25

用于指定响应客户端的超时时间。这个超时仅限于两个连接活动之间的时间,如果超过这个时间,客户端没有任何活动或者Nginx数据没有发送完,Nginx都将会关闭连接,默认值为60秒,可以改为参考值25秒。

syntax:     send_timeout    time; 
default:    send_timeout    60s;    
context:    http,server,location     

参数作用:设置服务器端发送HTTP响应信息到客户端的超时时间,这个超时仅仅为两次成功握手后的一个超时,非请求整个响应数据的超时时间,如在这个超时时间内,客户端没有接收任何数据,连接将被关闭。

细节见http://nginx.org/en/docs/http/ngx_http_module.html。

4、Nginx上传文件大小限制

syntax:     client_max_body_size size;  
default:    client_max_body_size 1m;   
context:   http,server,location 

参数作用:设置最大的允许的客户端请求主体大小,在请求头域有“Content-Length”,如果超过了此配置值,客户端会受到413错误,意思是请求的条目过大,有可能浏览器不能正确显示。设置为0表示禁止检查客户端请求主体大小。此参数对提高服务器端的安全性有一定作用。细节见http://nginx.org/en/docs/http/ngx_http_core_module.html

具体大小根据公司的业务做调整,如果不清楚就先设置为8m把,有关客户端请求主体的解释在HTTP原理一节已经解释过了,一般情况下,HTTP的post方法在提交数据时才会携带请求主体信息。

5、FastCGI调优

FastCGI参数是配合Nginx向后请求PHP动态引擎服务的相关参数。

FastCGI常见参数的Nginx配置示例如下:

$ cat /etc/nginx/nginx.conf
worker_processes  4;
worker_cpu_affinity 0001 0010 0100 1000;
worker_rlimit_nofile 65535;
user nginx;
events {
    use epoll;
    worker_connections  10240;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    tcp_nopush      on;
    keepalive_timeout  65;
    tcp_nodelay     on;
    client_header_timeout 15;
    client_body_timeout   15;
    send_timeout          15;
    server_tokens off;
    fastcgi_connect_timeout 30s;        #Nginx允许fcgi连接超时时间
    fastcgi_send_timeout 30s;           #Nginx允许fcgi返回数据的超时时间
    fastcgi_read_timeout 30s;           #Nginx读取fcgi响应信息的超时时间
    fastcgi_buffer_size 64k;            #Nginx读取响应信息的缓冲区大小
    fastcgi_buffers 4 64k;              #指定Nginx缓冲区的数量和大小
    fastcgi_busy_buffers_size 128k;     #当系统繁忙时buffer的大小
    fastcgi_temp_file_write_size 128k;  #Nginx临时文件的大小
    fastcgi_temp_path   /data/ngx_fcgi_tmp; #指定Nginx临时文件放置路径
    fastcgi_cache_path  /data/ngx_fcgi_cache    levels=2:2  keys_zone=ngx_fcgi_cache:10m   inactive=1d;    #指定Nginx缓存放置路径
    
    server {
        listen       80;
        location ~ .*\.(php|php5)?$ {
                root   html;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        include fastcgi.conf;
        fastcgi_cache ngx_fcgi_cache;     #开启fcgi缓存并起名叫ngx_fcgi_cache,很重要,有效降低CPU负载,并且防止502错误发生。
        fastcgi_cache_valid 200 302 1h; #指定应答代码的缓存时间,1h=1小时
        fastcgi_cache_valid 301 1d;     #1d=1天
        fastcgi_cache_valid any 1m;     #and 1m:将其他应答缓存1分钟
        fastcgi_cache_min_uses 1;       #待缓存内容至少要被用户请求过1次
        fastcgi_cache_use_stale error timeout invalid_header http_500;  #当遇到error,timeout,或者返回码500时,启用过期缓存返回用户(返回过期也比返回错误强)
#       fastcgi_cache_key   http://$host$request_uri;   
        
        }
    }

6、Nginx gzip

Nginx gzip压缩模块提供了压缩文件内容的功能,用户请求的内容在发送到用户客户端之前,Nginx服务器会根据一些具体的策略实施压缩,以节约网站出口带宽,同时加快数据传输效率,来提升用户访问体验。

Nginx gzip压缩的优点

1.提升网站用户体验:发送给用户的内容小了,用户访问单位大小的页面就加快了,用户体验提升了,网站口碑就好了。

2.节约网站带宽成本:数据是压缩传输的,因此节省了网站的带宽流量成本,不过压缩时会稍微消耗一些CPU资源,这个一般可以忽略。

此功能既能提升用户体验,又能使公司少花钱,一举多得。对于几乎所有的Web服务来说,这是一个非常重要的功能,Apache服务也有此功能。

需要和不需要压缩的对象

1.纯文本内容压缩比很高,因此,纯文本的内容最好进行压缩,例如:html,js,css,xml,shtml等格式的文件。

2.被压缩的纯文本文件必须要大于1KB,由于压缩算法的特殊原因,极小的文件压缩后可能反而变大。

3.图片,视频(流媒体)等文件尽量不要压缩,因为这些文件大多都是经过压缩的,如果再压缩很可能不会减少或减少很少,或者有可能增大,同时压缩时还会消耗大量的CPU,内存资源。

参数介绍及配置说明

此压缩功能与早期Apache服务的mod_deflate压缩功能很相似,Nginx的gzip压缩功能依赖于ngx_http_gzip_module模块,默认已安装。

1.gzip压缩配置语法

Syntax:        gzip on | off;
Default:         gzip off;
Context:        http, server, location, if in location
作用:传输压缩

2.gzip压缩比率配置语法

Syntax:        gzip_comp_level level;
Default:        gzip_comp_level 1;
Context:        http, server, location
作用:压缩本身比较耗费服务端性能

3.gzip压缩协议版本

Syntax:        gzip_http_version 1.0 | 1.1;
Default:        gzip_http_version 1.1;
Context:        http, server, location
作用:压缩使用在http那个协议,主流版本1.1

4.扩展压缩模块

Syntax:        gzip_static on | off | always;
Default:        
gzip_static off;
Context:        http, server, location
作用:预读gzip功能

对应的压缩参数说明如下:

#压缩配置
gzip on;
#<==开启gzip压缩功能

gzip_min_length 1k;
#<==设置允许压缩的页面最小字节数,页面字节数从header头的Content-Length中获取。默认值0,表示不管页面多大都进行压缩。建议设置成大于1K,如果小于1K可能会越压越大。

gzip_buffers 4 16K;
#<==压缩缓冲区大小。表示申请4个单位为16K的内存作为压缩结果流缓冲,默认值是申请与原始数据大小相同的内存空间来存储gzip压缩结果。

gzip_http_version 1.1;
#<==压缩版本(默认1.1,前端为squid2.5时使用1.0),用于设置识别HTTP协议版本,默认是1.1,目前大部分浏览器已经支持GZIP解压,使用默认即可。

gzip_comp_level 2;
#<==压缩比率。用来指定gzip压缩比,1压缩比最小,处理速度最快;9压缩比最大,传输速度快,但处理最慢,也比较消耗CPU资源。

gzip_types text/plain text/css application/x-javascript application/xml application/javascript application/javascript  image/jpeg image/jpg image/png;  
#<==用来指定压缩的类型,“text/html”类型总是会被压缩,这个就是HTTP原理部分讲的媒体类型。压缩类型可以在../conf/mime.types文件中查看

gzip_vary on;
#<==vary header支持。该选项可以让前端的缓存服务器缓存经过gzip压缩的页面,例如用Squid缓存经过Nginx压缩的数据。

不同的Nginx版本中,gzip_types的配置可能会有不同,上述配置示例适合Nginx 1.6.3。对应的文件类型,请查看安装目录下的mime.types文件。

完整的配置如下:

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16K;
gzip_http_version 1.1;
gzip_comp_level 9;
gzip_types text/plain text/css application/x-javascript application/xml application/javascript  image/jpeg image/jpg image/png;  
gzip_vary on; 

更多:http://nginx.org/en/docs/http/ngx_http_gzip_module.html

7、Nginx Expires 缓存

简单说,Nginx expires的功能就是为用户访问的网站内容设定一个过期时间,当用户第一次访问这些内容时,会把这些内容存储在用户浏览器本地,这样用户第二次及以后继续访问该网站时,浏览器会检查加载已经缓存在用户浏览器本地的内容,就不会去服务器请求了,当缓存的内容过期了会向源服务器发送请求,检查缓存内容是否被修改。

更深入的理解:

expires的功能就是允许通过Nginx配置文件控制HTTP的“Expires”和“Cache-Control”响应头部内容,告诉客户端浏览器是否缓存和缓存多久以内访问的内容。这个expires模块控制Nginx服务器应答时的expires头内容和Cache-Control头的max-age指令。缓存的有效期可以设置为相对于源文件的最后修改时刻或客户端的访问时刻。

这些HTTP头向客户端表明了额内容的有效性和持久性。如果客户端本地有内容缓存,则内容就可以从缓存而不是从服务器中读取,然后客户端会检查缓存中的副本,看其是否过期或失效,以决定是否重新从服务器获得内容更新。

Nginx expires功能优点

1.expires可以降低网站的带宽,节约成本。

2.加快用户访问网站的速度,提升用户访问体验。

3.服务器访问量降低了,服务器压力就减轻了,服务器成本也会降低,甚至可以节约人力成本。

4.对于几乎所有的Web服务来说,这是非常重要的功能之一,Apache服务也有此功能。

Nginx expires配置详解

前面已经介绍了expires的功能原理,接下来就来配置Nginx expires的功能。这里以location标签为例进行讲解,通过location URI规则将需要缓存的扩展名列出来,然后指定缓存时间。如果针对所有内容设置缓存,也可以不用location。Nginx默认安装了expires功能。

1.根据文件扩展名进行判断,添加expires功能范例

范例1:

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
    expires 3650d;
}
#该范例的意思是当用户访问网站URL结尾的文件扩展名为上述指定类型的图片时,设置缓存3650天,即10年。

范例2:

location ~ .*\.(js|css)$
 {
    expires 30d;
 }
#该范例的意思是当用户访问网站URL结尾的文件扩展名为js,css类型的元素时,设置缓存30天,即1个月。

2.根据URL中的路径(目录)进行判断,添加expires功能范例

范例3:

location ~ ^/(images|javascript|js|css|flash|media|static)/
{
    expires 360d;
}
#该范例的意思是当用过户访问网站URL中包含上述路径(例:images,js,css,这些在服务器端是程序目录)时,把访问的内容设置缓存360天,即1年。

3.配置Nginx expire配置效果检查

$ cat nginx.conf                          
worker_processes  1;
events {
    worker_connections  1024;
}
worker_rlimit_nofile 65535;
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server_tokens off;
    tcp_nopush on;
    tcp_nodelay on;
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16K;
    gzip_http_version 1.1;
    gzip_comp_level 9;
    gzip_types text/plain application/x-javascript text/css application/xml;
    gzip_vary on;

    server {
        listen       80;
        server_name  blog.youngboy.org;
        root html/blog;
        location / {
            index  index.php index.html index.htm;
        }
        location ~.*\.(php|php5)?$ {
             fastcgi_pass 127.0.0.1:9000;
             fastcgi_index index.php;
             include fastcgi.conf;
                }
        location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
             expires 3650d;
                }

        location ~ .*\.(js|css)$ {
              expires 30d;
                 }

          location ~ ^/(images|javascript|js|css|flash|media|static)/ {
               expires 360d;
                }
        }
}

使用curl -I 查看expires参数

[root@web01 /]# curl -I http://www.xiongminghao.top/wp-content/themes/wordstar/assets/css/font-awesome.css
HTTP/1.1 200 OK
Server: nginx
Date: Sat, 05 Jan 2019 16:43:02 GMT
Content-Type: text/css
Content-Length: 37414
Last-Modified: Sat, 29 Dec 2018 07:13:51 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: "5c271eaf-9226"
Expires: Mon, 04 Feb 2019 16:43:02 GMT #缓存过期时间
Cache-Control: max-age=2592000 #缓存的总时间
Accept-Ranges: bytes

注意事项:expires只能在web节点上的nginx的配置文件里生效,如果配置在nginx方向代理的话,expires是不生效的

8、Nginx 日志文件安全

1.不记录特定的访问日志

在实际工作中,对于负载均衡器健康节点检查或某些特定文件(比如图片,JS,CSS)的日志,一般不需要记录下来,因为在统计PV时是按照页面计算的,而且日志写入太频繁会消耗大量磁盘I/O,降低服务的性能。

具体配置方法为:

location ~ .*\.(js|jpg|JPG|jpeg|JPEG|css|bmp|gif|GIF)$ {
    access_log  off;
}

这里用location标签匹配不记录日志的元素扩展名,然后关闭日志

2.访问日志的权限设置

假如日志目录为/app/logs,则授权方法如下:

chown -R root.root /app/logs
chmod -R 600 /app/logs

不需要在日志目录上给Nginx用户读或写许可,但很多网友都没注意这个问题,他们把该权限直接给了Nginx或Apache用户,这就成为了安全隐患。

9、Nginx 访问控制

1.根据扩展名限制程序和文件访问

Web2.0时代,绝大多数网站都是以用户为中心多的,例如:bbs,blog,sns产品,这几个产品都有一个共同特点,就是不但允许用户发布内容到服务器,还允许用户发图片甚至上传附件到服务器上,由于为用户开了上传功能,因此给服务器带来了很大的安全风险。虽然很多程序在上传前会着一定的控制,例如:文件大小,类型等,但是,一不小心就会被黑客钻了控制,上传了木马程序。

下面将利用Nginx配置禁止访问和上传资源目录下的PHP,Shell,Perl,Python程序文件,这样用户即使上传了木马文件也没法执行,从而加强了网站的安全。

范例1:配置Nginx,禁止解析指定目录下的指定程序。

location ~ ^/images/.*\.(php|php5|sh|pl|py)$ {
    deny all;
}

location ~ ^/static/.*\.(php|php5|sh|pl|py)$    {
    deny all;
}
location ~* ^/data/(attachment|avatar)/.*\.(php|php5)$ {
    deny all;
}

对上述目录的限制必须写在Nginx处理PHP服务配置的前面,如下:

location ~ .*\.(php|php5)$
{
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fcgi.conf;
}

范例2:Nginx下配置禁止访问.txt和.doc文件。

实际配置信息如下:

location ~* \.(txt|doc)$
{
    if (-f $request_filename)
    {
        root /data/www/www;
        #rewrite ...可以重定向到某个URL
        break;
    }
}
location ~* \.(txt|doc)$
{
    root /data/www/www;
    deny all;
}

2.禁止访问指定的目录

范例1:配置禁止访问指定的单个或多个目录

#禁止访问单个目录的命令如下:
location ~ ^/static  {
    deny all;
}

#禁止访问多个目录的命令如下:
location ~ ^/(static|js)  {
    deny all;
}

范例2:禁止访问目录并返回指定的HTTP状态码,命令如下:

server  {
    listen 80;
    server_name www.youngboy.com youngboy.com;
    root /data/www/www;
    index index.html index.htm;
    access_log logs/www_access.log commonlog;
    location /admin/ {return 404;}
    location /tmplates/ {return 403;}
}

作用:

禁止访问目录下的指定文件"1",或者禁止访问指定目录下的所有内容。

最佳应用场景:

对于集群的共享存储,一般是存放静态资源文件,所以,可禁止执行指定扩展名的程序,例:.php,.sh,.pl,.py

10、Nginx 限制来源IP访问

使用ngx_http_access_module限制网站来源IP访问

案例环境:phpmyadmin数据库的Web客户端,内部开发人员用的。

范例1:禁止某目录让外界访问,但允许某IP访问该目录,且支持PHP解析,命令如下:

location ~ ^/yunjisuan/ {
    allow 202.111.12.211;
    deny all;
}

location ~ .*\.(php|php5)$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index   index.php;
    include         fastcgi.conf;
}

范例2:限制指定IP或IP段访问,命令如下:

location / {
    deny  192.168.1.1;
    allow 192.168.1.0/24;
    allow 10.1.1.0/16;
    deny  all;
}

参考资料:http://nginx.org/en/docs/http/ngx_http_access_module.html

企业问题案例: Nginx做反向代理的时候可以限制客户端IP吗?

答:可以,具体方法如下:

#方法一:使用if来控制,命令如下:
if ($remote_addr = 10.0.0.7) {
    return 403;
}

if ($remote_addr = 218.247.17.130) {
    set $allow_access_root \'ture\';     
}

#方法二:利用deny和allow只允许IP访问,命令如下:
location / {
    root html/blog;
    index index.php index.html index.htm;
    allow 10.0.0.7;
    deny all;
}

#方法三:只拒绝某些IP访问,命令如下:
location / {
    root html/blog;
    index index.php index.html index.htm;
    deny 10.0.0.7;
    allow all;
}

注意事项:

1.deny一定要加一个IP,否则会直接跳转到403,不再往下执行了,如果403默认页是在同一域名下,会造成死循环访问。

2.对于allow的IP段,从允许访问的段位从小到大排列,如127.0.0.0/24的下面才能是10.10.0.0/16,其中:

1)24表示子网掩码:255.255.255.0

2)16表示子网掩码:255.255.0.0

3)8表示子网掩码:255.0.0.0

3.以deny all:结尾,表示除了上面允许的,其他的都禁止。如:

1)deny 192.168.1.1;

2)allow 127.0.0.0/24;

3)allow 192.168.0.0/16;

4)allow 10.10.0.0/16;

5)deny all;

11、Nginx 禁止非法域名解析

Nginx如何防止用户IP访问网站(恶意域名解析,也相当于是直接IP访问企业网站)

方法一:让使用IP访问网站的用户,或者恶意解析域名的用过户,收到501错误,命令如下:

#直接用新的server标签
server {
    listen 80 default_server;
    server_name _;
    return 501;
}
说明:直接报501错误,从用户体验上不是很好

方法二:通过301跳转到主页,命令如下:

#当输入IP地址访问的时候会自动跳转到域名
server {
    listen 80 default_server;
    server_name _;
    rewrite ^(.*) http://blog.youngboy.org/$1 permanent;
}

方法三:发现某域名恶意解析到公司的服务器IP,在server标签里添加以下代码即可,若有多个server则要多处添加。

 if ($host !~ ^blog.youngboy.org$) {
        rewrite ^(.*) http://blog.youngboy.org/$1 permanent;
                }
#说明:代码含义为如果header信息的host主机名字非blog.youngboy.org,就301跳转到blog.youngboy.org

12、Nginx 防盗链

简单的说,就是某些不法网站未经允许,通过在其自身网站程序里非法调用其他网站的资源,然后在自己的网站上显示这些调用的资源,达到填充自身网站的效果。这一举动不仅浪费了调用资源网站的网络流量,还造成其他网站的带宽及服务压力吃紧,甚至宕机。

Nginx Web服务实现防盗链实战

在默认情况下,只需要进行简单的配置,即可实现防盗链处理。请看下面的实例。

实战模式演示盗链:

1)假定blog.youngboy.com是盗链的网站域名,先写一个代码程序内容,如下:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
<title>
jason
</title>
</head>
<body bgcolor=green>
<img src="http://www.youngboy.org/stu.jpg">
</body>
</html>   

假定我们维护的网站是www.youngboy.org。设定一张存在的图片地址,www. youngboy.org/stu.jpg,此时发现blog.youngboy.com盗链了。通过查看流量,分析日志看referer,就可以看到被盗链的具体情况。

下面是查看Web用户http_referer实战演示

#nginx日志格式为www.access.log
    log_format  main  \'$remote_addr - $remote_user [$time_local] "$request" \'
                      \'$status $body_bytes_sent "$http_referer" \'
                      \'"$http_user_agent" "$http_x_forwarded_for"\';

盗链网站blog.youngboy.com访问我们的站点时记录的日志

[root@web01 logs]# tail -f www.access.log 
10.0.0.1 - - [16/Oct/2018:08:56:43 +0800] "GET /stu.jpg HTTP/1.1" 304 0 "http://blog.youngboy.com/oldboy.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.16 Safari/537.36" "-"

对比下可知,盗链的网站多出了一个http_referer的信息,即"http://blog.youngboy.com/oldboy.html"

,表示用户打开我们网站的资源来自于此网站,也就是该站盗链了我们的网站www.youngboy.org资源

可在www.youngboy.org网站下设置防盗链,Nginx的方法如下

放置在server标签内

location ~*^.+\.(jpg|gif|png)$ {
    valid_referers none blocked *.youngboy.com youngboy.com;      
                if ($invalid_referer) {
        rewrite ^/ http://www.youngboy.com/img/nolink.jpg;
                        }
}
#提示:要根据自己公司的实际业务(是否有外链的合作)进行域名设置。

盗链的配置参数解释

location ~*^.+\.(jpg|gif|png)$ { 
#如果访问的文件匹配了括号中的结尾的扩展名,那么就执行下一步匹配
valid_referers none blocked *.youngboy.com youngboy.com;    
#如果http报文中referer不是youngboy.com,那么就执行一步匹配
  if ($invalid_referer) {
        rewrite ^/ http://www.youngboy.com/img/nolink.jpg;
#如果条件不成立,也就是说在http报文中的referer中的域名不是上述填写的域名,那么就会给客户端返回http://www.youngboy.com/img/nolink.jpg;
    }
}

13、Nginx 优雅错误

范例1:对错误代码403实行本地页面跳转,命令如下:

server {
    listen 80;
    server_name     blog.youngboy.org;
    location / {
        root html/blog;
        index   index.html  index.htm;
    }
    error_page  403 /403.html;    #当出现403错误时,会跳转到403.html页面
}

[root@web01 html]# echo “jason 403 page” >blog/403.html 
[root@web01 blog]# curl blog.youngboy.org/images/oldgirl.sh
jason 403 page
#上面的/403.html是相对于站点根目录html/blog的。

范例2:对错误代码404实行本地页面优雅显示,命令如下

server {
    listen  80;
    server_name blog.youngboy.org;
    location / {
        root    html/blog;
        index   index.html  index.htm;
        error_page  404 /404.html; #当出现404错误时,会跳转到404.html页面
    }
}
#代码中的/404.html是相对于站点根目录html/blog下的404.html文件,该文件可以是.php .jpg .html等等
error_page支持多个错误代码的跳转例如:
      error_page 403 404 /403.html;

范例3: 50x页面放到本地单独目录下,进行优雅显示

error_page  500 502 503 504 /50x.html;
location = /50x.html {
    root    /data/www/html;
}     
#这里指定单独的站点目录存放到50x.html文件中。

范例4: 错误状态码URL重定向,命令如下:

server {
    listen 80;
    server_name blog.youngboy.org;
    location / {
        root    html/www;
        index   index.html  index.htm;
        error_page  404 http://bbs.youngboy.org; #当出现404错误时,会跳转到指定的URL http://bbs.youngboy.org页面显示给用户,这个URL一般是企业另外的可用地址。

        access_log  /usr/local/nginx/logs/bbs_access.log    commonlog;
    }
}
#代码中的/404.html是相对于站点根目录html/www的。

范例5: 将错误状态码重定向到一个location,命令如下:

location / {
    error_page  404 = @fallback;
}
location @fallback {
    proxy_pass  http://backend;
}

阿里门户网站天猫的Nginx优雅显示配置案例如下:

error_page  500 501 502 503 504 http://err.tmall.com/error2.html;
error_page  400 403 404 405 408 410 411 412 413 414 415 http://err.tmall.com/error1.html;

14、Nginx 防爬虫

我们可以根据客户端的user-agents信息,轻松地阻止指定的爬虫爬取我们的网站。下面来看几个案例。

将配置放入server标签

范例1:阻止下载协议代理,命令如下:

##Block download agents##
if ($http_user_agent ~* LWP:Simple|BBBike|wget) {
    return 403;
}
#如果用户匹配了if后面的客户端(例如wget),就返回403.
#这里根据$http_user_agent获取客户端agent,然后判断是否允许或返回指定错误码。

范例2:添加内容防止N多爬虫代理访问网站,命令如下:

#这些爬虫代理使用“|”分隔,具体要处理的爬虫可以根据需求增加或减少,添加的内容如下:
if ($http_user_agent ~* "qihoobot|Baiduspider|Googlebot-Modile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Yahoo! SSlurp  China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot"){
    return 403;
}

范例3:测试禁止不同的浏览器软件访问,命令如下:

if ($http_user_agent ~* "Firefox|MSIE") {
    rewrite ^(.*) http://blog.youngboy.org/$1 permanent;
}
#如果浏览器为Firefox或IE,就会跳转到http://blog.youngboy.org

15、Nginx 限制HTTP请求方法

最常用的HTTP方法为GET,POST,我们可以通过Nginx限制HTTP请求的方法来达到提升服务器安全的目的,例如,让HTTP只能使用GET,HEAD和POST方法的配置如下:

#Only allow these request methods
if ($request_method !~ ^(GET|HEAD|POST)$) {
    return 501;
}

#命令解释:
($request_method !~ ^(GET|HEAD|POST)$)  #<==如果匹配的http请求方法不是get|head|post就拒绝掉

当上传服务器上传数据到存储服务器时,用户上传写入的目录就不得不给Nginx对应的用户相关权限,这样一旦程序有漏洞,木马就有可能被上传到服务器挂载的对应存储服务器的目录里,虽然我们也做了禁止PHP,SH,PL,PY等扩展名的解析限制,但还是会遗漏一些想不到的可执行文件。对于这样情况,该怎么办呢?事实上,还可以通过限制上传服务器的Web服务(可以具体到文件)使用GET方法,防止用户通过上传服务器访问存储内容,让访问存储渠道只能从静态或图片服务器入口进入。

例如,在上传服务器上限制HTTP的GET方法的配置如下:

#Only deny GET request methods ##
if ($request_method ~* ^(GET)$) {
    return 501;
}
#还可以加一层location,更具体地限制文件名

16、Nginx 连接限制

连接频率限制 limit_conn_module

请求频率限制 limit_req_module

http协议的连接与请求

HTTP是建⽴在TCP, 在完成HTTP请求需要先建⽴TCP三次握⼿(称为TCP连接),在连接的基础 上在HTTP请求。

Nginx连接限制配置

Nginx连接限制语法

Syntax: limit_conn_zone key zone=name:size; 
Default: Context: http

Syntax: limit_conn zone number; 
Default: Context: http, server, location

具体配置如下:

http {  #http段配置连接限制, 同⼀时刻只允许⼀个客户端IP连接 
limit_conn_zone $binary_remote_addr zone=conn_zone:10m;

...

server {

...

location / {
#同⼀时刻只允许⼀个客户端IP连接 
limit_conn conn_zone 1; 
}

#压⼒测试 yum install -y httpd-tools ab -n 50 -c 20 http://127.0.0.1/index.html

Nginx 请求限制配置

Nginx请求限制语法 
Syntax: limit_req_zone key zone=name:size rate=rate; Default: Context: http
Syntax: limit_conn zone number [burst=number] [nodelay]; Default: Context: http, server, location 

具体配置如下:

http {  ##http段配置请求限制, rate限制速率,限制⼀秒钟最多⼀个IP请求 
limit_req_zone $binary_remote_addr zone=req_zone:10m rate=1r/s;

...

server {

...

location / { 
#1r/s只接收⼀个请求,其余请求拒绝处理并返回错误码给客户端

limit_req zone=req_zone; 
#请求超过1r/s,剩下的将被延迟处理,请求数超过burst定义的数量, 多余的请求返回503

#limit_req zone=req_zone burst=3 nodelay; }

#压⼒测试 yum install -y httpd-tools ab -n 50 -c 20 http://127.0.0.1/index.html

连接限制没有请求限制有效?

我们前⾯说过, 多个请求可以建⽴在⼀次的TCP连接之上, 那么我们对请求的精度限制,当然

⽐对⼀个连接的限制会更加的有效。

因为同⼀时刻只允许⼀个连接请求进⼊。 但是同⼀时刻多个请求可以通过⼀个连接进⼊。 所以请求限制才是⽐较优的解决⽅案。