Nginx 使用
Nginx 使用
配置文件
全局配置部分
user www www;
worker_process 2;
error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
pid logs/nginx.pid
events {
use epoll;
worker_connections 2048;
}
http {
...
server {
}
}
- user www www
nginx 的运行用户和组。
- worker_process 2
再配置文件的顶级 main 部分,表示 worker 角色的工作进程的个数。nginx 进程分为 master 和 worker。master负责接收并分配请求给 worker 处理。这个数值可以简单设置为 cpu 的核数 grep ^processor /proc/cpuinfo | wc -l, 也即是 auto 值。如果开启了 ssl 和 gzip 更应该设置成于逻辑 cpu 数目一样或者 2 倍,可以减少 io 操作。如果 nginx 服务器上还有其他服务,可以适当减少。
- worker_cpu_affinity
也是写在 main 部分。在高并发情况下,通过设置 cpu 粘性来降低由于多 cpu 核切换造成的寄存器等线程重建带来的性能损耗。如 worker_cpu_affinity 0001 0010 0100 1000 (四核)。、
- worker_rlimit_nofile 10240
写在 main 部分,默认没有设置,可以限制为操作系统最大的限制65535。
- worker_connections 2048
写在 event 部分,每一个 worker进程能够并发处理(发起)的最大连接数(包含与客户端或后端被代理服务器的连接)。Nginx 作为反向代理服务器时,最大连接数 = worker_processes * worker_connections/4,所以这里客户端最大连接数是 1024,这个可以增大到 8192,看情况而定。但不能超过上面的 worker_rlimit_nofile。当 nginx 作为 http 服务器时,最大连接数计算公式里是除以2。
http 服务器部分
http {
include mime.types;
default_type application/octet-stream;
# log_format main '$remote_addr - $remote_user [$time_local] "$request"'
# '#$datatus $body_bytes_sent "$http_refer"'
# '"$http_user_agent" "$http_x_forwarded_for"'
#access_log logs/access_log main;
sendfile on;
# tcp_nopush on;
keepalive_timeout 65;
# gzip 压缩功能设置
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 6;
gzip_types text/html text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
gzip_vary on;
# http proxy 设置
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 75;
proxy_send_timeout 75;
proxy_read_timeout 75;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_temp_path /usr/local/nginx/proxy_temp 1 2;
upstream backend {
}
server {
}
}
- sendfile on
开启搞笑文件传输模式。sendfile 指令指定 nginx
是否调用 sendfile 函数来输出文件,减少用户空间到内核空间的上下文切换。对于普通应用设为 on,如果用来进行下载等磁盘IO重负载应用,可以设置为 off,以平衡磁盘与网络IO处理速度,降低系统的负载。
- keepalive_timeout 65
长连接超时时间,单位是秒。这个参数很敏感,设计浏览器的种类,后端服务的超时设置,操作系统的超时设置。长连接请求大量小文件的时候,可以减少重建连接的开销,但假如有大文件上传,65s 内没有上传完成会导致失败。如果设置时间过长,用户又多,长时间保持连接会占用大量资源。
- send_timeout
用于指定相应客户端的超时时间。这个超时仅限于两个连接活动之间的时间,如果超过这个时间,客户端没有任何活动,nginx 就关闭连接。
- client_max_body_size 10m;
允许客户端请求的最大单文件字节数。如有上传较大文件,需要设置这个值。
- client_body_buffer_size 128k;
缓冲区代理缓冲用户端请求的最大字节数。
gzip 模块
- gzip on;
开启 gzip 压缩输出,减少网络传输。
- gzip_min_length 1k;
设置允许压缩的压面最小字节数,页面字节数从 header 头的 content-length 中进行获取。默认值是 20。建议设置成大于 1k 的字节数,小于 1k 可能越压越大。
- gzip_buffers 4 16k;
设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。4 16k 代表以16k为单位,按照原始数据的大小以 16k 为单位的4倍申请内存。
- gzip_http_version 1.0;
用于识别 http 协议的版本。早期的浏览器不支持 gzip 压缩,用户就会看到乱码,为了支持早期版本加上了这个参数。
- gzip_copm_level 6;
gzip 压缩比,1压缩比最小处理速度最快,9压缩比最大但处理速度最慢(传输快但比较消耗cpu)
- gzip_types
匹配 mime 类型进行压缩,无论是否制定,text/html 类型总是会被压缩的。
- gzip_proxied any
Nginx 作为反向代理的时候启用,决定开启或者关闭后端服务器返回的结果是否压缩,匹配的前提是后端服务器必须要返回包含 ‘Via’ 的 header 头。
- gzip_vary on
和 http 头有关系,会在响应加个 ‘Vary: Accept-Encoding’,可以让前端的缓存服务器缓存经过 gzip 压缩的压面。例如用过 squid 缓存经过 nginx 压缩的数据。
http proxy 模块
- proxy_connect_timeout 60;
nginx 跟后端服务器连接超时时间(代理连接超时)
- proxy_read_timeout 60;
连接成功后,与后端服务器两个成功的响应操作之间超时时间(代理接收超时)
- proxy_buffer_size 4k;
设置代理服务器(nginx)从后端 real server 读取并保存用户头信息的缓冲区大小,默认与 proxy_buffers 大小相同。其实可以将这个指令值设的小一点。
- proxy_buffers 4 32k
proxy_buffers 缓冲区,nginx 针对单个连接缓存来自后端 real server 的响应,网页平均在 32k 以下的话这样设置。
- proxy_busy_buffers_size 64k;
高负荷下缓冲大小(proxy_buffers * 2)
- proxy_max_temp_file_size
当 proxy_buffers 放不下后端服务器的响应内容时,会将一部分保存到硬盘的临时文件中,这个值用来设置最大临时文件的大小,默认为 1024m,它与 proxy_cache 没有关系。大于这个值,将从 upstream 服务器传回。设置 0 为禁用。
- proxy_temp_file_write_size 64k;
当缓存被代理服务器的响应到临时文件时,这个选项限制每次写临时文件的大小。
- proxy_temp_path
指定临时文件在哪个目录
server 虚拟主机部分
upstream backend {
server 192.168.10.100:8080 max_fails=2 fail_timeout=30s;
server 192.168.10.101:8080 max_fails=2 fail_timeout=30s;
}
server {
listen 80;
server_name laily.net;
root /apps/aapp;
charset utf-8;
access_log logs/host.access.log main;
# 对 / 反向代理
location / {
root /apps/aapp;
index index.html;
proxy_pass http://backend;
proxy_redirect off;
# 后端服务器通过 x-forwarded-for 获取用户的真实 ip
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_next_upstream eror timeout invalid_header http_500 http_502 http_503 http_504;
}
# 静态文件,nginx 自己处理
location ~* /download/ {
root /apps/file;
}
location ~ .*\.(gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ {
root /apps/aapp/static;
expires 7d;
}
location /nginx_status {
stub_status on;
access_log off;
allow 192.168.10.0/24;
deny all;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
http 服务商支持若干个虚拟主机。每个虚拟主机对应一个 server 配置项。
- listen
监听端口,默认 80,小鱼 1024 需要以 root 身份启动。可以为 listen *:80, listen 127.0.0.1:80 等形式
- server_name
服务器名,如 www.aa.com,localhost,可以通过正则表达式。
http_stream 模块
这个模块通过一个简单的调度算法来实现客户端 ip 到后端服务器的负载均衡, upstream 后接负载均衡器的名字,后端 real server 以 host:port options 的方式组织在 {} 中。
location
- root /var/www/html
定义服务器默认网站根目录地址。如果 location URL 匹配的是子目录或者文件,root 没什么用。一般放在 server 指令或者 / 下。
- index index.html
定义路径下默认的访问文件名,一般跟着 root 放。
- proxy_pass http://backend
请求转向 backend 定义的服务器列表,即反向代理,对应 upstream 负载均衡器。也可以 proxy_pass http://ip:port。
- proxy 选项
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
暂且这样设置,每个都涉及很多内容。
访问控制
使用多个 allow,deny,允许某个或禁止某个 ip,ip段的访问,依次匹配,满足任何一个规则就停止往下匹配。
autoindex
nginx 默认不允许列出整个目录。如果需要整个功能,就在配置文件的 location, server 或 http 段中加入 autoindex on; 一般也把下面两个配置加上。
- autoindex_exact_size off;
默认为 on,显示出文件的确切大小,单位是 bytes。改为 off 后,显示出文件的大概大小,单位是 kb,mb 或者 gb。
- autoindex_localtime on;
默认为 off,显示文件时间为 GMT时间。改为 on 后,显示服务器时间。
参考:
http://seanlook.com/2015/05/17/nginx-install-and-config/
配置 location, rewrite
一个简单的 nginx 配置
...
events {
# 模式配置
}
http {
# 默认配置
server {
# 虚拟主机1
listen 80 default_server;
server_name example1.com www.example1.com;
...
}
server {
# 虚拟主机2
listen 8080;
server_name example2.com;
...
}
server {
# 虚拟主机3
listen 80;
server_name example3.com
}
}
一个请求访问到服务器,nginx 根据 url 中的 host 部分,匹配到某一个虚拟主机。
default_server 表示默认虚拟主机,如果没有指定,则第一个 server 配置会被认为是默认虚拟主机。
location 配置
一个 location 表示一个url 规则。
location 语法
location 前缀 匹配字符 {具体配置}
前缀有这几种: =(全匹配), ~(正则匹配,区分大小写), *(正则匹配,不区分大小写), ^(路径匹配) 或者为空(字符串匹配)。
匹配的优先级:
全匹配 -> 路径匹配 -> 正则匹配 -> 字符串匹配(前缀为空)
rewrite 配置
rewrite 语法
rewrite regex replacement [flag]
其中 regex 是正则表达式,replacement 是要重定向的字符串, flag 是标识。
比如
rewrite ^/cms/(.*).php$ /cms/index.php break;
含义:访问 /cms/xxx.php 重定向到 /cms/index.php
rewrite 可以写在 location 里,也可以写在 server 里。
rewrite 的 flag 标记
break 表示完成当前的 rewrite
last 表示完成当前的 rewrite,然后查找匹配改变后的新 location
redirect 在 replacement 字符串未以 “http://” 或者 “https://” 开头时,使用返回状态码为 302 的临时重定向。
permanent 返回状态码为 301 的永久重定向
redirect 和 permanect 会修改用户访问的 url。
break 和 last 是 nginx 内部的重定向。
nginx rewrite log 调试
需要检查 nginx 到底匹配了哪条 location/rewrite,就需要打开 rewrite log,
开启方法,在 http 段加上
rewrite_log on;
# error log 需要改成 notice 级别
location 示例
# 文件和目录不存在时,重定向到某个文件
if (!-e $request_filename) {
rewrite ^/(.*)$ index.html last;
}
# 禁止访问指定后缀的文件
location ~ /(\.ht|\.git|\.snv) {
deny all;
break;
}
# 设置某些类型文件的浏览器缓存时间
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
}
location ~ .*\.(js|css)$ {
expires 1h;
}
# 目录自动添加 /
if (-d $request_filename) {
rewrite ^/(.*)([^/])$ http://$host/$1$2/ premanent;
}
# 只允许固定 ip 访问,并添加访问密码
root /opt/www
allow 208.97.167.19;
allow 222.333.11.22;
deny all;
auth_basic "admin";
auth_basic_user_file aaa;
全局变量
$args #这个变量等于请求行中的参数。
$content_length #请求头中的Content-length字段。
$content_type #请求头中的Content-Type字段。
$document_root #当前请求在root指令中指定的值。
$host #请求主机头字段,否则为服务器名称。
$http_user_agent #客户端agent信息
$http_cookie #客户端cookie信息
$limit_rate #这个变量可以限制连接速率。
$request_body_file #客户端请求主体信息的临时文件名。
$request_method #客户端请求的动作,通常为GET或POST。
$remote_addr #客户端的IP地址。
$remote_port #客户端的端口。
$remote_user #已经经过Auth Basic Module验证的用户名。
$request_filename #当前请求的文件路径,由root或alias指令与URI请求生成。
$query_string #与$args相同。
$scheme #HTTP方法(如http,https)。
$server_protocol #请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
$server_addr #服务器地址,在完成一次系统调用后可以确定这个值。
$server_name #服务器名称。
$server_port #请求到达服务器的端口号。
$request_uri #包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。
$uri #不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。
$document_uri #与$uri相同。
例如请求 http://localhost:88/test1/test2/test.php
各变量值如下:
$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test2/test.php
$document_uri:/test1/test2/test.php
$document_root:D:\nginx/html
$request_filename:D:\nginx/html/test1/test2/test.php
配置 LDAP 认证
安装依赖
这个模块依赖于 nginx 的 http_auth_request_module 模块,没有安装的需要安装。
需要 python2 支持。
需要安装 python 的 python-ldap 模块。
安装 python-ldap 容易出错,原因是 python-ldap 是基于 OpenLDAP 的,需要先安装 OpenLDAP 相关的包
# ubuntu
apt-get install libsasl2-dev python-dev libldap2-dev libssl-dev
# centos
yum install openldap
yum install openldap24-libs
yum install openldap-clients
yum install openldap-devel
yum install openssl-devel
获取 nginx-ldap-auth 仓库
git clone https://github.com/nginxinc/nginx-ldap-auth.git
配置 nginx
在上面克隆的仓库里能看到 nginx 验证部分的配置,这里有一份简单点的配置。
# http 指令里配置
# 缓存可以减少ldap验证频率,不然每个页面都需要ldap验证一次
proxy_cache_path cache/ keys_zone=auth_cache:10m;
# server 里配置
#后端程序
location / {
auth_request /auth-proxy;
#nginx接收到nginx-ldap-auth-daemon.py返回的401和403都会重新跳转到登录页面
error_page 401 403 =200 /login;
后端程序
proxy_pass http://127.0.0.1:1000/;
}
#登录页面,由backend-sample-app.py提供,跑在同一台机器的8082端口(默认不是8082端口)
location /login {
proxy_pass http://127.0.0.1:8082/login;
proxy_set_header X-Target $request_uri;
}
location = /auth-proxy {
internal;
proxy_pass http://127.0.0.1:8888; #nginx-ldap-auth-daemon.py运行端口
#缓存设置
proxy_cache auth_cache;
proxy_cache_key "$http_authorization$cookie_nginxauth";
proxy_cache_valid 200 403 10m;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
# 最最重要的ldap配置,请务必按照贵公司的ldap配置如下四项,我在这一步卡了好久,就是ldap配置不对
# 这些配置都会通过http头部传递给nginx-ldap-auth-daemon.py脚本
proxy_set_header X-Ldap-URL "ldap://ip:port";
proxy_set_header X-Ldap-BaseDN "DC=example,DC=com";
proxy_set_header X-Ldap-BindDN "cn=Admin,ou=People,dc=example,dc=com";
proxy_set_header X-Ldap-BindPass "password";
proxy_set_header X-CookieName "nginxauth";
proxy_set_header Cookie nginxauth=$cookie_nginxauth;
}
启动相关服务
然后启动 nginx-ldap-auth 目录里的 nginx-ldap-auth-daemon.py, backend-sample-app.py ,注意调整好端口。
启动 nginx-ldap-auth-daemon.py 报错
启动 nginx-ldap-auth-daemon.py 时报错 ImportError: No module named pyasn1.error。
执行下面的命令解决
$ sudo pip install --upgrade pip
$ sudo pip install awscli --ignore-installed six
# thon-ldap 缺少这个依赖,而且需要版本号大于 0.1.5
$ sudo pip install pyasn1_modules==0.1.5
然后登录就会出现一个输入框需要输入账户和密码。
不过这里我发现除了设置 cookies 以外,还使用了 http 的 basic 验证。
https 配置证书
listen 443 ssl;
listen [::]:443 ssl ipv6only=on;
server_name example.com;
ssl on;
ssl_certificate /etc/ssl/private/example_com.crt;
ssl_certificate_key /etc/ssl/private/example_com.key;
nginx 强制跳转 https
server {
listen 192.168.1.111:80;
server_name test.com;
rewrite ^(.*)$ <https://$host$1> permanent;
}
去掉 www
server {
listen 443;
server_name www.aaa.com
return 301 https://aaa.com$request_uri;
}
独立出证书部分配置新建立一个文件
比如 /etc/nginx/conf.d/ssl-params.conf 说明见 https://godruoyi.com/posts/best-nginx-configuration-for-improved-security
server_tokens off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 60m;
ssl_session_tickets on;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;
ssl_prefer_server_ciphers on;
# 证书路径 绝对地址
ssl_certificate /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/godruoyi.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload";
add_header X-Frame-Options deny;
add_header X-Content-Type-Options nosniff;
add_header x-xss-protection "1; mode=block";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: https:; connect-src 'self' https:; img-src 'self' data: https: blob:; style-src 'unsafe-inline' https:; font-src https:";
然后配置 server 部分
server {
listen 443 ssl http2 fastopen=3 reuseport default_server;
listen [::]:443 ssl http2 fastopen=3 reuseport default_server;
server_name www.aaa.com aaa.com;
# 引入 SSL 配置
include conf.d/ssl-params.conf;
root /home/bot/xxx/;
index index.php;
}
从静态 json 文件中响应 json 数据
location /get_data/ {
default_type application/json;
// index abc.json
alias /home/bot/mydata/;
}
这个时候如果访问 /get_data/aaa/bbb.json 就会去 /home/bot/mydata/aaa/bbb.json 文件去读取数据并返回。
也可以在上面加上 index xxx.json,这样可以相当于指定一个默认 json 文件。
配置中的 alias 和 root
root
root 是当前代理项目的 rul 的相对路径。
location /a/b/ {
root /c/d/e/;
}
实际的访问路径 /a/b/c/d/e/
alias
alias 是绝对路径
location /a/b/ {
root /c/d/e/;
}
实际访问的是 /c/d/e
配置跨域请求
当出现 403 跨域错误时“No ‘Access-Control-Allow-Origin’ header is present on the requested resource”,需要给 Nginx 配置响应的 header 参数。
解决方案
只需要在 Nginx 配置中配置以下参数
locaction / {
add_header Access-Control-Allow-Origin: *;
add_header Access-Control-Allow-Methods: 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers: 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = "OPTIONS"){
return 204;
}
}
解释
- Access-Control-Allow-Origin
服务器默认是不被允许跨域的。配置 Access-Control-Allow-Origin *后,表示允许所有的跨域请求。*处也可以填单个域名,比如http://example.com。如果要支持多个域名就需要用另外的方法了。
- Access-Control-Allow-Headers
这个参数是为了防止以下错误 Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.。这个错误表示当前请求的 Content-Type 不被支持。设置这个于后面的预检请求有关。
- Access-Control-Allow-Methods
设置允许的请求方法。
- OPTIONS 的 204 返回
跨域时发送预检请求时,会使用 OPTIONS 方法,需要支持。
预检请求
CORS 全称为 Cross-Origin Resource Sharing 是专门为了解决跨域请求提出的。这个标准新增了一组 http header 字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 http 请求方法(Get 以外的请求,搭配某些 MIME 的 post 请求),浏览器必须先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务器是否允许该跨域请求。服务器确认允许后,才发起实际的 http 请求。在预检请求的返回中,服务端也可以通知客户端是否需要携带身份凭证(Cookies 或者 http 认证数据)。
参考
Nginx配置跨域请求 Access-Control-Allow-Origin
反向代理后的 Cookie 问题
比如有一个 a.com 服务,现在通过 nginx (b.com) 搭建了反向代理。
location /api {
proxy_pass https://a.com;
}
如果 a.com 这个服务设置 cookie 的时候指定了域名 a.com,那么通过 b.com 访问的时候并不会主动带上这个域名,因为域名注册在 a.com 上,会导致访问失败。
这种情况下可以加上 proxy_cookie_domain 指令用来修改注册 cookie 的域名。同样的还有 proxy_cookie_path 指令。
location /api {
proxy_pass https://a.com;
proxy_cookie_domain a.com b.com;
}
反向代理
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header REMOTE-HOST $remote_addr;
}
示例
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}