【Nginx】- 优化实践

Nginx 的优化

Linux安装Nginx

安装依赖包

//一键安装下面四个依赖gcc/zlib/prec-devel/openssl
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel
  1. 因为Nginx依赖于gcc的编译环境,所以,需要安装编译环境来使Nginx能够编译起来
yum install gcc-c++

img

  1. Nginx的http模块需要使用pcre来解析正则表达式,需要安装pcre
yum install -y pcre pcre-devel

img

  1. 安装依赖的解压包
yum install -y zlib zlib-devel

img

  1. ssl 功能需要 openssl 库,安装 openssl
yum install -y openssl openssl-devel

img

安装Nginx

下载Nginx后,将Nginx压缩包移动到Linux的待安装目录中。我这里是 /usr/local/nginx

  1. 使用命令:tar -zxvf nginx-1.16.1.tar.gz 解压Nginx:
    img

一次执行以下命令:

cd /usr/local/nginx

./configure --prefix=/usr/local/nginx
# ./configure --prefix=/usr/local/nginx --with-http_secure_link_module --add-module=/usr/local/fastdfs-nginx-module/src/   
# 如果提示找不到openssl的话,需要根据错误提示加上-with-http_ssl_module --with-openssl=/usr/local/src/openssl-1.1.1参数来指定openssl源码路径,如提示缺少pcre或其他依赖的话,也需要根据提示操作

make

make install

chown root nginx

chmod u+s nginx

关闭防火墙

若想使用外部主机连接上虚拟机访问Nginx,需要关闭虚拟机的防火墙。

centOS6及以前版本使用命令: systemctl stop iptables.service

centOS7关闭防火墙命令: systemctl stop firewalld.service

启动Nginx

cd /usr/local/nginx/sbin
./nginx 启动
./nginx -s stop 关闭
./nginx -s reload 重启
/usr/local/nginx/sbin/nginx -s reload -c /usr/local/nginx/conf/nginx.conf

设置开机自启

首先修改/etc/rc.d/rc.local文件,添加如下内容:
img

执行以下命令,使/etc/rc.d/rc.local变成可执行文件。

chmod +x /etc/rc.d/rc.local

使用reboot命令重启后,查看nginx是否成功的自启动了
img

日志文件清理

# docker安装的nginx
[root@server mysh]# cat nginx_logs.sh 

#!/bin/bash

#日志存储路径
log_path="/usr/app/docker/nginx/logs"
#取出昨天的时间
log_date=$(date -d yesterday +%Y-%m-%d)

#将访问日志重命名,标志为昨天的日期
mv ${log_path}/access.log ${log_path}/access_${log_date}.log
#将错误日志重命名,标志为昨天的日期
mv ${log_path}/error.log ${log_path}/error_${log_date}.log

#/usr/app/nginx/sbin/nginx -s reopen
#重启nginx服务,生成新的日志文件,docker安装的nginx 直接用-s reopen可能不生效,所以直接重启服务,由于需要重启,所以定时任务一般在凌晨执行
docker restart nginx

#只保留最近7天的日志文件,减少硬盘压力
find ${log_path} -type f -name "*.log" -ctime +7 -exec rm -f {} \;

# 编译安装的nginx
[root@server mysh]# cat nginx_logs.sh 

#!/bin/bash

#日志存储路径
log_path="/usr/local/nginx/logs"
#取出昨天的时间
log_date=$(date -d yesterday +%Y-%m-%d)

#将访问日志重命名,标志为昨天的日期
mv ${log_path}/access.log ${log_path}/access_${log_date}.log
#将错误日志重命名,标志为昨天的日期
mv ${log_path}/error.log ${log_path}/error_${log_date}.log

#和docker安装的nginx只有此步操作不同,nginx只带重新打开日志文件的参数,这样可以做到平滑的操作,不会影响nginx的正常运行
/usr/app/nginx/sbin/nginx -s reopen

#只保留最近7天的日志文件,减少硬盘压力
find ${log_path} -type f -name "*.log" -ctime +7 -exec rm -f {} \;

设置定时任务,每天定时执行任务,重命名日志且删除旧的日志

0 0 * * * /usr/local/nginx/nginx_logs.sh  # 每天00:00定时执行任务

Nginx平滑升级

最近nginx的0.8.55和nginx的0.7.69旧的稳定版本已经发布。我一项比较喜欢使用新版本的软件,于是把原来的nginx-1.0.2平滑升级至nginx-1.0.5稳定版。并记录这一过程,希望对有需要的朋友有点帮助。

  1. 开始之前先查看一下当前使用的版本。
# /usr/local/nginx/sbin/nginx -V
nginx: nginx version: nginx/1.0.5
nginx: built by gcc 4.1.2 20080704 (Red Hat 4.1.2-50)
nginx: TLS SNI support disabled
nginx: configure arguments: --user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_flv_module --with-cc-opt=-O3 --with-cpu-opt=opteron --with-http_gzip_static_module
※ 注意红色区域,这是以前编译的参数。马上编辑新版本需要用到。

2.下载新版本:http://nginx.org/en/download.html
然后:解压 > 便以前的准备 > 编译

# tar zxvf nginx-1.0.5.tar.gz
# cd nginx-1.0.5
# ./configure 
--user=www 
--group=www 
--prefix=/usr/local/nginx 
--with-http_stub_status_module 
--with-http_ssl_module 
--with-http_flv_module 
--with-cc-opt='-O3' 
--with-cpu-opt=opteron 
--with-http_gzip_static_module
# make
  1. 执行完后,这里不用在 make install 了,接下来重名/sbin/nginx为nginx.old
# mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old
  1. 复制编译后objs目录下的nginx文件到nginx的安装目录sbin/下
# cp objs/nginx /usr/local/nginx/sbin/
  1. 测试一下新复制过来文件生效情况:
# /usr/local/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
  1. 让nginx把nginx.pid文件修改成nginx.pid.oldbin,随即启动nginx,实现不间断服务运行
# kill -USR2 `cat /usr/local/nginx/nginx.pid`(发送平滑升级信号将旧的nginx.pid文件添加后缀nginx.pid.oldbin)
# kill  -WINCH(平缓停止worker process) `cat /usr/local/nginx/nginx.pid.oldbin
# kill -QUIT `cat /usr/local/nginx/nginx.pid.oldbin`
  1. 升级完成了,最后在看一下升级后的版本
# /usr/local/nginx/sbin/nginx -v
nginx: nginx version: nginx/1.0.5

nginx 平滑安装echo-nginx-module模块

前置声明

1.nginx-1.21.6已经安装;
2.nginx-1.21.6安装目录为:/usr/local/nginx

[root@VM-8-7-centos rdc]# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.21.6
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_secure_link_module --with-stream --with-stream_ssl_module --with-stream_realip_module --add-module=/opt/fastdfs_pkg/fastdfs-nginx-module-master/src

[root@VM-8-7-centos rdc]# cd /opt
[root@VM-8-7-centos opt]# git clone https://gitee.com/yaowenqiang/echo-nginx-module.git

[root@VM-8-7-centos rdc]# cd /usr/local
[root@VM-8-7-centos local]# wget http://nginx.org/download/nginx-1.21.6.tar.gz
[root@VM-8-7-centos local]# tar -xzvf nginx-1.21.6.tar.gz

[root@VM-8-7-centos rdc]# cd /usr/local
[root@VM-8-7-centos local]# cd nginx-1.21.6
[root@VM-8-7-centos nginx-1.21.6]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_secure_link_module --with-stream --with-stream_ssl_module --with-stream_realip_module --add-module=/opt/fastdfs_pkg/fastdfs-nginx-module-master/src --add-module=/opt/echo-nginx-module

[root@VM-8-7-centos nginx-1.21.6]# make

[root@VM-8-7-centos nginx-1.21.6]# mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old
[root@VM-8-7-centos nginx-1.21.6]# cp -f objs/nginx /usr/local/nginx/sbin/nginx
[root@VM-8-7-centos nginx-1.21.6]# make upgrade 

