FreeBSD 上 (截止到 2021.9.13 FreeBSD 13) 没有 ngx_stream_proxy_module 这个模块,所以 proxy protocol 这个特性没法在 FreeBSD 上用。
如果用了 Cloudflare 的 DNS 解析服务的话,那出现这个问题的原因可能是 Cloudflare 的 SSL/TLS 配置没有选对。
打开 Cloudflare 域名配置的 SSL/TLS 配置下的 Overview 选项卡后,可以看到当前域名下的 SSL/TLS 加密配置,有以下几个选项:
浏览器报重定向次数过多的原因是重定向规则有环。如果 Cloudflare 的 SSL/TLS 加密配置用了 Flexible ,那么 Cloudflare 到 Nginx 的数据就不被加密。这意味着,Cloudflare 会使用 HTTP 请求 Nginx 的 80 端口。但是 Nginx 里面的配置又写了 80 端口的 HTTP 被重定向到 HTTPS,这时候 Cloudflare 解析出重定向规则后就会告诉浏览器重新用 HTTPS 请求该地址,而该地址本身就是以 HTTPS 请求发到 Cloudflare 的,就是这样的环:
https://lishouzhong.com http://lishouzhong.com
浏览器 -------------------------> Cloudflare ------------------------> lishouzhong.com
^--------------------------------------------------------------------------------|
301/302 Redirect
Location: https://lishouzhong.com
遇到的问题是,Nginx 为 Gitea 提供反向代理,在向 Gitea 推送 commit 时,如果推送的 commit 中包含了大文件会出现无法推送的情况。
原因是 nginx 默认只允许 1M 大小的上行数据,即上传文件或者接收请求最大请求包不能超过 1M。要解决这个问题,需要修改 nginx 配置文件,可以在下列位置重新做限制:
http {
client_max_body_size 50M;
...
server {
client_max_body_size 50M;
...
location {
...
client_max_body_size 50M;
}
}
}
3 个主要应用场景:
4 个主要组成部分:
5 个主要优点:
可用命令 nginx -s
发送的信号:
nginx -s stop
立即停止 nginx 进程。nginx -s quit
优雅地停止 nginx 进程(不向用户发送 tcp reset 这种报文)。nginx -s reload
重载配置文件。nginx -s reopen
重新打开日志文件做日志文件的切割。只能用 kill 命令发送的信号:
kill -USR2 <old_pid>
:
kill -WINCH <old_pid>
:
kill -HUP <old_pid>
:
nginx -s reload
指令的作用,把旧 nginx 的worker进程拉起来,但这里并不直接使用 reload 的方式执行。kill -USR2 <new_pid>
:
直接备份原本的日志文件后,执行 nginx -s reopen
会使 nginx 重新生成日志文件。
SSL 证书更新后,需要执行 nginx -s reload
让 nginx 重新读取配置文件才能使用新的证书。
时间配置参数:
nginx 的 location 块的语法有两种写法:
location [ = | ~ | ~* | ^~ ] <uri> { ... }
location @name { ... }
第一部分参数根据检索顺序进行说明:
Search-Order | Modifier | Description | Match-Type | Stops-search-on-match |
---|---|---|---|---|
1st | = | The URI must match the specified pattern exactly | Simple-string | Yes |
2nd | ^~ | The URI must begin with the specified pattern | Simple-string | Yes |
3rd | (None) | The URI must begin with the specified pattern | Simple-string | No |
4th | ~ | The URI must be a case-sensitive match to the specified Rx | Perl-Compatible-Rx | Yes (first match) |
5th | ~* | The URI must be a case-insensitive match to the specified Rx | Perl-Compatible-Rx | Yes (first match) |
N/A | @ | Defines a named location block. | Simple-string | Yes |
location 块的 uri pattern 也支持 Rx Capturing-group (正则表达式捕获组),且 Rx 内部的 () 默认为捕获组模式,使用 (?:) 可以关闭捕获组模式。比如 (?:a|b) 意为以非捕获组模式对 a|b 进行匹配。
比如 location ~ ^/(?:index|update)$ 可以匹配 example.com/index 和 example.com/update 。
# -------------------------------------------------------------------------------------
# () : Group/Capturing-group, capturing mean match and retain/output/use what matched
# the patern inside (). the default bracket mode is "capturing group" while (?:)
# is a non capturing group. example (?:a|b) match a or b in a non capturing mode
# -------------------------------------------------------------------------------------
# ?: : Non capturing group
# ?= : Positive look ahead
# ?! : is for negative look ahead (do not match the following...)
# ?<= : is for positive look behind
# ?<! : is for negative look behind
# -------------------------------------------------------------------------------------
正向斜杠 / 在 nginx 中没有特殊含义,比如, location / 可以匹配任以路径。而反斜杠 \ 为转译字符。
# -------------------------------------------------------------------------------------
# / : It doesn't actually do anything. In Javascript, Perl and some other languages,
# it is used as a delimiter character explicitly for regular expressions.
# Some languages like PHP use it as a delimiter inside a string,
# with additional options passed at the end, just like Javascript and Perl.
# Nginx does not use delimiter, / can be escaped with \/ for code portability
# purpose BUT this is not required for nginx / are handled literally
# (don't have other meaning than /)
# -------------------------------------------------------------------------------------
nginx 支持 Perl-Compatible-Rx:
# -------------------------------------------------------------------------------------
# ~ : Enable regex mode for location (in regex ~ mean case-sensitive match)
# ~* : case-insensitive match
# | : Or
# () : Match group or evaluate the content of ()
# $ : the expression must be at the end of the evaluated text
# (no char/text after the match) $ is usually used at the end of a regex
# location expression.
# ? : Check for zero or one occurrence of the previous char ex jpe?g
# ^~ : The match must be at the beginning of the text, note that nginx will not perform
# any further regular expression match even if an other match is available
# (check the table above); ^ indicate that the match must be at the start of
# the uri text, while ~ indicates a regular expression match mode.
# example (location ^~ /realestate/.*)
# Nginx evaluation exactly this as don't check regexp locations if this
# location is longest prefix match.
# = : Exact match, no sub folders (location = /)
# ^ : Match the beginning of the text (opposite of $). By itself, ^ is a
# shortcut for all paths (since they all have a beginning).
# .* : Match zero, one or more occurrence of any char
# \ : Escape the next char
# . : Any char
# * : Match zero, one or more occurrence of the previous char
# ! : Not (negative look ahead)
# {} : Match a specific number of occurrence ex. [0-9]{3} match 342 but not 32
# {2,4} match length of 2, 3 and 4
# + : Match one or more occurrence of the previous char
# [] : Match any char inside
# ------------------------------------------------------------------------------------
ngx_http_limit_req_module 限制 HTTP 请求频率,采用漏桶算法。
ngx_http_limit_conn_module 限制 TCP 并发连接数。
两个模块都基于 IP 来限制访问频率。
ngx_http_limit_req_module 使用 limit_req_zone 和 limit_req 配合达到频率限制效果。一段时间内的,来自单个 IP 的 HTTP 请求如果超过指定数量,nginx 就返回 503 状态码 (通常会改为 429 Too Many Requests)。
ngx_http_limit_req_module 限制某段时间内同一 IP 访问频率:
http{
...
limit_req_zone $binary_remote_addr zone=httplimit:10m rate=20r/s;
limit_req_status 429;
...
server{
...
limit_req zone=httplimit burst=10 nodelay;
...
}
...
}
注意: 限速 20r/s 的意义是,每 50ms 只处理一个请求。即,假设 1s 内只有两个请求,而这两个请求都在 50ms 内到达,那么第二个到来的请求会被丢弃 (或者进入 burst 队列)。
给一个例子帮助理解。假设 1s 内只有 3 个请求,而这 3 个请求都在 50ms 内到达。那么,第一个到达的请求被正常处理,第二三个到达的请求进入 buster 队列,如果:
ngx_http_limit_conn_module 限制单个 IP 的 TCP 并发连接数:
http{
...
limit_conn_zone $binary_remote_addr zone=tcplimit:10m;
limit_conn_status 429;
...
server{
...
limit_conn tcplimit 40;
limit_rate 500K;
...
}
...
}
gzip 配置的常用参数:
给一个常用配置:
# gzip
gzip on;
gzip_buffers 16 8K;
gzip_comp_level 6;
gzip_min_length 100;
gzip_types *; # compress all MIME type files
gzip_disable "MSIE [1-6]\."; # ie6 and earlier version do not suport gzip
gzip_vary on;
注: 图片或者 mp3 这样的二进制文件的压缩率较小,耗费 CPU 资源较多,所以这类文件也可以不压缩。
需要注意: inactive 计时器走完,缓存会被删除,而 proxy_cache_valid 计时器走完后,缓存不被删除。
只要有请求出现,inactive 计时器就被刷新,重新开始计时。而不论有没有请求进入, proxy_cache_valid 计时器都不会被刷新,计时不会被打断。而一直没请求出现的话, inactive 和 proxy_cache_valid 的计时器都不会被刷新。
如果这两个配置项同时被启用,则会有如下的情况:
inactive 计时 1m,proxy_cache_valid 计时 1h,请求进来,cache 出现,各自的计时器启动:
inactive 计时 1m,proxy_cache_valid 计时 1m,请求进来,cache 出现,各自的计时器启动:
inactive 计时 1h,proxy_cache_valid 计时 1m,请求进来,cache 出现,各自的计时器启动:
根据上面的分析可以得出一个结论: 使用 proxy_cache_valid 的目的就在于设置一个强制刷新缓存的频率 。
在不配置 proxy_cache_valid 的情况下,如果某个缓存被频繁访问,那么就会导致 inactive 计时器不断被刷新,而 inactive 计时器不结束的话,nginx 就不会更新这个被频繁访问的缓存。如果被缓存的数据已经被更新了,由于 inactive 计时器一直没有结束,新数据无法进入缓存,那么更新后的数据无法被任何人访问到。
可以静态编译进 Nginx 二进制文件,也可以编译成库文件由 Nginx 挂载。
推荐把第三方模块编译成库文件,这样可以正常从包管理软件更新 Nginx 版本。
Brotli 是谷歌开源的比 gzip 更高效的压缩算法。
git clone https://github.com/google/ngx_brotli.git --recursive
从 Github 下载 ngx_brotli 源代码。
nginx -v
查看 Nginx 的版本,去官网下载对应的版本。解压到 ngx_brotli 同级的文件夹。
进入 Nginx 源码文件夹,执行:
./configure --with-compat --add-dynamic-module=../ngx_brotli
按照提示,安装上缺少的依赖。然后执行 make modules
开始编译库文件。
代码跑完后,Nginx 源码文件夹下会出现一个 objs 文件夹,里面有 ngx_http_brotli_static_module.so 和 ngx_http_brotli_filter_module.so 两个库文件。
此时需要注意: 如果使用 Debian 11 及以上系统,系统默认装了 libbrotli1 这个 shared library ,它包含了 ngx_http_brotli_filter_module 。Debian 10 及以下系统默认没装这个包。
所以 Debian 11 及以上版本系统只需要在 Nginx 配置文件里写明加载 /ngx_http_brotli_static_module.so/ 即可。Debian 10 及以下需要加载两个 .so 库文件。
如果系统是 FreeBSD,可以在 /usr/ports/www/nginx 使用 Ports 编译。编译后可以在 /usr/ports/www/nginx/work 文件夹中找到编译好的源码文件夹。
在编译好源码之后,把在源码包的 objs 文件夹中找到的以 .so 结尾的库文件复制到 Nginx 的动态库文件夹,再在配置文件里加载模块即可。
给一个常用配置:
# brotli
brotli on;
brotli_comp_level 6;
brotli_buffers 16 8k;
brotli_min_length 1k;
brotli_types *;