重加载nginx配置
## 1)
systemctl reload nginx
## 2)
service nginx reload
## 3)
/etc/init.d/nginx reload
## 4)
/usr/sbin/nginx -s reload
## 5)
kill -HUP $(cat /var/run/nginx.pid)
## or
kill -HUP $(pgrep -f "nginx: master")
## 6)
/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
监听80和443端口
如果你使用完全相同的配置http和https提供服务(单个服务器同同时处理http和https请求),nginx足够智能,可能忽略通过端口80加载的SSL指令。 Nginx 的最佳时间是使用单独的服务器进行这样的重定向(不与你的主要配置的服务器共享),对所有内容进行硬编码,并且完全不适用正则表达式。 我不喜欢复制规则,但是单独的监听指令无疑可以帮助你维护和修改配置。 如果将多个域固定到一个ip地址,则很有用。这使你可以将一个监听指令(例如,如果将其保留在配置文件中)附加到多个域配置。 如果你使用的是https,则可能还需要对域进行硬编码,因为你必须预先知道要提供的证书。
## For HTTP:
server {
listen 10.240.20.2:80;
...
}
## For HTTPS:
server {
listen 10.240.20.2:443 ssl;
...
}
显示指定监听的地址和端口
Nginx的listen指令用于监听指定的ip地址和端口号,配置形式为:listen : 。若ip地址或端口缺失,Nginx会以默认值来替换。 而且,仅当需要区分域listen指令中的同一级别匹配的服务器块时,才会评估server_name指令。 实例:
server {
## This block will be processed:
listen 192.168.252.10; ## --> 192.168.252.10:80
...
}
server {
listen 80; ## --> *:80 --> 0.0.0.0:80
server_name api.random.com;
...
}
防止使用未定义的服务器名称处理请求
Nginx应该组织使用未定义的服务器名称(也使用ip地址)处理请求。它可以防止配置错误,例如流量转发不到不正确的后端。通过创建默认虚拟虚拟主机可以轻松解决该问题,该虚拟主机可以捕获带有无法识别的主机标头的所有请求。 如果没有一个listen指令具有default_server参数,则具有address:port对的第一台服务器将是该对的默认服务器(这意味着Nginx始终具有默认服务器)。 如果有人使用IP地址而不是服务器名称发出请求,则主机请求标头字段将包含IP地址,并且可以使用IP地址作为服务器名称来处理请求。 在现代版本的Nginx中,不需要服务器名称。如果找不到具有匹配的listen和server_name的服务器,Nginx将使用默认服务器。如果你的配置分散在多个文件中,则评估顺序将不明确,因此你需要显式标记默认服务器。 Nginx使用Host标头进行server_name匹配。它不适用TLSSNI。这意味着对于SSL服务器,Nginx必须能够接受SSL链接,这归结为具有证书/秘钥。证书/密钥而昆虫是任意值,例如自签名。 示例:
## Place it at the beginning of the configuration file to prevent mistakes:
server {
## For ssl option remember about SSL parameters (private key, certs, cipher suites, etc.);
## add default_server to your listen directive in the server that you want to act as the default:
listen 10.240.20.2:443 default_server ssl;
## We catch:
## - invalid domain names
## - requests without the "Host" header
## - and all others (also due to the above setting)
## - default_server in server_name directive is not required - I add this for a better understanding and I think it's an unwritten standard
## ...but you should know that it's irrelevant, really, you can put in everything there.
server_name _ "" default_server;
...
return 444;
## We can also serve:
## location / {
## static file (error page):
## root /etc/nginx/error-pages/404;
## or redirect:
## return 301 https://badssl.com;
## return 444;
## }
}
server {
listen 10.240.20.2:443 ssl;
server_name domain.com;
...
}
server {
listen 10.240.20.2:443 ssl;
server_name domain.org;
...
}
不要在 listen 或 upstream 中使用 hostname
通常,在 listen 或上游指令中使用主机名是一种不好的做法。
在最坏的情况下,Nginx 将无法绑定到所需的 TCP 套接字,这将完全阻止 Nginx 启动。
最好和更安全的方法是知道需要绑定的 IP 地址,并使用该地址代替主机名。 这也可以防止 Nginx 查找地址并消除对外部和内部解析器的依赖。
在 server_name 指令中使用$ hostname(计算机的主机名)变量也是不当行为的示例(类似于使用主机名标签)。
我认为也有必要设置 IP 地址和端口号对,以防止可能难以调试的软错误。 示例: 错误配置:
upstream {
server http://x-9s-web01-prod:8080;
}
server {
listen rev-proxy-prod:80;
...
}
正确配置:
upstream {
server http://192.168.252.200:8080;
}
server {
listen 10.10.100.20:80;
...
}
指令中只配置一个 SSL
此规则使调试和维护更加容易。
请记住,无论 SSL 参数如何,您都可以在同一监听指令(IP 地址)上使用多个 SSL 证书。
我认为要在多个 HTTPS 服务器之间共享一个 IP 地址,您应该使用一个 SSL 配置(例如协议,密码,曲线)。这是为了防止错误和配置不匹配。
还请记住有关默认服务器的配置。这很重要,因为如果所有 listen 指令都没有 default_server 参数,则配置中的第一台服务器将是默认服务器。因此,您应该只使用一个 SSL 设置,并且在同一 IP 地址上使用多个名称。
从 Nginx 文档中:
这是由 SSL 协议行为引起的。在浏览器发送 HTTP 请求之前,已建立 SSL 连接,nginx 不知道所请求服务器的名称。因此,它可能仅提供默认服务器的证书。
还要看看这个:
TLS 服务器名称指示扩展名(SNI,RFC 6066)是在单个 IP 地址上运行多个 HTTPS 服务器的更通用的解决方案,它允许浏览器在 SSL 握手期间传递请求的服务器名称,因此,服务器将知道哪个用于连接的证书。
另一个好主意是将常用服务器设置移到单独的文件(即 common / example.com.conf)中,然后将其包含在单独的服务器块中。
示例:
## Store this configuration in e.g. https.conf:
listen 192.168.252.10:443 default_server ssl http2;
ssl_protocols TLSv1.2;
ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384";
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp521r1:secp384r1;
...
## Include this file to the server context (attach domain-a.com for specific listen directive):
server {
include /etc/nginx/https.conf;
server_name domain-a.com;
ssl_certificate domain-a.com.crt;
ssl_certificate_key domain-a.com.key;
...
}
## Include this file to the server context (attach domain-b.com for specific listen directive):
server {
include /etc/nginx/https.conf;
server_name domain-b.com;
ssl_certificate domain-b.com.crt;
ssl_certificate_key domain-b.com.key;
...
}
使用 geo/map 模块替代 allow/deny
使用地图或地理模块(其中之一)可以防止用户滥用您的服务器。这样就可以创建变量,其值取决于客户端 IP 地址。
由于仅在使用变量时才对其进行求值,因此甚至仅存在大量已声明的变量。地理位置变量不会为请求处理带来任何额外费用。
这些指令提供了阻止无效访问者的完美方法,例如使用 ngx_http_geoip_module。例如,geo 模块非常适合有条件地允许或拒绝 IP。
geo 模块(注意:不要将此模块误认为是 GeoIP)在加载配置时会构建内存基数树。这与路由中使用的数据结构相同,并且查找速度非常快。如果每个网络有许多唯一值,那么较长的加载时间是由在数组中搜索数据重复项引起的。否则,可能是由于插入基数树引起的。
我将两个模块都用于大型列表。您应该考虑一下,因为此规则要求使用多个 if 条件。我认为,对于简单的列表,毕竟允许/拒绝指令是更好的解决方案。看下面的例子:
## Allow/deny:
location /internal {
include acls/internal.conf;
allow 192.168.240.0/24;
deny all;
...
## vs geo/map:
location /internal {
if ($globals_internal_map_acl) {
set $pass 1;
}
if ($pass = 1) {
proxy_pass http://localhost:80;
}
if ($pass != 1) {
return 403;
}
...
}
示例:
## Map module:
map $remote_addr $globals_internal_map_acl {
## Status code:
## - 0 = false
## - 1 = true
default 0;
### INTERNAL ###
10.255.10.0/24 1;
10.255.20.0/24 1;
10.255.30.0/24 1;
192.168.0.0/16 1;
}
## Geo module:
geo $globals_internal_geo_acl {
## Status code:
## - 0 = false
## - 1 = true
default 0;
### INTERNAL ###
10.255.10.0/24 1;
10.255.20.0/24 1;
10.255.30.0/24 1;
192.168.0.0/16 1;
}
Map所有事物
使用地图管理大量重定向,并使用他们来自定义建/值对。 map指令可映射字符串,因此可以表示例如192.168.144.0/24作为正则表达式,并继续使用map指令。 Map模块提供了一种更优雅的解决方案,使用清晰解析大量正则表达式,例如 用户代理,引荐来源。 你还可以对地图使用includ指令,这样配置文件看起来很漂亮。 实例:
map $http_user_agent $device_redirect{
default "desktop";
~(?i)ip(hone|od) "mobile";
~(?i)android.*(mobile|mini) "mobile";
~Mobile.+Firefox "mobile";
~^HTC "mobile";
~Fennec "mobile";
~IEMobile "mobile";
~BB10 "mobile";
~SymbianOS.*AppleWebKit "mobile";
~Opera\sMobi "mobile";
}
## Turn on in a specific context (e.g. location):
if ($device_redirect = "mobile") {
return 301 https://m.domain.com$request_uri;
}
为所有未配置的路径设置根路径
为请求设计服务器指令内部的全局根路径。它为未定义的位置指定根路径。 根据官方文档: 如果你在每个位置块中添加一个根路径,则不匹配的位置块将没有根路径。因此,重要的是,根指令必须在你的位置块之前发生,然后根目录指令可以在需要时覆盖该指令。 示例:
server {
server_name domain.com;
root /var/www/domain.com/public;
location / {
...
}
location /api {
...
}
location /static {
root /var/www/domain.com/static;
...
}
}
使用return指令进行URL重定向(301/302)
这是一个简单的规则。你应该使用服务器块和return语句,因为它们比评估RegEx更快。因为Nginx停止处理请求(而不必处理正则表达式),所以它更加简单快捷。 示例:
server {
server_name www.example.com;
## return 301 https://$host$request_uri;
return 301 $scheme://www.example.com$request_uri;
}
配置日志轮换策略
日志文件为你提供有关服务器活动和性能一级可能出现得任何问题的反馈。他们记录了有关请求和Nginx内部的详细信息。不幸的是,日志使用了更多的磁盘空间。 你应该定义一个过程,该过程将定期存档当前日志文件并启动一个新日志文件,重命名并有选择地压缩当前日志文件,删除旧日志文件,并强制日志记录系统开始使用新日志文件。 我认为最好的工具是logrotate。如果我想自动管理日志,也想睡个好觉,那么我会在任何地方使用它。那么我会在任何地方使用它。这是一个旋转日志的简单程序,使用corontab可以工作。它是计划的工作,而不是守护程序,因为无需重新加载其配置。 示例:
## Check manually (all log files):
logrotate -dv /etc/logrotate.conf
## Check manually with force rotation (specific log file):
logrotate -dv --force /etc/logrotate.d/nginx
cat > /etc/logrotate.d/nginx << __EOF__
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 nginx nginx
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
## test ! -f /var/run/nginx.pid || kill -USR1 `cat /var/run/nginx.pid`
invoke-rc.d nginx reload >/dev/null 2>&1
endscript
}
/var/log/nginx/localhost/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 nginx nginx
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
## test ! -f /var/run/nginx.pid || kill -USR1 `cat /var/run/nginx.pid`
invoke-rc.d nginx reload >/dev/null 2>&1
endscript
}
/var/log/nginx/domains/example.com/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 nginx nginx
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
## test ! -f /var/run/nginx.pid || kill -USR1 `cat /var/run/nginx.pid`
invoke-rc.d nginx reload >/dev/null 2>&1
endscript
}
__EOF__
不要重复所有指令,只能在http块中使用
一次使用index指令。它只需要在你的http上下文中发生,并将在下面继承。 我认为我们在赋值相同规则时应格外小心。但是,当前,规则的重复有时是可以的,或者不一定是大麻烦。 示例: 错误配置
http {
...
index index.php index.htm index.html;
server {
server_name www.example.com;
location / {
index index.php index.html index.$geo.html;
...
}
}
server {
server_name www.example.com;
location / {
index index.php index.htm index.html;
...
}
location /data {
index index.php;
...
}
...
}
正确的配置:
http {
...
index index.php index.htm index.html index.$geo.html;
server {
server_name www.example.com;
location / {
...
}
}
server {
server_name www.example.com;
location / {
...
}
location /data {
...
}
...
}
Debugging
使用自定义日志格式
你可以在Nginx配置中作为变量访问的任何内容都可以记录,包括非标准的HTTP标头等。因此,这是一种针对特定情况创建自己的日志格式的简单办法。 这对于调试特定的location指令非常有帮助。 示例:
## Default main log format from the Nginx repository:
log_format main
'$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
## Extended main log format:
log_format main-level-0
'$remote_addr - $remote_user [$time_local] '
'"$request_method $scheme://$host$request_uri '
'$server_protocol" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time';
## Debug log formats:
log_format debug-level-0
'$remote_addr - $remote_user [$time_local] '
'"$request_method $scheme://$host$request_uri '
'$server_protocol" $status $body_bytes_sent '
'$request_id $pid $msec $request_time '
'$upstream_connect_time $upstream_header_time '
'$upstream_response_time "$request_filename" '
'$request_completion';
使用调试模式来追踪以外行为
通常,error_log指令是在main中指定的,但是也可以在server或location块中指定,全局设置将被覆盖,并且这个error_log指令将设置其自己的日志文件和日志记录级别。 如果要记录ngx_http_rewite_module(at the notice level),应该在http、server或location块中开启rewrite_log on; 注意:
示例
## Turn on in a specific context, e.g.:
## - global - for global logging
## - http - for http and all locations logging
## - location - for specific location
error_log /var/log/nginx/error-debug.log debug;
error_log memory:32m debug;
events{
debug_connection 192.168.252.15/32;
debug_connection 10.10.10.0/24;
}
error_log /var/log/nginx/debug.log debug;
...
http {
server {
## To enable debugging:
error_log /var/log/nginx/domain.com/domain.com-debug.log debug;
## To disable debugging:
error_log /var/log/nginx/domain.com/domain.com-debug.log;
...
}
}
核心转储
核心转储基本上是程序崩溃时内存的快照。 Nginx是一个非常文档的守护程序,但是有时可能会发生正在运行的Nginx进程独特终止的情况 如果要保存内存存储,它可以确保应启用两个重要的指令,但是,为了正确处理内存转储,需要做一些事情。有关它的完整信息,请参阅转储进程的内存(来自本手册)。 当你的Nginx实例收到以外错误或崩溃,应始终启用核心转储。 实例:
worker_rlimit_core 500m;
worker_rlimit_nofile 65535;
working_directory /var/dump/nginx;
性能
工作进程数
work_processes -用于设置Nginx的工作进程。
一般一个进程足够了,你可以把连接数设的很大。(worker_processes:1,work_connections:10000)如果有SSL、gzip这些比较消耗CPU的工作,而且是多核CPU的话,可以设为和CPU的数量一样。或者要处理很多很多的小文件,而且文件总大小比内存大很多的时候,也可以吧进程数增加,以充分利用Io带宽(主要似乎是IO操作有block)。
实例:
## the safest way:
worker_processes auto;
## VCPU = 4,expr $(nproc --all) -1
worker_processes 3;
最大连接数
worker_connections - 单个Nginx工作进程允许同时建立的外部链接的数量。数字越大,能同时处理的连接越多。 worker_connections不是随便设置的,而是与两个指标有重要关联:
使用HTTP/2
HTTP/2将使我们的应用程序更快,更简单且可靠。HTTP/2的主要目标是通过启用完整的请求和响应多路复用来减少延迟,通过有效压缩HTTP标头字段来最小化协议开销,并增加对请求优先级和服务器推送的支持。 HTTP/2与HTTP/1.1向后兼容,因此有可能完全忽略它,并且一些都会像以前一样继续工作,因为如果不支持HTTP/2的客户端永远不会像服务器请求HTTP/2通讯升级:他们之间的通讯将完全是HTTP/1. 请注意,http/2在单个TCP连接中多路复用许多请求。通常,当使用HTTP/2时,将与服务器建立单个TCP链接。 你还应该包括ssl参数,这是必须得,因为浏览器不支持未经加密的HTTP/2. HTTP/2对旧和不安全的密码有一个非常大的黑名单,因此你应该避免使用他们。 实例:
server{
listen 10.240.20.2:443 ssl http2;
...
}
维护SSL会话
客户端每次发出请求是都进行新的SSL握手的需求。默认情况下,内置会话缓存并不是最佳选择,因为它只能由一个工作进程使用,并且可能导致内存碎片,最好是共享缓存。 使用ssl_session_cache时,通过ssl保持链接的性能可能会大大提高。10M的值是一个很好的起点(1MB共享缓存可以容纳大约4000个会话)。通过共享,所有工作进程之间共享一个缓存(可能在多个虚拟服务器中使用相同名称的缓存)。 但是,大多数服务器不清除会员或票证秘钥,因此增加了服务器受到损害将泄露先前(和将来)连接中的数据的风险。 示例:
ssl_session_cache shared:NGX_SSL_CACHE:10m;
ssl_session_timeout 12h;
ssl_session_tickets off;
ssl_buffer_size 1400;
尽可能在server_name指令中使用明确名称
确切名称,以星号开头的通配符名称和以星号结尾的通配符名称存储在绑定到监听端口的三个哈希表中,首先搜索确切名称哈希表。如果未找到名称,则搜索具有以星号开头的通配符的哈希表。如果未在此处找到名称,则搜索带有通配符名称以星号结尾的哈希表。搜索通配符名称哈希表比搜索精确名称哈希表要慢,因为名称是按域部分搜索的。 正则表达式是按顺序测试的,因此是最慢的方法,并且不可缩放。由于这些原因,最好在可能的地方使用确切的名称。 示例:
## It is more efficient to define them explicitly;
server{
listen 192.168.252.10:80;
server_name example.org www.example.org *.example.org;
...
}
## than to use the simplified form:
server {
listen 192.168.252.10:80;
server_name .example.org;
...
}
避免使用if检查serve_name
当Nginx受到请求时,无论请求的是那个子域,无论是www.example.com还是普通的example.com,如果始终对if指令进行评估。由于你是在nginx检查每个请求的Host标头。效率极低。 而是使用两个服务器指令,如下面的示例。这种方法降低了Nginx的处理要求。 示例: 错误配置
server{
server_name domain.com www.domain.com;
if($host = www.domain.com){
return 301 https://domain.com$request_uri;
}
server_name domain.com
...
}
正确的配置:
server{
server_name www.domain.com;
return 301 $scheme://domain.com$request_uri;
## if you force your web traffic to use https:
## 301 https://domain.com$request_uri;
}
server{
listen 192.168.252.10:80;
server_name domain.com;
...
}
使用$request_uri来避免使用正则表达式
使用内置变量$request_uri,我们可以完全避免进行任何捕获或匹配。默认情况下,正则表达式的代价较高,并且会降低性能。 此规则用于解决将URL不变地传递到新主机,确保仅通过现有URI进行返回的效率更高。 示例: 错误示例:
## 1)
rewrite ^/(.*)$ https://example.com/$1 permanent;
## 2)
rewrite ^ https://example.com$request_uri?permanent;
正确配置:
return 301 https://example.com$request_uri;
使用try_files指令确认文件是否存在
try_files绝对是一个非常有用的东西。你可以使用try_files指令检查一个文件是否按指定顺序存在。 你应该使用try_files而不是if指令。对于这个操作,它肯定是比使用if更好的方法,因为if指令非常低效,因为它在每次请求时都会执行。 使用try_files的好处是,一个命令可以立即切换行为。我认为代码也更具有可读性。 try_files允许你;
从预定义的列表中检查文件是否存在
检查指定目录下的文件是否存在
如果找不到任何文件,则使用内部重定向。
示例 错误配置:
...
root /var/www/example.com;
location /images {
if (-f $request_filename) {
expires 30d;
break;
}
...
}
正确配置:
...
root /var/www/example.com;
location /images {
try_files $uri =404;
...
}
使用return代替rewrite来做重定向
你应该使用服务器块和return语句,因为他们比通过位置块评估RegEx更简单,更快捷。该指令停止处理,并将指定的代码返回给客户端。 示例: 错误配置:
server {
...
if ($host = api.domain.com) {
rewrite ^/(.*)$ http://example.com/$1 permanent;
}
...
正确配置:
server {
...
if ($host = api.domain.com) {
return 403;
## or other examples:
## return 301 https://domain.com$request_uri;
## return 301 $scheme://$host$request_uri;
}
...
开启PCRE JIT来加速正则表达式处理
允许使用JIT的正则表达式来加速他们的处理 通过PCRE库编译Nginx的,你可以用你的location块进行复杂的操作和使用功能强大的return和rewrite。 PCRE JIT可以显著加快正则表达式的处理。Nginx的与pcre_jit比没有更快的幅度。 如果你试图在使用pcre_jit;没有可用的JIT,或者Nginx的与现有JIT,单当前加载PCRE库编译不支持JIT,将配置解析式发出警告。 当你编译使用nginx配置pcre库时,才需要--with-PCRE-JIT时(./configure --with-PCRE=)。当使用系统PCRE库JIT是否被支持依赖于库是如何被编译。 从nginx的文档: JIT正在从与--enable-JIT配置参数内置8.20版本开始PCRE库提供。当PCRE库与nginx的内置(with-PCRE=)时,JIT支持经由--with-PCRE-JIT配置参数使能。 示例:
## in global context:
pcre_jit on;
进行精确的位置匹配以加快选择过程
精确的位置匹配通常用于通过立即结束算法的执行来加快选择过程。 示例:
## Matches the query / only and stops searching:
location = / {
...
}
## Matches the query /v9 only and stops searching:
location = /v9 {
...
}
...
## Matches any query due to the fact that all queries begin at /,
## but regular expressions and any longer conventional blocks will be matched at first place:
location / {
...
}
使用limit_conn改善对下载速度的限制
Nginx提供了两个指令来限制下载速度
此解决方案限制了每个链接的nginx下载速度,因此,如果一个用户打开多个(例如)视频文件,则可以下载X*链接到视频文件的次数。 示例:
## Create limit connection zone:
limit_conn_zone $binary_remote_addr zone=conn_for_remote_addr:1m;
## Add rules to limiting the download speed:
limit_rate_after 1m; ## run at maximum speed for the first 1 megabyte
limit_rate 250k; ## and set rate limit after 1 megabyte
## Enable queue:
location /videos {
## Max amount of data by one client: 10 megabytes (limit_rate_after * 10)
limit_conn conn_for_remote_addr 10;
...