Nginx 配置
内容列表
建议主要参考官网英文文档。
具体指令直接可以在官网文档的 Alphabetical index of directives(按字母顺序排列的指令索引)中搜索即可。
官方文档:http://nginx.org/en/docs/
Nginx
Nginx 是一位俄罗斯开发者(伊戈尔·赛索耶夫)开发的服务器,于 2004 年 10 月 4 日公开发布。Nginx 的优势在于轻量级和高性能,尤其是高并发的场景下,相对其它服务器来说表现比较好,因此现在颇受欢迎。Nginx 通常运行在 Unix/Linux 环境下,当然现在官方也发布了 Windows 环境下的应用,不过性能有所降低,这是受限于系统环境的影响。
Nginx 在生产环境下的应用场景通常作为负载均衡的前端服务器,对请求进行分发,实现极高的并发量。当然,在开发环境下,nginx 也可以作为一个工具来使用,提供给我们极大的便利,例如利用反向代理来实现前后端的完全分离开发。
Nginx 的架构被设计为模块化,从官方文档我们就可以明显的看出来,相应的配置需要在对应的模块中去查找。默认安装的情况下,官方文档中的所有模块并不会被全部安装,只会安装大部分满足常用需求的模块,至于一些特殊需求所要用到的模块,可以自己手动编译安装,当然模块化的好处就是可以自己开发模块来扩展 nginx 的功能。
Nginx 特定场景下的配置
不管 nginx 基于什么场景发挥什么作用,都是基于特定的配置来实现,nginx 的配置文件也相对比较简单。
工作进程
Nginx 是基于异步非阻塞 IO 模型的,同时也支持多进程,通常将其工作进程数目设置为 CPU 的核心数,以发挥其最大作用,实现高并发。
{
worker_processes 4;
...
}
这个配置是写在配置文件顶部的,其值也可以为 auto
。
隐藏 nginx 版本号
隐藏掉版本号,可以降低被攻击的风险。
http {
...
server_tokens off;
}
设置编码
通常来说,将编码设置为 UTF-8
是比较合适的。
server {
...
charset utf-8;
}
更改上传数据大小限制
Nginx 默认的数据上传大小为 2M,某些情况下我们需要将其更改的大一些,以符合业务需求。
server {
...
client_max_body_size 20m;
}
开启 gzip
开启 gzip 压缩可以在客户端请求文本文件时,将传输大小压缩至少**70%**左右,可以获得非常好的优化效果,通常都会开启 gzip 压缩配置。
http {
# ...
# gzip
gzip on;
gzip_min_length 20;
gzip_buffers 4 16k;
gzip_comp_level 6;
gzip_types text/plain text/xml text/css text/javascript application/x-javascript application/javascript application/json;
gzip_http_version 1.0;
gzip_disable "MSIE [1-6]\.";
gzip_proxied off;
gzip_vary on;
# ...
}
其中有几个配置需要特别注意:
gzip_min_length
文件大小小于该值的文件将不会被压缩,大于此值时才会被压缩。
gzip_buffers
设置用于处理请求压缩的缓冲区数量和大小。比如
32 4K
表示按照内存页(one memory page)大小以 4K 为单位(即一个系统中内存页为 4K),申请 32 倍的内存空间。通常默认即可。gzip_comp_level
设置压缩级别,值为 1-9。压缩级别越高,压缩效果越好,但同时越耗费时间和 CPU 性能,所以通常设置为 6 即可。
gzip_types
设置要压缩的文件 MIME 类型,默认包含
text/html
。gzip 只对文本文件的压缩效果较好,不建议设置非文本文件。gzip_http_version
设置要进行压缩的 http 协议版本,默认设置为 1.0 即可,因为 nginx 和后端服务器(Server)默认采用 HTTP/1.0 进行通信的,防止出现不压缩的情况。
官网文档:ngx_http_gzip_module
路由匹配规则
nginx 像一个路由,客户端通过什么地址访问服务器,服务器则在配置文件中通过设置好的路由来匹配请求进行转发。
nginx 的匹配规则分为 3 类:
- 正则匹配:由
~
(不忽略大小写) 和~*
(忽略大小写)开头 - 精确匹配:由
=
开头 - 前缀匹配:由
^~
开头或没有任何字符的规则
匹配顺序:首先检查精确匹配,匹配到则终止;其次,检查前缀字符串匹配,匹配到时,若是以 ^~
开头的则终止,否则继续进行正则匹配;最后,检查正则匹配,顺序为配置文件中书写顺序(从上到下),匹配到第一条则终止,若没匹配到,则以匹配到的前缀匹配规则为最终结果。
# 精确匹配,加速 / 请求的处理
location = / {
# ...
}
# 前缀匹配,处理一些需要缓存的静态资源
location ^~ /static/ {
root /Data/static/;
expires 7d;
}
# 正则匹配,处理静态资源
location ~* \.(html|js|css|png|jpg|jpeg|gif|json|ico|otf|eot|svg|ttf|woff|woff2|map)$ {
root /Data/webapps/;
}
# 前缀匹配,默认处理(可以做反向代理,处理动态资源请求)
location / {
proxy_pass http://127.0.0.1:8080;
}
在非精确匹配的规则内部是可以嵌套 location
规则的。
####### 调试技巧
nginx 本身是比较难调试的,不过在配置 location
指令时,可以利用 return
指令来进行调试。
location /test/ {
return 600;
}
此时若访问 /test/ 路径,可以看到响应码为 600
时,说明路径匹配成功。同时,可以添加一些辅助文本信息:
location /test/ {
default_type text/html;
return 600 'Hello';
}
虚拟目录
虚拟目录解决了客户端请求资源的 URL 与服务器端对应资源存在位置不一致的问题。如下所示:
# 虚拟目录
location /static/ {
alias /DataDisk/resources/;
}
# 这样,客户端发送 example.com/static/bg.png 的请求实际映射到了服务器端 /DataDisk/resources/bg.png 的资源上
URL 作为统一资源定位符,代表的是资源所在的真实网络位置,但在某些情况下,出于安全性、降低逻辑复杂性等因素的考虑,给客户端提供一个虚拟目录可能更好,这个时候使用 alias
指令就可以实现。
作为对比,我们使用 root
指令通常指定的是真实目录。如下所示:
# 真实目录
location /static/ {
root /DataDisk/resources;
}
# 这样,客户端发送 example.com/static/bg.png 的请求实际映射到了服务器端 /DataDisk/resources/static/bg.png 的资源上
可以看出,root
指令通常适合用在资源路径完全真实存在的情况下,而 alias
指令则更适合用在资源路径前缀部分不是真实存在的情况下。
文件列表浏览
静态资源服务器一般允许用户查看服务器上的文件列表,例如 CDN、镜像站等。nginx 出于安全考虑,默认是不允许客户端浏览器查看服务器上的文件列表的,可以通过以下指令来进行配置:
location /static/ {
autoindex on; # 开启客户端文件列表浏览
autoindex_exact_size off; # 默认显示的文件确切大小,单位 b,关闭后自动计算 KB/MB/GB 等
autoindex_localtime on; # 文件的改动时间以服务器时间为准
}
允许跨域
有时候,比较大(几百兆以上)的静态资源需要在客户端使用异步方式加载(例如 Ajax),但是多个人合作开发时,拷贝这些静态资源到各自本地(如果不这么做,将会出现跨域问题)是最糟糕的解决方案,这个时候我们可以将静态资源放在一个服务器上,然后使用反向代理或者允许跨域的配置巧妙的解决这个问题。
location /static/ {
...
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
add_header 'Access-Control-Allow-Credentials' 'true';
}
Access-Control-Allow-Origin
必选,这个响应头信息代表的是允许跨域请求的域名,
*
则表示允许任意域名向此服务器发起跨域请求。Access-Control-Allow-Credentials
可选,这个响应头信息代表的是跨域请求是否需要携带 Cookie 信息,默认为
false
,在需要利用 Session-Cookie 机制的情况下务必设置为true
。
反向代理
Nginx 可以作为一个反向代理服务器,来为我们提供一些场景下的解决方案,例如负载均衡、跨域、前后端完全分离开发场景等等。
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cookie_path /project/ /;
proxy_pass http://127.0.0.1:8181/project/;
}
这里有几点需要注意下:
proxy_set_header
目的是为了保证后端(被代理的)服务器获取到远程客户端的真实信息,相当于将前端(nginx 反向代理)服务器的信息隐藏,造成客户端直接访问后端服务器的“假象”。
Host
应尽可能设置成$http_host
,这样会包含完整的 IP 和端口信息,设置为$host
时将不会包含端口信息。proxy_cookie_path
目的是为了在访问路径与代理路径发生改变(不一致)的情况下防止出现客户端 Cookie 丢失的问题。
proxy_pass
则是后端(被代理)服务器地址。
####### 代理服务路径变化时
如果说在反向代理过程中,路径没有差异,一般来说不会出现什么问题,但是如果路径有变化时,会出现两个问题,一个是 cookie 丢失,另一个则是 后端服务器重定向错误。 第一个问题可以用 proxy_cookie_path
指令解决,第二个问题则使用 proxy_redirect
指令解决。具体如下:
location /test/ {
proxy_cookie_path /project/ /test/;
proxy_pass http://127.0.0.1:8181/project/;
proxy_redirect ~(https?://[^/]+)?/project/(.*) $scheme://$http_host/test/$2;
}
首先,proxy_pass
指令配置的代理服务在用户实际访问时路径发生了变化。用户以 /test/users 路径访问时,实际被 nginx 代理到后端的服务路径为 /project/users,可以明显的看到路径的前缀发生了变化。
此时,proxy_cookie_path
指令告诉 nginx 将后端被代理服务的响应头中 cookie_path 进行转换,这样在客户端访问任意路径时,cookie_path 也会保持和访问路径一致,而不是实际代理的服务路径,否则 cookie 将会在客户端丢失。
同时,如果说被代理的服务有重定向需求的话,不配置 proxy_redirect
指令,重定向的路径将会发生错误,需要告诉 nginx 将其路径中部分进行替换。例如,用户访问 /test/,被代理的服务路径为 /project/,此时被代理服务做一个重定向操作到 /project/index.html,如果不做转换,用户会直接访问该路径将发生错误。在这里,proxy_redirect
指令所做的就是将响应头中 Location
字段的值由 /project/index.html 替换为 /test/index.html,这样用户将会正常访问到资源。
重定向
重定向是一个比较常见的需求,nginx 的重定向指令(rewrite)还是相当简单的。例如,需要将所有 http 请求重定向到 https 下,官方推荐这么做:
server {
listen 80;
server_name localhost;
return 301 https://example.com$request_uri;
}
事实上,也可以用 rewrite
指令,不过官方不推荐:
server {
...
rewrite ^/(.*)$ https://example.com/$1 permanent;
}
注意: 301
重定向可能会导致 POST 请求被改变为 GET 请求,并可能丢失提交数据,此时使用 308
状态码替换即可。
####### 项目首页重定向
大多数时候,我们在同一个域名下会部署多个 Web 应用,访问的话需要 WebAppName 来进行区分,例如 localhost:80/App
,那么 App
其实就代表了一个 Web 应用,将会映射到相应的文件夹。这里有一个细节性问题,文件夹的路径必然以 /
结束,所以大多数服务器都会自动做一次重定向,将 localhost:80/App
重定向到 localhost:80/App/
。如果 Nginx 没有配置,默认是不会做这个重定向的,为了用户访问方便,我们需要解决这个问题:
localhost / {
# 这是一个默认配置文件中的配置项
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
这个配置基本上解决了该问题,但在内外网端口不一致时,会出现问题。例如,我们通过 www.example.com:80/App
访问部署在内网 8080
端口上的 Nginx 时,Nginx 会将其重定向到 www.example.com:8080/App/
,这里的差异在于,重定向时丢失了外网端口,用户此时将会访问失败。
目前,还没找到比较优雅的解决办法,可以用以下配置暂时解决该问题:
location ~ ^/[^/]+$ {
return 301 $scheme://$http_host$uri/;
}
日志分割
Nginx 的访问日志(access_log)默认是没有进行分割的,时间一长,日志文件就会有 GB 级别的大小,日志写入速度变慢,也会影响 nginx 的性能。我们可以通过很简单的方式,将访问日志设置为按天记录,将日志记录在不同的文件中。
server {
...
# cut log by day
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})") {
set $year $1;
set $month $2;
set $day $3;
}
access_log logs/access/host.access-$year-$month-$day.log main;
}
官网文档:ngx_http_log_module