# 重新启动Nginx

安装lua模块

安装

下载相关软件
v2.1-20210510.tar.gz      #不要下载官网的,要去下载openresty的优化版本 后面会说出现的坑
lua-nginx-module-0.10.20.tar.gz  # 0.10.16 以后都需要3,4两个包
lua-resty-core-0.1.22.tar.gz [必须]
lua-resty-lrucache-0.10.tar.gz [必须]
nginx-1.17.5.tar.gz
ngx_devel_kit-0.3.1.tar.gz

对应下载的网站:https://github.com/openresty
luajit: wget --no-check-certificate https://github.com/openresty/luajit2/archive/refs/tags/v2.1-20210510.tar.gz
lua-nginx-module:wget --no-check-certificate https://github.com/openresty/lua-nginx-module/archive/refs/tags/v0.10.20.tar.gz
lua-resty-core:wget --no-check-certificate https://github.com/openresty/lua-resty-core/archive/refs/tags/v0.1.22.tar.gz
lua-resty-lrucache:wget --no-check-certificate https://github.com/openresty/lua-resty-lrucache/archive/refs/tags/v0.11.tar.gz
nginx-1.17.5:wget --no-check-certificate http://nginx.org/download/nginx-1.17.5.tar.gz
ngx_devel_kit-0.3.1:wget --no-check-certificate https://github.com/vision5/ngx_devel_kit/archive/refs/tags/v0.3.1.tar.gz
curl -L -O 'https://github.com/openresty/echo-nginx-module/archive/v0.58.tar.gz'
tar -xzvf v0.58.tar.gz && rm v0.58.tar.gz
mv echo-nginx-module-0.58 /tmp/echo-nginx-module

LuaJIT解压后直接make & make install即可

编译安装lua

[root@dev-192-168-1-60 conf.d]# nginx -V
Tengine version: Tengine/2.3.2
nginx version: nginx/1.17.3
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/tengine --user=www --group=www --with-http_v2_module --with-http_ssl_module --with-http_gzip_static_module --with-http_realip_module --with-http_flv_module --with-http_mp4_module --with-pcre-jit --with-jemalloc --add-module=/usr/local/echo-nginx-module-0.63 --add-module=/usr/local/lua-nginx-module-0.10.22 --add-module=/usr/local/ngx_devel_kit-0.3.2

make -j2
make install

加载模块

ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/liblua-5.1.so.2

echo "/usr/local/lib" >> /etc/ld.so.conf
ldconfig

启动报错

报错一:

[root@dev-192-168-1-60 tengine-2.3.2]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
/usr/local/nginx/sbin/nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory

以及编译时出现

configuring additional modules
adding module in /usr/local/src/nginx-module-vts-0.1.18
 + ngx_http_vhost_traffic_status_module was configured
adding module in /usr/local/src/echo-nginx-module-0.63
 + ngx_http_echo_module was configured
adding module in /usr/local/src/lua-nginx-module-0.10.22
checking for LuaJIT 2.x ... not found
    ./configure: error: unsupported LuaJIT version; ngx_http_lua_module requires LuaJIT 2.x.

解决办法

export LUAJIT_INC=/usr/local/include/luajit-2.1
export LUAJIT_LIB=/usr/local/lib

报错二:

[root@dev-192-168-1-60 tengine-2.3.2]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
nginx: [alert] detected a LuaJIT version which is not OpenResty's; many optimizations will be disabled and performance will be compromised (see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better, consider using the OpenResty releases from https://openresty.org/en/download.html)
nginx: [alert] failed to load the 'resty.core' module (https://github.com/openresty/lua-resty-core); ensure you are using an OpenResty release from https://openresty.org/en/download.html (reason: module 'resty.core' not found:
	no field package.preload['resty.core']
	no file './resty/core.lua'
	no file '/usr/local/share/luajit-2.0.5/resty/core.lua'
	no file '/usr/local/share/lua/5.1/resty/core.lua'
	no file '/usr/local/share/lua/5.1/resty/core/init.lua'
	no file './resty/core.so'
	no file '/usr/local/lib/lua/5.1/resty/core.so'
	no file '/usr/local/lib/lua/5.1/loadall.so'
	no file './resty.so'
	no file '/usr/local/lib/lua/5.1/resty.so'
	no file '/usr/local/lib/lua/5.1/loadall.so') in /usr/local/nginx/conf/nginx.conf:113

解决办法

