Nginx的常用27种配置和技巧
1. 一个站点配置多个域名
server {
listen 80;
server_name ops-coffee.cn b.ops-coffee.cn;
}
server_name
后跟多个域名即可,多个域名之间用空格分隔.
2. 一个服务配置多个站点
server {
listen 80;
server_name a.ops-coffee.cn;
location / {
root /home/project/pa;
index index.html;
}
}
server {
listen 80;
server_name ops-coffee.cn b.ops-coffee.cn;
location / {
root /home/project/pb;
index index.html;
}
}
server {
listen 80;
server_name c.ops-coffee.cn;
location / {
root /home/project/pc;
index index.html;
}
}
基于 Nginx 虚拟主机配置实现,Nginx 有三种类型的虚拟主机
**基于 IP 的虚拟主机:**需要你的服务器上有多个地址,每个站点对应不同的地址,这种方式使用的比较少
**基于端口的虚拟主机:**每个站点对应不同的端口,访问的时候使用 ip:port 的方式访问,可以修改 listen 的端口来使用
**基于域名的虚拟主机:**使用最广的方式,上边例子中就是用了基于域名的虚拟主机,前提条件是你有多个域名分别对应每个站点,server_name 填写不同的域名即可
3. nginx 添加账号密码验证
server {
location / {
auth_basic "please input user&passwd";
auth_basic_user_file key/auth.key;
}
}
有很多服务通过 nginx 访问,但本身没有提供账号认证的功能,就可以通过 nginx 提供的 authbase 账号密码认证来实现,可以用以下脚本来生成账号的密码
# cat pwd.pl
#!/usr/bin/perl
use strict;
my $pw=$ARGV[0] ;
print crypt($pw,$pw)."\n";
使用方法:
# perl pwd.pl ops-coffee.cn
opf8BImqCAXww
# echo "admin:opf8BImqCAXww" > key/auth.key
4. nginx 开启列目录
当你想让 nginx 作为文件下载服务器存在时,需要开启 nginx 列目录
server {
location download {
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}
autoindex_exact_size:为 on(默认)时显示文件的确切大小,单位是 byte;改为 off 显示文件大概大小,单位 KB 或 MB 或 GB
**autoindex_localtime:**为 off(默认)时显示的文件时间为 GMT 时间;改为 on 后,显示的文件时间为服务器时间
默认当访问列出的 txt 等文件时会在浏览器上显示文件的内容,如果你想让浏览器直接下载,加上下边的配置。
if ($request_filename ~* ^.*?\.(txt|pdf|jpg|png)$) {
add_header Content-Disposition 'attachment';
}
5. 配置默认站点
server {
listen 80 default;
}
当一个 nginx 服务上创建了多个虚拟主机时默认会从上到下查找,如果匹配不到虚拟主机则会返回第一个虚拟主机的内容,如果你想指定一个默认站点时,
可以将这个站点的虚拟主机放在配置文件中第一个虚拟主机的位置,或者在这个站点的虚拟主机上配置 listen default
6. 不允许通过 IP 访问
server {
listen 80 default;
server_name _;
return 404;
}
可能有一些未备案的域名或者你不希望的域名将服务器地址指向了你的服务器,这时候就会对你的站点造成一定的影响,需要禁止 IP 或未配置的域名访问,
我们利用上边所说的 default 规则,将默认流量都转到 404 去
上边这个方法比较粗暴,当然你也可以配置下所有未配置的地址访问时直接 301 重定向到你的网站去,也能为你的网站带来一定的流量
server {
rewrite ^/(.*)$ https://ops-coffee.cn/$1 permanent;
}
7. 直接返回验证文件
location = /XDFyle6tNA.txt {
default_type text/plain;
return 200 'd6296a84657eb275c05c31b10924f6ea';
}
很多时候微信等程序都需要我们放一个 txt 的文件到项目里以验证项目归属,我们可以直接通过上边这种方式修改 nginx 即可,无需真正的把文件给放到服务器上
8. nginx 配置 upstream 反向代理
http {
...
upstream tomcats {
server 192.168.106.176 weight=1;
server 192.168.106.177 weight=1;
}
server {
location /ops-coffee/ {
proxy_pass http://tomcats;
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_set_header X-Forwarded-Proto $scheme;
}
}
}
稍不注意可能会落入一个proxy_pass
加杠不加杠的陷阱,这里详细说下proxy_pass http://tomcats
与proxy_pass http://tomcats/
的区别:
虽然只是一个/的区别但结果确千差万别。分为以下两种情况:
- 目标地址中不带 uri(
proxy_pass http://tomcats
)。此时新的目标 url 中,匹配的 uri 部分不做修改,原来是什么就是什么。
location /ops-coffee {
proxy_pass http://192.168.106.135:8181;
}
http://domain/ops-coffee/ --> http://192.168.106.135:8181/ops-coffee/
http://domain/ops-coffee/action/abc --> http://192.168.106.135:8181/ops-coffee/action/abc
- 目标地址中带 uri(
proxy_pass http://tomcats/
,/也是 uri),此时新的目标 url 中,匹配的 uri 部分将会被修改为该参数中的 uri。
location /ops-coffee/ {
proxy_pass http://192.168.106.135:8181/;
}
http://domain/ops-coffee/ --> http://192.168.106.135:8181
http://domain/ops-coffee/action/abc --> http://192.168.106.135:8181/action/abc
9. nginx upstream 开启 keepalive
upstream tomcat {
server ops-coffee.cn:8080;
keepalive 1024;
}
server {
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://tomcat;
}
}
nginx 在项目中大多数情况下会作为反向代理使用,例如 nginx 后接 tomcat,nginx 后接 php 等,这时我们开启 nginx 和后端服务之间的 keepalive 能够减少频繁创建 TCP 连接造成的资源消耗,配置如上
keepalive
:指定每个 nginxworker 可以保持的最大连接数量为 1024,默认不设置,即 nginx 作为 client 时 keepalive 未生效
proxy_http_version 1.1
:开启 keepalive 要求 HTTP 协议版本为 HTTP 1.1
proxy_set_header Connection ""
:为了兼容老的协议以及防止 http 头中有Connection close
导致的 keepalive 失效,这里需要及时清掉 HTTP 头部的 Connection
10. 404 自动跳转到首页
server {
location / {
error_page 404 = @ops-coffee;
}
location @ops-coffee {
rewrite .* / permanent;
}
}
网站出现 404 页面不是特别友好,我们可以通过上边的配置在出现 404 之后给自动跳转到首页去
11. 隐藏版本号
http {
server_tokens off;
}
经常会有针对某个版本的 nginx 安全漏洞出现,隐藏 nginx 版本号就成了主要的安全优化手段之一,当然最重要的是及时升级修复漏洞
12. 开启 HTTPS
server {
listen 443;
server_name ops-coffee.cn;
ssl on;
ssl_certificate /etc/nginx/server.crt;
ssl_certificate_key /etc/nginx/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
ssl on:开启 https
ssl_certificate:配置 nginx ssl 证书的路径
ssl_certificate_key:配置 nginx ssl 证书 key 的路径
ssl_protocols:指定客户端建立连接时使用的 ssl 协议版本,如果不需要兼容 TSLv1,直接去掉即可
ssl_ciphers:指定客户端连接时所使用的加密算法,你可以再这里配置更高安全的算法
13. 添加黑白名单
白名单配置
location /admin/ {
allow 192.168.1.0/24;
deny all;
}
上边表示只允许 192.168.1.0/24 网段的主机访问,拒绝其他所有
也可以写成黑名单的方式禁止某些地址访问,允许其他所有,例如
location /ops-coffee/ {
deny 192.168.1.0/24;
allow all;
}
更多的时候客户端请求会经过层层代理,我们需要通过$http_x_forwarded_for
来进行限制,可以这样写
set $allow false;
if ($http_x_forwarded_for = "211.144.204.2") { set $allow true; }
if ($http_x_forwarded_for ~ "108.2.66.[89]") { set $allow true; }
if ($allow = false) { return 404; }
14. 添加账号认证
server {
location / {
auth_basic "please input user&passwd";
auth_basic_user_file key/auth.key;
}
}
15. 限制请求方法
if ($request_method !~ ^(GET|POST)$ ) {
return 405;
}
$request_method
能够获取到请求 nginx 的 method
配置只允许 GET\POST 方法访问,其他的 method 返回 405
16. 拒绝 User-Agent
if ($http_user_agent ~* LWP::Simple|BBBike|wget|curl) {
return 444;
}
可能有一些不法者会利用 wget/curl 等工具扫描我们的网站,我们可以通过禁止相应的 user-agent 来简单的防范
Nginx 的 444 状态比较特殊,如果返回 444 那么客户端将不会收到服务端返回的信息,就像是网站无法连接一样
17. 图片防盗链
location /images/ {
valid_referers none blocked www.ops-coffee.cn ops-coffee.cn;
if ($invalid_referer) {
return 403;
}
}
valid_referers :验证 referer,其中none
允许referer
为空,blocked
允许不带协议的请求,
除了以上两类外仅允许referer
为www.ops-coffee.cn
或ops-coffee.cn
时访问images
下的图片资源,否则返回 403
当然你也可以给不符合 referer 规则的请求重定向到一个默认的图片,比如下边这样
location /images/ {
valid_referers blocked www.ops-coffee.cn ops-coffee.cn
if ($invalid_referer) {
rewrite ^/images/.*\.(gif|jpg|jpeg|png)$ /static/qrcode.jpg last;
}
}
18. 控制并发连接数
可以通过ngx_http_limit_conn_module
模块限制一个 IP 的并发连接数
http {
limit_conn_zone $binary_remote_addr zone=ops:10m;
server {
listen 80;
server_name ops-coffee.cn;
root /home/project/webapp;
index index.html;
location / {
limit_conn ops 10;
}
access_log /tmp/nginx_access.log main;
}
}
limit_conn_zone
:设定保存各个键(例如$binary_remote_addr
)状态的共享内存空间的参数,zone=空间名字:大小,
大小的计算与变量有关,例如$binary_remote_addr
变量的大小对于记录 IPV4 地址是固定的 4 bytes,而记录 IPV6 地址时固定的 16 bytes,
存储状态在 32 位平台中占用 32 或者 64 bytes,在 64 位平台中占用 64 bytes。1m 的共享内存空间可以保存大约 3.2 万个 32 位的状态,1.6 万个 64 位的状态
limit_conn
:指定一块已经设定的共享内存空间(例如 name 为 ops 的空间),以及每个给定键值的最大连接数
上边的例子表示同一 IP 同一时间只允许 10 个连接
当有多个limit_conn
指令被配置时,所有的连接数限制都会生效
http {
limit_conn_zone $binary_remote_addr zone=ops:10m;
limit_conn_zone $server_name zone=coffee:10m;
server {
listen 80;
server_name ops-coffee.cn;
root /home/project/webapp;
index index.html;
location / {
limit_conn ops 10;
limit_conn coffee 2000;
}
}
}
上边的配置不仅会限制单一 IP 来源的连接数为 10,同时也会限制单一虚拟服务器的总连接数为 2000
19. 缓冲区溢出攻击
缓冲区溢出攻击是通过将数据写入缓冲区并超出缓冲区边界和重写内存片段来实现的,限制缓冲区大小可有效防止
client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 1k;
**client_body_buffer_size:**默认 8k 或 16k,表示客户端请求 body 占用缓冲区大小。如果连接请求超过缓存区指定的值,
那么这些请求实体的整体或部分将尝试写入一个临时文件。
client_header_buffer_size:表示客户端请求头部的缓冲区大小。绝大多数情况下一个请求头不会大于 1k,
不过如果有来自于 wap 客户端的较大的 cookie 它可能会大于 1k,Nginx 将分配给它一个更大的缓冲区,这个值可以在 large_client_header_buffers 里面设置
**client_max_body_size:**表示客户端请求的最大可接受 body 大小,它出现在请求头部的 Content-Length 字段, 如果请求大于指定的值,
客户端将收到一个"Request Entity Too Large" (413)错误,通常在上传文件到服务器时会受到限制
large_client_header_buffers表示一些比较大的请求头使用的缓冲区数量和大小,默认一个缓冲区大小为操作系统中分页文件大小,通常是 4k 或 8k,
请求字段不能大于一个缓冲区大小,如果客户端发送一个比较大的头,nginx 将返回"Request URI too large" (414),请求的头部最长字段不能大于一个缓冲区,否则服务器将返回"Bad request" (400)
同时需要修改几个超时时间的配置
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 5 5;
send_timeout 10;
client_body_timeout:
表示读取请求 body 的超时时间,如果连接超过这个时间而客户端没有任何响应,Nginx 将返回"Request time out" (408)错误
client_header_timeout:
表示读取客户端请求头的超时时间,如果连接超过这个时间而客户端没有任何响应,Nginx 将返回"Request time out" (408)错误
keepalive_timeout:
参数的第一个值表示客户端与服务器长连接的超时时间,超过这个时间,服务器将关闭连接,可选的第二个参数参数表示
Response 头中 Keep-Alive: timeout=time 的 time 值,这个值可以使一些浏览器知道什么时候关闭连接,以便服务器不用重复关闭,如果不指定这个参数,nginx 不会在应 Response 头中发送 Keep-Alive 信息
send_timeout:
表示发送给客户端应答后的超时时间,Timeout 是指没有进入完整 established 状态,只完成了两次握手,如果超过这个时间客户端没有任何响应,nginx 将关闭连接
20. Header 头设置
通过以下设置可有效防止 XSS 攻击
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
**X-Frame-Options:**响应头表示是否允许浏览器加载frame
等属性,有三个配置DENY
禁止任何网页被嵌入,SAMEORIGIN
只允许本网站的嵌套,ALLOW-FROM
允许指定地址的嵌套
**X-XSS-Protection:**表示启用XSS
过滤(禁用过滤为X-XSS-Protection: 0
),mode=block
表示若检查到 XSS 攻击则停止渲染页面
**X-Content-Type-Options:**响应头用来指定浏览器对未指定或错误指定Content-Type
资源真正类型的猜测行为,nosniff
表示不允许任何猜测
在通常的请求响应中,浏览器会根据Content-Type
来分辨响应的类型,但当响应类型未指定或错误指定时,浏览会尝试启用MIME-sniffing
来猜测资源的响应类型,这是非常危险的
例如一个.jpg 的图片文件被恶意嵌入了可执行的 js 代码,在开启资源类型猜测的情况下,浏览器将执行嵌入的 js 代码,可能会有意想不到的后果
另外还有几个关于请求头的安全配置需要注意
**Content-Security-Policy:**定义页面可以加载哪些资源,
add_header Content-Security-Policy "default-src 'self'";
上边的配置会限制所有的外部资源,都只能从当前域名加载,其中default-src
定义针对所有类型资源的默认加载策略,self
允许来自相同来源的内容
**Strict-Transport-Security:**会告诉浏览器用 HTTPS 协议代替 HTTP 来访问目标站点
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
上边的配置表示当用户第一次访问后,会返回一个包含了Strict-Transport-Security
响应头的字段,这个字段会告诉浏览器,在接下来的 31536000 秒内,
当前网站的所有请求都使用https
协议访问,参数includeSubDomains
是可选的,表示所有子域名也将采用同样的规则
21. nginx 开启 gz 压缩配置
gzip on;
gzip_min_length 1k;
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 6;
gzip_proxied any;
gzip_types application/octet-stream application/javascript text/plain text/css application/json application/x-javascript text/xml
application/xml application/xml+rss text/javascript image/x-icon image/bmp image/png image/gif
image/jpeg image/jpg image/svg+xml font/truetype font/opentype font/eot application/vnd.ms-fontobject(.svg .ttf .eot .woff);
# make sure gzip does not lose large gzipped js or css files
# see http://blog.leetsoft.com/2007/7/25/nginx-gzip-ssl
gzip_buffers 16 512k;
# Disable gzip for certain browsers.
gzip_disable ?~@~\MSIE [1-6].(?!.*SV1)?~@~];
22. nginx 配置 http=>https 跳转
server {
listen 80;
server_name wps-h5.cn;
return 301 https://$server_name$request_uri;
}
server {
listen 80;
server_name xiutangh5.cn;
return 301 https://$server_name$request_uri;
}
server {
listen 80;
server_name coh5.cn;
return 301 https://$server_name$request_uri;
}
23. 配置负载均衡
upstream app {
#ip_hash;
sticky name=ktag expires=10y httponly ;
server 10.0.0.17:8080 fail_timeout=30s ;
server 10.0.0.17:8081 fail_timeout=30s ;
}
upstream form{
ip_hash;
server 10.0.0.17:9080 fail_timeout=30s ;
}
map $http_user_agent $is_bot {
default 0;
~[a-z]bot[^a-z] 1;
~[sS]pider[^a-z] 1;
'Yahoo! Slurp China' 1;
'Mediapartners-Google' 1;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen 443 ssl http2;
server_name x.xxxx.cn;
ssl on;
ssl_certificate /etc/nginx/certs/stage.web.ksosoft.com.crt;
ssl_certificate_key /etc/nginx/certs/stage.web.ksosoft.com_nopwd.key;
access_log /logs/drivetest.web.ksosoft.com.log main;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1.1;
proxy_set_header x-scheme $scheme;
if ($is_bot) {
proxy_pass http://10.254.131.27:18000;
}
proxy_pass http://drivetest_app/;
}
location /events {
client_max_body_size 40m;
client_body_timeout 120;
proxy_pass http://10.253.18.132/events;
}
}
注意其中有些很关键的配置。
24. Error: worker_connections are not enough while connecting to upstream
这个错误是转发到后端服务的并发数不够用了,这个时候返回给前段的错误是500
。这个问题的解决办法是,将下面的连接数改大。
events {
worker_connections 1024;
}
25. Nginx 根据 IP 地址进行反向代理(if,else 用法)
在 nginx 中else是不能用的。可以采用下面的方法实现。
location ~ \.php$ {
root /www;
set $is_p 0;
if ($remote_addr ~* "172.31.0.190|172.31.0.193"){#productiong
fastcgi_pass 172.16.1.3:9000;
set $is_p 1;
}
if ($is_p = 0){#stage
fastcgi_pass 172.16.1.4:9000;
}
#fastcgi_pass 172.16.1.20:9000; #docker bridge0 -custom
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /www$fastcgi_script_name; #script path
include fastcgi_params;
}
注意语法,变量赋值用$set。if 后边一定要空格,否则不能识别判断。
26. Nginx 实现注入 trace_id 实现全链路追踪
set $trace_id "${request_id}";
if ($http_x_request_id != "" ){
set $trace_id "${http_x_request_id}";
}
add_header request_id $trace_id;
27. Nginx 实现脚本注入
location / {
sub_filter </head>
'</head><script language="javascript" src="$script"></script>';
sub_filter_once on;
}