[root@dev-192-168-1-60 local]# tar -xf lua-resty-core-0.1.24.tar.gz 
[root@dev-192-168-1-60 local]# tar -xf lua-resty-lrucache-0.13.tar.gz 
[root@dev-192-168-1-60 local]# cd lua-resty-core-0.1.24
[root@dev-192-168-1-60 lua-resty-core-0.1.24]# make install PREFIX=/usr/local/lua_core
install -d /usr/local/lua_core/lib/lua//resty/core/
install -d /usr/local/lua_core/lib/lua//ngx/
install -d /usr/local/lua_core/lib/lua//ngx/ssl
install lib/resty/*.lua /usr/local/lua_core/lib/lua//resty/
install lib/resty/core/*.lua /usr/local/lua_core/lib/lua//resty/core/
install lib/ngx/*.lua /usr/local/lua_core/lib/lua//ngx/
install lib/ngx/ssl/*.lua /usr/local/lua_core/lib/lua//ngx/ssl/
[root@dev-192-168-1-60 lua-resty-core-0.1.24]# cd ../lua-resty-lrucache-0.13
[root@dev-192-168-1-60 lua-resty-lrucache-0.13]# make install PREFIX=/usr/local/lua_core
install -d //usr/local/lua_core/lib/lua//resty/lrucache
install lib/resty/*.lua //usr/local/lua_core/lib/lua//resty/
install lib/resty/lrucache/*.lua //usr/local/lua_core/lib/lua//resty/lrucache/
在http模块里添加
lua_package_path "/usr/local/lua_core/lib/lua/?.lua;;";

报错三:

[root@dev-192-168-1-60 local]# /usr/local/tengine/sbin/nginx -c /usr/local/tengine/conf/nginx.conf
nginx: [alert] detected a LuaJIT version which is not OpenResty's; many optimizations will be disabled and performance will be compromised (see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better, consider using the OpenResty releases from https://openresty.org/en/download.html)
nginx: [alert] [lua] base.lua:41: use of lua-resty-core with LuaJIT 2.0 is not recommended; use LuaJIT 2.1+ instead
nginx: [alert] [lua] lrucache.lua:25: use of lua-resty-lrucache with LuaJIT 2.0 is not recommended; use LuaJIT 2.1+ instead

解决办法

[root@dev-192-168-1-60 local]# wgat https://luajit.org/download/LuaJIT-2.1.0-beta3.tar.gz
[root@dev-192-168-1-60 local]# tar -xf LuaJIT-2.1.0-beta3.tar.gz 
[root@dev-192-168-1-60 local]# cd LuaJIT-2.1.0-beta3
[root@dev-192-168-1-60 LuaJIT-2.1.0-beta3]# ls
COPYRIGHT  doc  dynasm  etc  Makefile  README  src
[root@dev-192-168-1-60 LuaJIT-2.1.0-beta3]# make && make install
[root@dev-192-168-1-60 LuaJIT-2.1.0-beta3]# ln -sf luajit-2.1.0-beta3 /usr/local/bin/luajit

Nginx优化实践

配置文件授权

修改nginx.conf权限 访问控制。修改nginx.conf前文件读写权限为644, 文件权限要求,<= 640 执行修改命令

chmod 640 nginx.conf

修改文件拥有者 如果使用普通用户修改文件

sudo chown appuser nginx

限制主目录与主目录属主属组和权限<=740

chmod -R 740 nginx

登录授权认证

创建管理用户和用户组
描述:建议nginx中间件创建专门的用户和组来管理中间件,在nginx.conf文件中配置user用户

用户 用户组
user [root] [owner];

请求URL授权认证

  1. 配置登录信息
    server {}模块中配置
#【认证提示符 [ 可任意编辑 ] 】
auth_basic "Input Password:";        

#【认证密码文件】          
auth_basic_user_file "NGINX_HOME/passFile";    
  1. 生成密码文件工具htpasswd
# 查看是否安装htpasswd
which htpasswd  							  

#【首先安装生成密码所需的软件】
yum -y install  httpd-tools   			      

# 查看是否安装
rpm -qf /usr/bin/htpasswd  					  
  1. 创建用户密码文件
    描述:存放位置为nginx.conf 相同级别目录,我这里默认设置账户信息为:nginx/Nginx@123

具体命令:htpasswd -c nginx_HOME/[文件名称] [账户名称]

# 命令模式格式:【创建密码文件(文件路径和配置文件中相同)和用户名】
htpasswd -c /NGINX_HOME/[文件名称] [账户名称] 	

# 命令实践
# or
# 生成密码文件
htpasswd -cb /NGINX_HOME/htpasswd ceshi 123789  
# or
# 注:【追加用户,不使用-c选项(否则会覆盖)】
htpasswd  /NGINX_HOME/pass   jerry       
# or
htpasswd -c /NGINX_HOME/nginx_htpass nginx

# 上面几种方式根据项目模块,自定义选择

 #【设置密码】
New password:                                

Re-type new password: 

Adding password for user tom
  1. 设置密码文件权限
    描述:了安全设置文件权限
# 为了安全设置文件权限
chmod 400 /NGINX_HOME/nginx_htpass   
  1. 检查语法 & 重启
#【重新加载配置文件】
/NGINX_HOME/sbin/nginx -s reload         

#【检查配置文件是否正确】
/NGINX_HOME/sbin/nginx -t

访问文件控制

描述:禁止nginx访问 .htxxx文件,在nginx.conf文件中server {}模块中配置

location ~ /.ht { deny all; }

隐藏软件名称和版本号

在nginx.conf文件中http {}模块配置

# 隐藏版本号。
# 不会让nginx执行的速度更快,但它可以关闭在错误页面中的nginx版本数字,
# 这样对于安全性是有好处的
server_tokens off;  

登录源限制

描述:入侵防护,nginx中间件对中间件管理后台操作进行登录源限制。

在nginx.conf文件中server {}模块中的local {}模块中配置

# 允许的IP   
allow 192.168.1.1;  
deny  192.168.1.2;   
deny all;

限制IP同一时间段内的访问次数

描述:入侵防护。nginx中间件限制某些ip在同一时间段内的访问次数

http{     
	limit_req_zone $binary_remote_addr zone=allips:10m rate=20r/s;     
	server{         	
		location {             
			limit_req zone=allips burst=5 nodelay;         
		}     
	} 
} 

http{     
	limit_conn_zone   one  $binary_remote_addr  10m;     
	server{         
		location {         
			# 连接数限制            
			limit_conn one 20;          
		}     
	} 
}

nginx中间件自定义缓存

描述:入侵防护。在nginx.conf文件中http {}模块配置

# 分配给请求数据的Buffer大小。
# 请求的数据小于client_body_buffer_size直接将数据先在内存中存储。
# 如果请求的值大于client_body_buffer_size小于client_max_body_size,就会将数据先存储到临时文件中
# 临时文件在client_body_temp 指定的路径中,默认该路径值是/tmp/.
client_body_buffer_size 1k; 														
# 用于读取客户端请求标头的缓冲区大小。对于大多数请求,一个1K字节的缓冲区就足够了。
# 但是,如果请求中包含长Cookie或来自WAP客户端,则该请求可能不适合1K。
# 如果请求行或请求标头字段不适合此缓冲区,
# 	则将分配由large_client_header_buffers指令配置的较大缓冲区 。
client_header_bfuffer_size 1k; 
# 上传文件大小限制,客户端请求服务器最大允许大小。
# 如果请求的正文数据大于client_max_body_size,
# 	HTTP协议会报错 413 Request Entity Too Large。
# 	就是说如果请求的正文大于client_max_body_size,一定是失败的。
# 如果需要上传大文件,一定要修改该值。
client_max_body_size 1k; 
# 用于读取大型客户端请求标头的最大值number和size缓冲区。		
# 请求行不能超过一个缓冲区的大小,否则会向客户端返回414(请求URI太大)错误。	
# 请求标头字段也不能超过一个缓冲区的大小,否则会将400(错误请求)错误返回给客户端。	
# 缓冲区仅按需分配。默认情况下,缓冲区大小等于8K字节。			
# 如果在请求处理结束后将连接转换为保持活动状态,则会释放这些缓冲区。											
large_client_header_buffers 2 1k; 

敏感字段进行过滤

描述:数据安全。建议nginx.conf中间件对敏感字段进行过滤,在nginx.conf文件下local或 local /*模块中配置

if ( $query_string ~* "union.*select.*" ) {  
	rewrite ^/(.*)$ $host  permanent; 
} 
 
if ( $query_string ~* "concat.*" ) {  
	rewrite ^/(.*)$ $host  permanent; 
}

会话超时&空闲锁定超时

描述:资源控制。建议nginx中间件配置会话超时,在nginx.conf文件中http {}模块或者 server {}模块中 设置,一般放置在http {}模块

# 设置请求体(request body)的读超时时间。
# 仅当在一次readstep中,没有得到请求体,就会设为超时。
# 超时后,nginx返回HTTP状态码408(“Request timed out”)
# 存放模块:http server location
client_body_timeout 10; 				
																										
# 等待client发送一个请求头的超时时间(例如:GET / HTTP/1.1)	
# .仅当在一次read中,没有收到请求头,才会算成超时。	
# 如果在超时时间内,client没发送任何东西,nginx返回HTTP状态码408(“Request timed out”)
# 存放模块:http server								
client_header_timeout 10; 																							
# 第一个参数指定了与client的keep-alive连接超时时间。服务器将会在这个时间后关闭连接。
# 可选的第二个参数指定了在响应头Keep-Alive: timeout=time中的time值。
# 这个头能够让一些浏览器主动关闭连接,这样服务器就不必要去关闭连接了。
# 没有这个参数,nginx不会发送Keep-Alive响应头(尽管并不是由这个头来决定连接是否“keep-alive”)
# (服务器在返回数据给用户时,在头header文件中会添加keepalive字段,75s,
# 浏览器在这个时间后能够主动关闭连接)
# 存放模块:http server location
keepalive_timeout 55; 																		
# 客户端的响应超时时间。默认为60													
send_timeout 60;								

proxy_redirect

nginx反向代理apache的时候出了一点点问题,原来后端apache用的端口是8080通过反向代理后,使用wireshark抓包发现location头域数值为http://192.168.1.154:8080/wuman/ 如果把这个返回给客户端肯定是不可以的,看起来别扭而且还暴露了apache的具体信息

所以在这里用到了nginx的proxy_redirect指定修改被代理服务器返回的响应头中的location头域跟refresh头域数值

以下是截取nginx的一小段配置文档

server {
       listen       80;
       server_name  www.boke.com;
       location / {
            proxy_pass http://192.168.1.154:8080;
            proxy_redirect off;
       }
 }

此时我们通过curl查看结果得出

[root@localhost nginx]# curl -I http://www.boke.com/wuman
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 24 Dec 2015 12:02:00 GMT
Content-Type: text/html; charset=iso-8859-1
Connection: keep-alive
Location: http://192.168.1.154:8080/wuman/

这里location为带有后端服务器实际地址跟端口的响应头信息这样在实际线上是不允许的所以这里我们打算通过proxy_redirect将被代理服务器的响应头中的location字段进行修改后返回给客户端

server {
       listen       80;
       server_name  www.boke.com;
       location / {
            proxy_pass http://192.168.1.154:8080;
            proxy_redirect http://192.168.1.154:8080/wuman/  http://www.boke.com/wuman/;
       }

server {
       listen       80;
       server_name  www.boke.com;
       location / {
            proxy_pass http://192.168.1.154:8080;
            proxy_redirect ~^http://192.168.1.154:8080(.*)   http://www.boke.com$1;
       }

则curl查看返回结果

[root@localhost nginx]# curl -I http://www.boke.com/wuman
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 24 Dec 2015 12:08:34 GMT
Content-Type: text/html; charset=iso-8859-1
Connection: keep-alive
Location: http://www.boke.com/wuman/

此时查看location已经变成了我们想要的结果了。 此时通过replacement 301重定向到了我们新的页面

出处:
proxy_redirect 语法:proxy_redirect [ default|off|redirect replacement ]
默认值:proxy_redirect default
使用字段:http, server, location
如果需要修改从被代理服务器传来的应答头中的"Location"和"Refresh"字段,可以用这个指令设置。
假设被代理服务器返回Location字段为: http://localhost:8000/two/some/uri/
这个指令:
proxy_redirect http://localhost:8000/two/ http://frontend/one/;
将Location字段重写为http://frontend/one/some/uri/

在代替的字段中可以不写服务器名:
proxy_redirect http://localhost:8000/two/ /;

这样就使用服务器的基本名称和端口,即使它来自非80端口。
如果使用“default”参数,将根据location和proxy_pass参数的设置来决定。

例如下列两个配置等效:

location /one/ {  
    proxy_pass       http://upstream:port/two/;  
    proxy_redirect   default;
    } 
location /one/ {  
    proxy_pass       http://upstream:port/two/;  
    proxy_redirect   http://upstream:port/two/   /one/;
}

在指令中可以使用一些变量:

proxy_redirect   http://localhost:8000/    http://$host:$server_port/;

这个指令有时可以重复:

proxy_redirect   default;  
proxy_redirect   http://localhost:8000/    /;  
proxy_redirect   ;  /;

参数off将在这个字段中禁止所有的proxy_redirect指令:

proxy_redirect   off;  
proxy_redirect   default;  
proxy_redirect   http://localhost:8000/    /;  
proxy_redirect   ;  /;

利用这个指令可以为被代理服务器发出的相对重定向增加主机名:

限流模块limit_req_zone、limit_req_conn

根据nginx官网提供的说法,有两种算法,一种是漏桶算法,一种是令牌桶算法.

  • limit_req_zone 用来限制单位时间内的请求数目,以及速度限制。
  • limit_req_conn 用来限制同一时间连接数,即并发限制。

令牌桶算法
img
令牌桶:

  • 令牌以固定速率产生,并缓存到令牌桶中;
  • 令牌桶放满时,多余的令牌被丢弃;
  • 请求要消耗等比例的令牌才能被处理;
  • 令牌不够时,请求被缓存或者被拒绝

漏桶算法
img
漏桶算法

  • 水(请求)从上方倒入水桶,从水桶下方流出(被处理);
  • 来不及流出的水存在水桶中(缓冲),以固定速率流出;
  • 水桶满后水溢出(丢弃)。
  • 这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃。

相比漏桶算法,令牌桶算法不同之处在于它不但有一只“桶”,还有个队列,这个桶是用来存放令牌的,队列才是用来存放请求的。
从两者的作用上赖看,漏桶算法比较直接,限制实时限制数据的传输速率与频率,对于突发流量不管那么多就是规定死的,而令牌桶能够在限制的数据的平均传输速率的同时允许某种程度高并发的突发传输。

limit_req_zone 参数配置

放在http{}

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
  • 第一个参数:$binary_remote_addr 表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址。
  • 第二个参数:zone=mylimit:10m表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息。
  • 第三个参数:rate=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,还可以有比如30r/m的。
    img

放在server{}

limit_req zone=mylimit burst=1 nodelay;
  • 第一个参数:zone=one 设置使用哪个配置区域来做限制,与上面limit_req_zone 里的name对应。
  • 第二个参数:burst=5,重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个大小为5的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内。
  • 第三个参数:nodelay,如果设置,超过访问频次而且缓冲区也满了的时候就会直接返回503,如果没有设置,则所有请求会等待排队。
    img

设置搜索引擎例外

limit_req_zone  $anti_spider  zone=one:10m   rate=10r/s;
limit_req zone=one burst=100 nodelay;

if ($http_user_agent ~* "googlebot|bingbot|Feedfetcher-Google") {
    set $anti_spider $http_user_agent;
}

http_limit_conn_module 参数配置

这个模块用来限制单个IP的请求数。并非所有的连接都被计数。只有在服务器处理了请求并且已经读取了整个请求头时,连接才被计数。

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

limit_conn_zone $binary_remote_addr zone=addr:10m;

# 一次只允许每个IP地址一个连接
server {
    location /download/ {
        limit_conn addr 1;
    }
}
Syntax: limit_conn_zone key zone=name:size;
Default:    —
Context:    http

limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
limit_conn_zone $binary_remote_addr zone=addr:10m;

# 可以配置多个limit_conn指令。例如,以上配置将限制每个客户端IP连接到服务器的数量,同时限制连接到虚拟服务器的总数。
server {
    limit_conn perip 10;
    limit_conn perserver 100;
}

这里,客户端IP地址作为关键。请注意,不是$remote_addr,而是使用$binary_remote_addr变量$remote_addr变量的大小可以从7到15个字节不等。存储的状态在32位平台上占用32或64字节的内存,在64位平台上总是占用64字节。对于IPv4地址,$binary_remote_addr变量的大小始终为4个字节,对于IPv6地址则为16个字节。存储状态在32位平台上始终占用32或64个字节,在64位平台上占用64个字节。一个兆字节的区域可以保持大约32000个32字节的状态或大约16000个64字节的状态。如果区域存储耗尽,服务器会将错误返回给所有其他请求。

Syntax: limit_conn_log_level info | notice | warn | error;
Default:    limit_conn_log_level error;
Context:    http, server, location

# 当服务器限制连接数时,设置所需的日志记录级别。
Syntax: limit_conn_status code;
Default: limit_conn_status 503;
Context:    http, server, location

实战

实例一 限制访问速率

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;

server { 
    location / { 
        limit_req zone=mylimit;
    }
}

上述规则限制了每个IP访问的速度为2r/s,并将该规则作用于根目录。如果单个IP在非常短的时间内并发发送多个请求,结果会怎样呢?
img

实例二 burst缓存处理
短时间内发送了大量请求,Nginx按照毫秒级精度统计,超出限制的请求直接拒绝。这在实际场景中未免过于苛刻,真实网络环境中请求到来不是匀速的,很可能有请求“突发”的情况,也就是“一股子一股子”的。Nginx考虑到了这种情况,可以通过burst关键字开启对突发请求的缓存处理,而不是直接拒绝。
来看我们的配置:

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4;
    }
}

我们加入了burst=4,意思是每个key(此处是每个IP)最多允许4个突发请求的到来。如果单个IP在10ms内发送6个请求,结果会怎样呢?
img

相比实例一成功数增加了4个,这个我们设置的burst数目是一致的。具体处理流程是:1个请求被立即处理,4个请求被放到burst队列里,另外一个请求被拒绝。通过burst参数,我们使得Nginx限流具备了缓存处理突发流量的能力。

但是请注意:burst的作用是让多余的请求可以先放到队列里,慢慢处理。如果不加nodelay参数,队列里的请求不会立即处理,而是按照rate设置的速度,以毫秒级精确的速度慢慢处理

实例三 nodelay降低排队时间
实例二中我们看到,通过设置burst参数,我们可以允许Nginx缓存处理一定程度的突发,多余的请求可以先放到队列里,慢慢处理,这起到了平滑流量的作用。但是如果队列设置的比较大,请求排队的时间就会比较长,用户角度看来就是RT变长了,这对用户很不友好。有什么解决办法呢?nodelay参数允许请求在排队的时候就立即被处理,也就是说只要请求能够进入burst队列,就会立即被后台worker处理,请注意,这意味着burst设置了nodelay时,系统瞬间的QPS可能会超过rate设置的阈值。nodelay参数要跟burst一起使用才有作用。
延续实例二的配置,我们加入nodelay选项:

延续实例二的配置,我们加入nodelay选项:

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4 nodelay;
    }
}

单个IP 10ms内并发发送6个请求,结果如下:
img

跟实例二相比,请求成功率没变化,但是总体耗时变短了。这怎么解释呢?实例二中,有4个请求被放到burst队列当中,工作进程每隔500ms(rate=2r/s)取一个请求进行处理,最后一个请求要排队2s才会被处理;实例三中,请求放入队列跟实例二是一样的,但不同的是,队列中的请求同时具有了被处理的资格,所以实例三中的5个请求可以说是同时开始被处理的,花费时间自然变短了。
但是请注意,虽然设置burst和nodelay能够降低突发请求的处理时间,但是长期来看并不会提高吞吐量的上限,长期吞吐量的上限是由rate决定的,因为nodelay只能保证burst的请求被立即处理,但Nginx会限制队列元素释放的速度,就像是限制了令牌桶中令牌产生的速度。

看到这里你可能会问,加入了nodelay参数之后的限速算法,到底算是哪一个“桶”,是漏桶算法还是令牌桶算法?当然还算是漏桶算法。

考虑一种情况,令牌桶算法的token为耗尽时会怎么做呢?由于它有一个请求队列,所以会把接下来的请求缓存下来,缓存多少受限于队列大小。但此时缓存这些请求还有意义吗?
如果server已经过载,缓存队列越来越长,RT越来越高,即使过了很久请求被处理了,对用户来说也没什么价值了。所以当token不够用时,最明智的做法就是直接拒绝用户的请求,这就成了漏桶算法

示例四 自定义返回值

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4 nodelay;
        limit_req_status 598;
    }
}

img

Nginx头信息漏洞扫描

缺少X-Frame-Options

关于http头部 X-Frame-Options 缺失漏洞解决,X-Frame-Options有三个可选项:

  • DENY #拒绝任何域加载
  • SAMEORIGIN #允许同源域下加载(常用)
  • ALLOW-FROM #可以定义允许frame加载的页面地址

解决方法

# Nginx的nginx.conf中location下配置
add_header X-Frame-Options SAMEORIGIN;

HTTP Strict-Transport-Security 响应头缺失

HTTP Strict-Transport-Security 可以限定浏览器只能通过HTTPS访问当前资源,禁止HTTP方式。

  • max-age= 设置在浏览器收到这个请求后的 秒的时间内凡是访问这个域名下的请求都使用 HTTPS 请求。
  • includeSubDomains 可选,如果这个可选的参数被指定,那么说明此规则也适用于该网站的所有子域名。
  • preload 可选,加入预加载列表

解决方法

# Nginx的nginx.conf中location下配置
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";

HTTP Referrer-Policy 响应头缺失

referer是用来防止CORS(跨站请求伪造)的一种最常见及有效的方式。而Referrer-Policy则是客户端对这个带信息策略的配置。推荐使用strict-origin-when-cross-origin作为默认策略。

Referrer-Policy可以设置的值有如下列表:

  • no-referrer 整个Referer首部会被移除。访问来源信息不随着请求一起发送
  • no-referrer-when-downgrade 没有指定策略时的默认行为。在同等安全级别的情况下,引用页面的地址会被发送(HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP)。
  • origin 在任何情况下,仅发送文件的源作为引用地址。
  • origin-when-cross-origin 对于同源的请求,会发送完整的URL作为引用地址,但是对于非同源请求仅发送文件的源。
  • same-origin 对于同源的请求会发送引用地址,但是对于非同源请求则不发送引用地址信息
  • strict-origin 在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS),但是在降级的情况下不会发送 (HTTPS->HTTP)。
  • strict-origin-when-cross-origin 对于同源的请求,会发送完整的URL作为引用地址;在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。
  • unsafe-url 无论是同源请求还是非同源请求,都发送完整的 URL(移除参数信息之后)作为引用地址。(最不安全)

解决方法

# Nginx的nginx.conf中location下配置
add_header Referrer-Policy strict-origin-when-cross-origin;

HTTP X-Permitted-Cross-Domain-Policies 响应头缺失

X-Permitted-Cross-Domain-Policies 用于指定当不能将crossdomain.xml文件(当需要从别的域名中的某个文件中读取 Flash 内容时用于进行必要设置的策略文件)放置在网站根目录等场合时采取的替代策略。
常用的配置值:master-only 只允许使用主策略文件(/crossdomain.xml)

解决方法

# Nginx的nginx.conf中location下配置
add_header X-Permitted-Cross-Domain-Policies master-only;

HTTP X-Download-Options 响应头缺失

Web 服务器对于 HTTP 请求的响应头中缺少 X-Download-Options会导致浏览器提供的安全特性失效,容易遭受 Web 前端黑客攻击的影响。可以使用X-Download-Options标头下载所请求的数据。 X-Download-Options标头可在Internet Explorer 8及更高版本的浏览器中使用。这个标头就像一个深度防御机制,特别适合于允许用户上传内容的应用程序,避免用户直接打开其上传的包含恶意代码的文件,通过向X-Download-Options HTTP头添加noopen指令来删除用户必须打开文件的选项。

解决方法

# Nginx的nginx.conf中location下配置
add_header X-Download-Options noopen

HTTP X-Content-Type-Options 响应头缺失

互联网上的资源有各种类型,通常浏览器会根据响应头的Content-Type字段来分辨它们的类型。例如:"text/html"代表html文档,"image/png"是PNG图片,"text/css"是CSS样式文档。然而,有些资源的Content-Type是错的或者未定义。这时,某些浏览器会启用MIME-sniffing来猜测该资源的类型,解析内容并执行。这个响应头的值只能是nosniff,可用于IE8+和Chrome。

解决方法

# Nginx的nginx.conf中location下配置
add_header X-Content-Type-Options nosniff

HTTP X-Permitted-Cross-Domain-Policies 响应头缺失

解决方法

# Nginx的nginx.conf中location下配置
add_header X-Permitted-Cross-Domain-Policies value;

HTTP X-Download-Options 响应头缺失

解决方法

# Nginx的nginx.conf中location下配置
add_header X-Download-Options "noopen" always;

HTTP Content-Security-Policy 响应头缺失

CSP 是一个计算机的安全标志,主要用来防止 XSS、点击劫持、SQL 注入等攻击;CSP 通过定义运行加载脚本的位置和内容防止恶意代码的加载。

作用:用于定义页面可以加载哪些资源,减少和上报 XSS 的攻击,防止数据包嗅探攻击

解决方法

# Nginx的nginx.conf中location下配置
add_header Content-Security-Policy "default-src 'self' * 'unsafe-inline' 'unsafe-eval' blob: data: ;";

img

img

HTTP X-XSS-Protection 响应头缺失

解决方法

# Nginx的nginx.conf中location下配置
add_header X-XSS-Protection 1;

Permissions-Policy(Feature-Policy)

Permissions Policy由之前的Feature Policy更名而来,用法也做了相应的变动。Feature Policy 是一个新的 http 响应头属性,允许一个站点开启或者禁止一些浏览器属性和 API,来更好的确保站点的安全性和隐私性。有点类似内容安全策略,但是它控制的是浏览器的特征而不是安全行为。

语法:

Feature-Policy: <feature> <allowlist>

允许开启或者禁止的浏览器属性和API列表

许开启或者禁止的浏览器属性和API列表还没有完全敲定,具体可参考Features list

示例
禁用摄像头和定位 API,则可以在返回的 response 中传递以下定义 feature policy 的 HTTP 的头部信息:

add_header Feature-Policy  "camera 'none'; geolocation 'none'f";

语法变更:
原有 Feature-Policy 示例:

"Feature-Policy": "camera 'none'; microphone 'none'"

Permissions-Policy 语法变更为:

"Permissions-Policy": "camera=(),microphonee=()"

使用方法:

# 反谷歌追踪FLoC
add_header Permissions-Policy  "interest-cohort=()"

set-cookies 属性缺失

set-Cookie 没有设置 secure 、HttpOnly属性

nginx.conf location 根据项目路径配置(实际就是把/替换为/; Secure; HttpOnly

# Nginx的nginx.conf中location下配置
proxy_cookie_path / "/; Secure; HttpOnly";

负载也会产生一条set-cookies信息,upstream route后加上Secure HttpOnly

    upstream portal{
          sticky path=/hh name=hh_route Secure HttpOnly;

CSRF跨站域请求伪造攻击

控制referer来源,例如非192.168.41网段地址返回403错误

nginx.conf server 下配置

    valid_referers none server_names 192.168.41.*;
    if ($invalid_referer) {
        return 403;
    }

server模块超时配置

    #nginx跟后端服务器连接超时时间(代理连接超时)
    proxy_connect_timeout  360000s;

    #后端服务器数据回传时间(代理发送超时)
    proxy_send_timeout  360000s;

    #连接成功后,后端服务器响应时间(代理接收超时)
    proxy_read_timeout  360000s;

    #指定nginx与后端fastcgi server连接超时时间
    fastcgi_connect_timeout 360000s;

    #指定nginx向后端传送请求超时时间(指已完成两次握手后向fastcgi传送请求超时时间)
    fastcgi_send_timeout 360000s;

    #指定nginx向后端传送响应超时时间(指已完成两次握手后向fastcgi传送响应超时时间)
    fastcgi_read_timeout 360000s;

不安全的http请求方式漏洞

例如只允许get post请求,其他返回403

nginx.conf location 添加

    if ($request_method !~* GET|POST) {
        return 403;
    }

版本号信息隐藏

nginx版本号信息隐藏
nginx.conf http 添加

server_tokens off;

应用服务版本号信息隐藏
nginx.conf location 设置

proxy_hide_header aas-version;

请求头控制web访问-只允许手机访问

例如只允许手机访问,则请求头Windows、Macintosh,直接返回404
nginx location 配置

    set $flag 0;
    if ($http_user_agent ~* "Windows" ) {
        set $flag "1";
    }
    if ($http_user_agent ~* "Macintosh" ) {
        set $flag "1";
    }
    if ($flag = "1"){
        return 404;
    }

实现自动匹配手机和pc端

直接通过http_user_agent匹配到手机时指向新的路径

server {
        # 监听所有的ipv4的地址
        listen 4000;
        server_name  192.168.1.59;

        location / {
           root /data/static/nft-frontend-pc/build;
            if ($http_user_agent ~* (mobile|nokia|iphone|ipad|android|samsung|htc|blackberry)) {
            #rewrite  ^(.*)    http://192.168.1.59:5000$1 permanent;
            root /data/static/nft-frontend-h5/build;
            }
           index index.html;
           try_files $uri $uri/ @rewrites;
        }

        location @rewrites {
           rewrite ^(.+)$ /index.html last;
        }

        location  /v1/api {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://192.168.1.59:10560;
        }
 }

或者可以指定一个新的域名进行rewrite

rewrite  ^(.*)    http://192.168.1.59:5000$1 permanent;

但是要把整段放到location外面

if ($http_user_agent ~* (mobile|nokia|iphone|ipad|android|samsung|htc|blackberry)) {
    rewrite  ^(.*)    http://192.168.1.59:5000$1 permanent;
}

使用lua通过request_body按条件开放访问权限

需求:当我们的请求在我们条件内的主机上时只允许balance.query和asset.list请求,其余主机不受限制

部署nginx以及添加lua模块

upstream algoapp {
   server 192.168.1.91:8080;
}
server {
        listen      80;
        server_name  abc.test;
        location / {
                if ($request_method = POST ) {
                    set $upstream '';
                    access_by_lua '
                    ngx.req.read_body()
                    local data = ngx.req.get_body_data()
                    local match = ngx.re.match(ngx.var.request_body, "balance.query|asset.list")
                    local addr = ngx.re.match(ngx.var.remote_addr, "172.16.3.11|172.16.3.20|192.168.1.27|192.168.1.60|172.16.9.2|192.168.1.159")
                    if addr then
                        if match then
                            ngx.var.upstream = "algoapp"
                        else
                            return 403
                        end
                    else
                        ngx.var.upstream = "algoapp"
                    end ' ;
                proxy_pass http://$upstream;
                }
                #proxy_pass http://algoapp;
                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;
        }
}

测试1-条件内的不同主机上执行balance.query请求

在主机192.168.1.159

[root@openvpn data]# ifconfig |grep "192.168.1.159"
        inet 192.168.1.159  netmask 255.255.255.0  broadcast 192.168.1.255
[root@openvpn data]# curl -H "Content-Type: application/json" -X POST -d '{"method":"balance.query","id":1,"jsonrpc":"2.0","params":[634272,"AITD"]}' http://abc.test
{
    "error": null,
    "result": [
        {
            "asset": "AITD",
            "available": "0",
            "freeze": "0"
        }
    ],
    "id": 1
}

在非主机192.168.1.36

[root@build-serice jenkins]# ifconfig |grep "192.168.1.36"
        inet 192.168.1.36  netmask 255.255.255.0  broadcast 192.168.1.255
[root@build-serice jenkins]#  curl -H "Content-Type: application/json" -X POST -d '{"method":"balance.query","id":1,"jsonrpc":"2.0","params":[634272,"AITD"]}' http://abc.test
{
    "error": null,
    "result": [
        {
            "asset": "AITD",
            "available": "0",
            "freeze": "0"
        }
    ],
    "id": 1
}

显示都可以操作,此时符合需求

测试2-非条件请求在条件主机上

在条件主机上:192.168.1.159

[root@openvpn data]#ifconfig |grep "192.168.1.159"
        inet 192.168.1.159  netmask 255.255.255.0  broadcast 192.168.1.255
[root@openvpn data]# curl -H "Content-Type: application/json" -X POST -d '{"jsonrpc":"2.0","method":"asset.summary","params":["BTC","BCH"], "id":1}' http://match-reward.sgpexchange.test
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head><title>500 Internal Server Error</title></head>
<body>
<center><h1>500 Internal Server Error</h1></center>
 Sorry for the inconvenience.<br/>
Please report this message and include the following information to us.<br/>
Thank you very much!</p>
<table>
<tr>
<td>URL:</td>
<td>http://match-reward.sgpexchange.test/</td>
</tr>
<tr>
<td>Server:</td>
<td>dev-192-168-1-60</td>
</tr>
<tr>
<td>Date:</td>
<td>2022/11/25 17:19:18</td>
</tr>
</table>
<hr/>Powered by Tengine<hr><center>tengine</center>
</body>
</html>

非条件主机:192.168.1.36

[root@build-serice jenkins]# curl -H "Content-Type: application/json" -X POST -d '{"jsonrpc":"2.0","method":"asset.summary","params":["BTC","BCH"], "id":1}' http://abc.test
{
    "error": null,
    "result": [
        {
            "name": "BTC",
            "total_balance": "113205.6303438",
            "available_count": 29,
            "available_balance": "113203.4504888",
            "freeze_count": 1,
            "freeze_balance": "2.179855"
        },
        {
            "name": "BCH",
            "total_balance": "0",
            "available_count": 0,
            "available_balance": "0",
            "freeze_count": 0,
            "freeze_balance": "0"
        }
    ],
    "id": 1
}

Nginx Access日志敏感信息脱敏打印

通过url传递一些敏感信息如token等,不希望这些信息被完整明文信息记录在nginx的log中。

在nginx.conf日志中增加如下配置:

img

日志分隔

方式1

编写nginx日志分割脚本

[root@localhost ~]# vim /usr/local/nginx/logs/NginxLogRotate.sh
#!/bin/bash 
LOGS_PATH=/usr/local/nginx/logs 
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d) 
cp ${LOGS_PATH}/access.log ${LOGS_PATH}/access_${YESTERDAY}.log && >${LOGS_PATH}/access.log
cp ${LOGS_PATH}/error.log ${LOGS_PATH}/error_${YESTERDAY}.log && >${LOGS_PATH}/error.log

2、设置定时任务运行脚本

[root@localhost ~]# crontab -e
0 0 * * *  /usr/local/nginx/logs/NginxLogRotate.sh

定时任务每天00:00执行脚本/usr/local/nginx/logs/NginxLogRotate.sh,实现定时自动分割Nginx日志(包括访问日志和错误日志),这样Nginx每天都会生成一个新的日志文件

方式2

[root@localhost ~]# vim fenge.sh ##编写脚本文件

#!/bin/bash
#Filename:fenge.sh
d=$(date -d "-1 day" "+%Y%m%d") ##显示一天前的时间
logs_path="/var/log/nginx" ##分割日志的保存路径
pid_path="/usr/local/nginx/logs/nginx.pid" ##pid的路径
[ -d $logs_path ] || mkdir -p $logs_path ##没有目录则创建目录
mv /usr/local/nginx/logs/access.log ${logs_path}/test.com-access.log-$d
##原有日志文件生成到新路径下
kill -USR1 $(cat $pid_path) ##结束重新生成新的pid文件
find $logs_path -mtime +30 | xargs rm -rf ##删除30天前的日志文件

[root@localhost ~]# chmod +x fenge.sh ##给执行权限
[root@localhost ~]# ./fenge.sh ##执行脚本文件
[root@localhost ~]# cd /var/log/nginx/ ##切换到Nginx的日志目录下
[root@localhost nginx]# ls
test.com-access.log-20191112
[root@localhost nginx]# date -s 2019-11-14 ##修改日期为明天的时间
2019年 11月 14日 星期四 00:00:00 CST
[root@localhost nginx]# cd ~
[root@localhost ~]# ./fenge.sh ##重新执行脚本
[root@localhost ~]# cd /var/log/nginx/
[root@localhost nginx]# ls ##查看日志分割日志文件
test.com-access.log-20191112 test.com-access.log-20191113
[root@localhost nginx]# crontab -e ##周期性计划任务
0 1 * * * /opt/fenge.sh

location规则详解

一、语法规则

  • = 开头表示精确匹配

  • ^~ 开头表示uri以某个常规字符串开头,理解为匹配url路径即可(非正则)

  • ~ 开头表示区分大小写的正则匹配

  • ~* 开头表示不区分大小写的正则匹配

  • !~!~*分别为区分大小写不匹配及不区分大小写不匹配的正则

  • / 通用匹配,任何请求都会匹配到

优先级:

  • 等号类型(=)的优先级最高。一旦匹配成功,则不再查找其他location的匹配项

  • ^~和通用匹配。使用前缀匹配,不支持正则表达式,如果有多个location匹配成功的话,不会终止匹配过程,会匹配表达式最长的那个(下方有例子)

  • 如果上一步得到的最长的location为^~类型,则表示阻断正则表达式,不再匹配正则表达式

  • 如果上一步得到的最长的location不是^~类型,继续匹配正则表达式,只要有一个正则成功,则使用这个正则的location,立即返回结果,并结束解析过程

二、验证

1、精确匹配

在conf.d文件夹下创建配置文件test.com.conf,内容如下:
img

上图中第一个和第二个location匹配条件一样,都是/test.html,但第二个为精准匹配到静态路径,因此第一个不会执行,会执行第二个,www.test.com为本地域名解析,access_log和error_lor可以单独为每个模块定义日志

通过域名和路径访问后的内容如下:
img

注意:路径/usr/share/nginx/test_html文件夹下需要有test.html才可以正常访问

上图中指定静态资源路径用的关键字root,还可以用alias,那么root和alias的区别是什么?

  • 用root属性指定的值是要加入到最终路径中的,匹配条件会拼接到路径中

  • 用alias属性指定的值不需要加入到最终路径中

如上图中所示,请求的条件为test.html,通过root指定的路径为/usr/share/nginx/test_html,因此在匹配的时候,这个路径下就必须要有test.html这个文件才可以,否则就会找不到而报错,如果用alias,那么通过浏览器进行请求的时候,alias也是指定到/usr/share/nginx/test_htm路径下,但是会匹配默认的index.html,而无须强制匹配test.html,但是此时就不能使用”=”来进行精确匹配,现在将root改为alias,如图:
img
注意:alias指定的路径结尾要加”/”

下面的配置文件采用roo指定路径,当通过域名请求http://www.test.com/html/的时候,将跳转到/usr/share/nginx/html/下的index.html页面,如图:
img
img

下面的配置文件采用alias指定路径,请求http://www.test.com/linshi/的时候,将跳转到/usr/share/nginx/test_html/index.html页面,如图:
img
img

2、通过^~方式实现匹配

例如:下面配置文件有两条规则,分别匹配url以字母a开头,但是长度不同,首先将长的规则先注释掉,如图:
img

通过curl请求查看状态码,如图:
img

说明:当前只有一个规则开启,因此当匹配url以/a/开头的任何url时,都会返回状态码666

现在将第二条规则注释取消,打开规则,如图:
img
再次发起同样的请求,观察返回状态码,如图:
img

从上图可以看出,两条规则同时被匹配成功,但是第二条规则比较长,因此第二条规则优先被匹配

如果^~匹配成功了,那么表示阻断正则表达式,不再进行正则匹配

3、通过”~”方式实现匹配

上图中的匹配规则都是通过”^~”方式来实现的,那么在匹配最长规则的时候又分为两种情况:

  • 第一种:最长规则通过~来实现匹配(上图中的最长规则就是通过~实现)

  • 第二种:最长的规则不是通过^~实现匹配,而是通过普通匹配来实现

当最长规则是通过普通匹配的时候,将会继续正则匹配, 只要有一个正则成功,则使用这个正则的location,停止继续匹配,返回结果

现在将第二条规则改为普通匹配,并添加一条正则匹配,如图:
img

再次执行同样的请求命令,得到的状态码如下:
img

说明:从上图可以看出请求/a/b/的时候,首先会到达第二条规则(最长规则),由于第二条规则为普通匹配(不是^~匹配),因此会继续去匹配正则,也就是第三条规则,因此最终返回状态码为888

4、通过"~*"方式实现匹配:

” ~* ” 表示不区分大小写的正则匹配

例如:通过url请求/a/b/或者/A/B/,查看返回状态码,如图:
img

请求结果如下:
img

从上图可以看出,无论匹配的是大写还是小写,都会返回对应状态码888

常用方法还有匹配图片后缀,并返回指定信息,如图:
img
img

5、”!~*” 和”!~” 不常用,再次不做介绍

6、通过”/” 实现通用匹配

本例子中,9091服务为httpd

第一种情况:proxy_pass最后面没有斜杠,匹配路径有斜杠(/bbb/),如图:
img

说明:proxy_pass最后面没有斜杠”/”,此时通过浏览器请求http://10.9.2.248/bbb/,那么实际访问的地址就是 http://10.9.2.248:9091/bbb/,会将匹配路径/bbb一起加过去

此时如果在http的目录页面目录htdocs中创建目录bbb,在bbb目录下创建文件index.html,然后即可实现正常访问,如图:
img

第二种情况: proxy_pass最后面有斜杠 “/”,匹配路径也有斜杠(/bbb/),如图:
img

说明:proxy_pass最后面有斜杠”/”,此时通过浏览器请求http://10.9.2.248/bbb/,那么实际访问的地址就是 http://10.9.2.248:9091,会将/bbb抛弃的,如图:
img

第三种情况:proxy_pass后面还有其他路径但是最后没有 “/”, 匹配路径也有斜杠(/bbb/) ,如图:
img

说明,此时通过浏览器访问http://10.9.2.248/bbb/index.html,实际请求的是http://10.9.2.248/cxxindex.html(注意位置是默认路径下,不是ccc路径下,如果proxy_pass的路径为/ccc/ddd,那么实际请求的就是ccc路径下的cccindex.html)

在httpd的默认路径下(htdocs)创建文件cxxindex.html,然后访问,如图:
img

第四种情况: proxy_pass后面还有其他路径但是最后有 “/”, 匹配路径也有斜杠(/bbb/) ,如图:
img

说明:此时通过浏览器访问:http://10.9.2.248/bbb/index.html,实际访问的是http://10.9.2.248/ccc/index.html

在httpd默认路径下(htdocs)创建一个名称为ccc的文件夹,在内部定义一个index.html,然后访问,如图:
img

第五种情况:location匹配路径末尾没有 “/”,proxy_pass后面也没有”/”,如图:
img

说明:8081端口为httpd的服务器端口,如果匹配路径和proxy_pass后都没有”/”,那么此时访问http://10.9.2.248/bbb,默认将请求到http://127.0.0.1:8081/bbb/index.html的内容,此时在httpd默认路径htdocs下创建文件夹bbb,内部创建index.html,定义内容为:i am match bbb,通过IP请求如图:
img

windows下将nginx做成服务

windows下安装Nginx后,只能通过双击进行来启动,通过任务管理器来停止进程,如果要通过命令行启动Nginx,可将其做成服务。

准备工具:NSSM

下载地址:https://nssm.cc/

NSSM是一个服务封装程序,它可以将普通exe程序封装成服务,使之像windows服务一样运行。同类型的工具还有微软自己的srvany,不过nssm更加简单易用,并且功能强大。它的特点如下:

  • 支持普通exe程序(控制台程序或者带界面的Windows程序都可以)

  • 安装简单,修改方便

  • 可以重定向输出(并且支持Rotation)

  • 可以自动守护封装了的服务,程序挂掉了后可以自动重启

  • 可以自定义环境变量

部署:

1、首先将Nginx上传到Windows机器上解压,并将nssm程序放在相同位置,如图:
img

2、进入当前路径下的命令窗口,执行命令如下:

nssm  install nginx     #最后的nginx表示要做成的服务名

img

3、在弹出的界面中,点击Path后面的按钮选择Nginx可执行文件,其余内容会自动带出,如图:
img

4、点击下方的Install service,可以看到安装成功,如图:
img

5、执行如下命令启动Nginx,如图:

net start nginx            #启动Nginx

img

启动脚本

##创建软连接让系统识别nginx启动脚本
[root@localhost nginx]# ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/
[root@localhost nginx]# cd /etc/init.d/ ##切换到启动配置文件目录
[root@localhost init.d]# ls
functions netconsole network README
[root@localhost init.d]# vim nginx ##编辑启动脚本文件

#!/bin/bash
# chkconfig: - 99 20 ##注释信息
# description: Nginx Service Control Script
PROG="/usr/local/nginx/sbin/nginx" ##设置变量为nginx命令文件
PIDF="/usr/local/nginx/logs/nginx.pid" ##设置变量PID文件 进程号为5346
case "$1" in
start)
$PROG ##开启服务
;;
stop)
kill -s QUIT $(cat $PIDF) ##关闭服务
;;
restart) ##重启服务
$0 stop
$0 start
;;
reload) ##重载服务
kill -s HUP $(cat $PIDF)
;;
*) ##错误输入提示
echo "Usage: $0 {start|stop|restart|reload}"
exit 1
esac
exit 0
[root@localhost init.d]# chmod +x /etc/init.d/nginx ##给启动脚本执行权限
[root@localhost init.d]# chkconfig --add nginx ##添加到service管理器中
[root@localhost init.d]# service nginx stop ##就可以使用service控制nginx
[root@localhost init.d]# 服务 nginx 启动

配置模板

nginx.conf

user appuser;
worker_processes  4;

error_log  logs/error.log  notice;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}


http {
	include       mime.types;
	default_type  application/octet-stream;
	
	log_format  main  '$remote_addr - $remote_user [$time_iso8601] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"'
                      '$upstream_addr $upstream_response_time $request_time ';
    access_log  logs/access.log  main;

    sendfile        on;
	
	# 会话的超时时间
	keepalive_timeout  60s;
	# 服务器向客户端发送响应的超时时间
	send_timeout 60s;
	# 客户端请求头读取超时时间 
	client_header_timeout  60s;
	# 客户端请求主体读取超时时间
	client_body_timeout 60s;
    # 上传文件大小限制
	client_max_body_size 101m;
	# 连接的超时时间
	proxy_connect_timeout 60s;
	# 发送数据的超时时间
	proxy_send_timeout 60s;
	# 读取数据的超时时间
	proxy_read_timeout 120s;

	#gzip  on;
	proxy_intercept_errors on;
	# 不允许列出整个目录
	autoindex off;
	# 不显示版本号
	server_tokens off;
	# 访问频次
	limit_req_zone $binary_remote_addr zone=allips:10m rate=20r/s;
	# IP请求数
    limit_conn_zone $binary_remote_addr zone=addr:10m;
	
	include /usr/local/nginx/conf/fams.conf
}

facmss.conf

server {
        listen  28145;
        charset utf-8;
		error_page 500 502 503 504 /50X_nginx.html
		error_page 400 404 /40X_nginx.html
		# 重定向使用相对路径
		absolute_redirect off;
		# 空闲时间
		keepalive_timeout 65s;
		  
		# access日志文件按天分割
        if ($time_iso8601 ~ '(\d{4}-\d{2}-\d{2})') {
           set $time $1;
        }
        access_log logs/$time.access.log main;
		
        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;

		# 文件服务器group1转发
        location /fams/group1/{
            proxy_pass http://x.x.x.x:8080/group1/;
        }
		
		# 工行正向代理
		location ^~ /gyjapi1 {
			#proxy_pass http://gyjapi1.dccnet.com.cn/api/;
			proxy_pass http://x.x.x/api/;
		}
		
		# 前端页面
		location ^~ /fams/ {
            proxy_set_header X-Nginx-Proxy true;
            proxy_set_header Connection "";
            alias /usr/local/nginx/html/fams/fams-client/;
            index index.html index.htm;
            try_files $uri $uri/ /index.html;

            add_header X-Content-Type-Options "nosniff";
            add_header X-XSS-Protection: "1;mode=block";
            add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data: ;";
            add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
            add_header Referrer-Policy "same-origin";
            add_header X-Permitted-Cross-Domain-Policies "master-only";
            add_header X-Download-Options "noopen";
            add_header X-Frame-Options "SAMEORIGIN";
        }
		
		# 代理服务
        location /fams/fams-client-api/ {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://x.x.x.x:7001/fams/fams-client-api/;
			
			# 请求主体的缓冲区大小 
			client_body_buffer_size 8k;
			# 最大请求主体 
			client_max_body_size 4m;
			# 请求头缓冲区 
			client_header_buffer_size 1m;
			# 大型客户端请求头缓冲区 
			large_client_header_buffers 4 8k;
			
			# 关闭响应内容缓冲区
			proxy_buffering off;
			# 设置代理服务器(nginx)保存用户头信息的缓冲区大小
			proxy_buffer_size 4k;
			# proxy_buffers缓冲区,网页平均在32k以下的设置
			proxy_buffers 8 1M; 
			# 高负荷下缓冲大小(proxy_buffers*2)
			proxy_busy_buffers_size  2M;
			# 设定缓存文件夹大小,大于这个值,将从upstream服务器传
			proxy_temp_file_write_size 2M;
			# 关闭硬盘缓冲
            proxy_max_temp_file_size 0;
            
			# 访问频次
            limit_req zone=allips burst=200 nodelay;
			# IP请求数
			limit_conn addr 20;
			
			# 参数过滤
			if ( $query_string ~* "union.*select.*" ) {
				rewrite ^/(.*)$ $host  permanent;
			}
			if ( $query_string ~* "concat.*" ) {
				rewrite ^/(.*)$ $host  permanent;
			}
        }

       location ~ /\.ht {
          deny all;
       }
	   
	   location /50X_nginx.html {
          root /usr/local/nginx/html
       }
	   location /40X_nginx.html {
          root /usr/local/nginx/html
       }
    }
posted @   明小子@  阅读(111)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示