# Nginx学习笔记
### nginx目录结构
| 路径 | 说明 |
| :---------------------: | :------------------------: |
| `/var/log/nginx` | 存放`nginx`运行日志 |
| `/etc/nginx` | `nginx`配置所在文件夹 |
| `/etc/nginx/nginx.conf` | `nginx`主配置文件 |
| `/etc/nginx/conf.d` | 存放所有`server`块配置文件 |
### Nginx配置文件构成
一个Nginx配置文件通常包含3个模块:
- 全局块:比如工作进程数,定义日志路径;
- Events块:设置处理轮询事件模型,每个工作进程最大连接数及http层的keep-alive超时时间;
- http块:路由匹配、静态文件服务器、反向代理、负载均衡等。
其中http块又可以进一步分成3块,http全局块里的配置对所有站点生效,server块配置仅对单个站点生效,而location块的配置仅对单个页面或url生效。
```nginx
# 全局块
...
# events块
events {
...
}
# http块
http
{
# http全局块
...
# 虚拟主机server块
server
{
# server全局块
...
# location块
location [PATTERN]
{
...
}
location [PATTERN]
{
...
}
}
server
{
...
}
# http全局块
...
}
```
### nginx默认配置
nginx包括一个主配置文件`nginx.conf`和多个`server`块配置(存放于`/etc/nginx/conf.d/`)
查看去掉注释后的`nginx.conf`文件,grep去除了带 `#` 的行和 `^$` (即空白行)
```sh
cat /etc/nginx/nginx.conf | grep -vE "#|^$"
```
`nginx`主配置文件:`nginx.conf`
```nginx
user www-data; # 启动nginx的用户
worker_processes auto; # 工作进程数,建议配置为CPU核心数
pid /run/nginx.pid; # 设置记录主进程ID的文件
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768; # 定义工作进程最大连接数
}
http {
##
# Basic Settings
##
sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65; # 设置超时时间(秒)
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型
##
# 设置ssl
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
# 请求日志和错误日志
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on; #开启gzip压缩输出
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
```
注意到倒数第二行和倒数第三行的`include`,它表示将`/etc/nginx/conf.d/`目录下以.conf结尾的文件和目录/etc/nginx/site-enabled/下的所有文件直接包含进来。
你可以理解为将文件的内容直接复制到这里(即`/etc/nginx/nginx.conf`中)。
> 为了方便维护我们`server`相关配置,不会让某一个配置文件过于庞大。通常是将所有的虚拟主机配置文件(也就是`server`配置块的内容)存放在 `/etc/nginx/conf.d/` 或者` /etc/nginx/sites-enabled/` 目录中,在主配置文件中已经默认声明了会读取这两个文件夹下所有 `*.conf` 文件。
根据使用习惯,我一般会在`/etc/nginx/conf.d/`建立配置文件来配置server块的内容
首先将默认的配置文件拷贝到`/etc/nginx/conf.d/`中
```sh
cp /etc/nginx/sites-enabled/default /etc/nginx/conf.d/default.conf
```
查看默认的`server`块配置:
```sh
cat /etc/nginx/conf.d/default.conf | grep -vE "#|^$"
```
默认配置如下:
```nginx
server {
# 侦听 80 端口,分别配置了 IPv4 和 IPv6
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
# 定义服务器的默认网站根目录位置
root /usr/share/nginx/html;
# 定义主页的文件名
index index.html index.htm;
# 定义虚拟服务器的名称
server_name localhost;
# location 块
location / {
try_files $uri $uri/ =404;
}
}
```
### nginx监听多端口
1.首先在文件`nginx.conf`的`http { ... }`块中加`#`注释掉`include /etc/nginx/sites-enabled/*;`
2.复制nginx默认的server块配置到`/etc/nginx/conf.d`文件夹中
```sh
cp /etc/nginx/sites-enabled/default /etc/nginx/conf.d/default.conf
```
3.新建配置文件,监听80之外的端口
新建一个配置文件`myweb.conf`,新建文件夹`/var/myweb`,里面创建一个文件`index.html`
根目录为`/var/myweb`,监听端口为`6543`,根目录和端口号可以自定义
```nginx
server {
listen 6543 default_server;
listen [::]:6543 default_server;
root /var/myweb;
index index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
```
4.重载`nginx`
```sh
nginx -t # 检查nginx配置文件是否正确,ok表示配置文件语法正确
nginx -s reload # 重载nginx
```
### location规则
| 匹配符 | 匹配规则 | 优先级 |
| :----: | :----------------------------------------------------: | :----: |
| `=` | 精确匹配 | 1 |
| `^~` | 前缀匹配,以某个字符串开头 | 2 |
| `~` | 区分大小写的正则匹配 | 3 |
| `~*` | 不区分大小写的正则匹配 | 4 |
| `!~` | 区分大小写的不匹配正则 | 5 |
| `!~*` | 不区分大小写的不匹配正则 | 6 |
| `/uri` | 返回最长匹配的`location`,与`location`所在位置顺序无关 | 7 |
| `/` | 通用匹配,任何请求都会匹配到 | 8 |
规则:**先精确匹配,没有则查找带有 ^~的前缀匹配,没有则进行正则匹配,最后才返回前缀匹配的结果(如果有的话)**
如果优先级相同的话,按照匹配程序选择,匹配程度越高越优先;如果匹配程度相同,则按照nginx中的配置文件中的先后顺序进行选择。
### 正向代理和反向代理
{{}}
> **正向代理**(forward proxy):是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。
**客户端必须设置正向代理服务器,当然前提是要知道正向代理服务器的IP地址,还有代理程序的端口。**
**正向代理的用途:**
1. 访问原来无法访问的资源,如某些404网站
2. 可以做缓存,加速访问资源
3. 对客户端访问授权,上网进行认证
4. 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
> 反向代理(reverse proxy):实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
**反向代理的用途:**
1. 保证内网的安全,可以使用反向代理提供WAF功能,阻止web攻击
2. 负载均衡,通过反向代理服务器来优化网站的负载
### nginx使用反向代理
后端服务的接口设置为`8080`,`nginx`监听的端口为`80`
#### 使用proxy_pass进行请求转发
{{< admonition type="tip" title="提示">}}
location后面的路径最好以`/`结尾,不然会有奇奇怪怪的错误!
{{< /admonition>}}
```nginx
location / {
proxy_pass http://localhost:8080;
}
```
- 如果`proxy-pass`的地址只配置到端口,不包含`/`或其他路径,那么`location`将被追加到转发地址中
```nginx
location /path/ {
proxy_pass http://localhost:8080;
}
```
例如访问 `http://localhost/path/page.html` 将被代理到 `http://localhost:8080/path/page.html`
- 如果`proxy-pass`的地址包括`/`或其他路径,那么`location`不会追加到转发地址中
```nginx
location /path/ {
proxy_pass http://localhost:8080/zh-cn/;
}
```
例如访问 `http://localhost/path/page.html` 将被代理到 `http://localhost:8080/zh-cn/page.html`
> 更多例子请访问[这个链接](https://www.cnblogs.com/bigberg/p/7651197.html) !!!
#### 设置代理请求headers
用户可以重新定义或追加`header`信息传递给后端服务器。可以包含文本、变量及其组合。默认情况下,仅重定义两个字段:
```nginx
proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
```
由于使用反向代理之后,后端服务无法获取用户的真实IP,所以,一般反向代理都会设置以下header信息。
```nginx
location /path/ {
proxy_set_header Host $host; # 保留代理之前的host
proxy_set_header X-Real-IP $remote_addr; # 保留代理之前的真实客户端ip
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 在多级代理的情况下,记录每次代理之前的客户端真实ip
proxy_pass http://localhost:8080;
}
```
### 负载均衡
使用 `upstream`定义一组服务 。
> upstream 位于 http上下文中,与server 并列,不要放在server中
1. 轮询
轮询可用的服务器进行转发
```nginx
upstream index {
server 127.0.0.1:6002;
server 127.0.0.1:6001;
}
server {
listen 6500 default_server;
listen [::]:6500 default_server;
root /home/zfp/myweb;
index index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ =404;
proxy_pass http://index;
}
}
```
2. 最小连接数
将下一个请求分配给活动连接数最少的服务器(较为空闲的服务器)
```nginx
upstream index {
least_conn;
server 127.0.0.1:6002;
server 127.0.0.1:6001;
}
```
> 请注意,使用轮循机制或最少连接的负载平衡,每个客户端的请求都可能分发到不同的服务器。不能保证同一客户端将**始终**定向到同一服务器。
3. ip-hash
```nginx
upstream index {
ip_hash;
server 127.0.0.1:6002;
server 127.0.0.1:6001;
}
```
>此方法可确保来自同一客户端的请求将始终定向到同一服务器,除非此服务器不可用。
4. 随机
每个请求都将传递到随机选择的服务器。
`two`是可选参数,`nginx`在考虑服务器权重的情况下随机选择两台服务器,然后使用指定的方法选择其中一台,默认为选择连接数最少(`least_conn`)的服务器。
```nginx
upstream index {
random two least_conn;
server 127.0.0.1:6002;
server 127.0.0.1:6001;
}
```
5. 权重
每接收到4个请求,3个转发到`http://localhost:6002`,1个转发到`http://localhost:6001`
```nginx
upstream index {
server 127.0.0.1:6002 weight=3;
server 127.0.0.1:6001 weight=1;
}
```
> 在反向代理中,如果后端服务器在某个周期内响应失败次数超过规定值,nginx会将此服务器标记为失败,并在之后的一个周期不再将请求发送给这台服务器。
通过`fail_timeout`来设置检查周期,默认为10秒。
通过`max_fails`来设置检查失败次数,默认为1次。
在以下示例中,如果nginx无法向服务器发送请求或在30秒内请求失败次数超过3次,则会将服务器标记为不可用30秒。
```nginx
upstream index {
server 127.0.0.1:6002;
server 127.0.0.1:6001 max_fails=3 fail_timeout=30s;
}
```
### https配置
**使用openssl生成证书**
```sh
openssl genrsa -des3 -out server.key 2048
openssl req -new -key server.key -out server.csr
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
```
**nginx配置**
如果证书设置了密码需要将密码保存到文件中,并通过下面的语句访问
```nginx
ssl_password_file /etc/nginx/conf.d/server.pass;
```
详细配置如下:
```nginx
upstream index{
server 127.0.0.1:6002 weight=3;
server 127.0.0.1:6001 weight=1;
}
server {
# 启用 https
listen 6500 default_server ssl http2;
root /home/zfp/myweb;
index index.html index.htm;
# 后面跟上证书的key和crt文件
ssl_certificate /etc/nginx/conf.d/server.crt;
ssl_certificate_key /etc/nginx/conf.d/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
# 证书如果设置了密码需要添加下面这一行
ssl_password_file /etc/nginx/conf.d/server.pass;
# 设置ssl会话缓存
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
server_name _;
location / {
try_files $uri $uri/ =404;
proxy_pass http://index;
}
location /nginx_status {
stub_status on;
}
}
```
### tcp反向代理
注意: `stream`块和`http`块处于同一级,所以设置tcp反向代理需要在`nginx.conf`中操作
在`nginx.conf`中添加`mysql`的反向代理
```nginx
#HTTP代理
http {
...
}
#TCP代理
stream {
server {
listen 13306;
proxy_pass localhost:3306;
}
}
```
{{}}
**负载均衡**
```nginx
stream {
upstream backend-mysql {
server localhost:3306;
server localhost:3307;
keepalive 8;
}
server {
listen 13306;
proxy_pass backend-mysql;
}
}
```
使用`keepalive`定义连接池里空闲连接的数量。
`keepalive_timeout` 默认`60s`。如果连接池里的连接空闲时间超过这个值,则连接关闭。
### 重写
#### return
服务端停止处理并将状态码`status code`返回给客户端
- `return code URL`
- `return code text`
- `return code`
- `return URL`
`status code`类型
#### 状态码
- 2xx 成功
- 3xx 表示重定向
- - 301 永久重定向
- 302 临时重定向
- 4xx 请求地址出错
- - 403 拒绝请求
- 404 请求找不到
- 5xx 服务器内部错误
下面是一个临时将请求链接重定向到其他端口的例子
```nginx
server {
listen 6004 default_server;
listen [::]:6004 default_server;
root /home/zfp/myweb;
index index.html index.htm;
server_name _;
# 临时重定向
# 这里需要写真实ip,不能用localhost,重定向不是反向代理
# 将http://192.168.153.131:6004/重定向到https://192.168.153.131:6500/
return 302 https://192.168.153.131:6500;
location / {
try_files $uri $uri/ =404;
}
location /nginx_status {
stub_status on;
}
}
```
域名变更,将老域名的请求永久重定向到新域名
```nginx
server {
listen 80;
listen 443 ssl;
server_name www.old-name.com old-name.com;
return 301 $scheme://www.new-name.com;
}
```
请求的地址前加上`www`
```nginx
server {
listen 80;
listen 443 ssl;
server_name domain.com;
return 301 $scheme://www.domain.com$request_uri;
}
```
#### rewrite
如果指定的正则表达式与请求`URI`匹配,则`URI`将按照字符串中的指定进行更改。指令按其在配置文件中出现的先后顺序执行。
```nginx
server {
# ...
rewrite ^(/download/.*)/media/(\w+)\.?.*$ $1/mp3/$2.mp3 last;
rewrite ^(/download/.*)/audio/(\w+)\.?.*$ $1/mp3/$2.ra last;
return 403;
# ...
}
```
上面是使用该指令的示例 `nginx`重写规则。它匹配以字符 **/download** 开头的 URL,然后在路径后面的某个位置包含 **/media**/ 或 **/audio/** 目录。它会将目录替换为 **/mp3/**,并添加相应的文件扩展名 **.mp3** 或 **.ra**。
例如,**/download/cdn-west/media/file1** 变成了 **/download/cdn-west/mp3/file1.mp3**。
使用重写规则建议打开重写日志
```nginx
rewrite_log on;
```
### last和break
规则:
- `last`:如果当前规则不匹配,停止处理后续`rewrite`规则,使用重写后的路径,重新搜索`location`及其块内指令
- `break`:如果当前规则不匹配,停止处理后续`rewrite`规则,执行`{}`块内其他指令
在`/home/AdminLTE-3.2.0/pages`下创建一个`1.txt`,里面内容是`this is a file`
假设请求地址为`http://192.168.153.131:8000/old/1.txt`
> 例1:不适用last和break
```nginx
server {
listen 8000;
server_name _;
rewrite_log on;
location / {
rewrite ^/old/(.*) /new/$1;
rewrite ^/new/(.*) /pages/$1;
#根目录
root /home/AdminLTE-3.2.0;
#首页
index index.html index2.html index3.html;
}
location /pages/1.txt {
return 200 "this is rewrite test!";
}
}
```
{{< admonition type="info" title="说明">}}
会依次执行两条rewrite语句,请求变为`http://192.168.153.131:8000/pages/1.txt`,最后输出`this is rewrite test!`
{{< /admonition>}}
> 例2:使用break
```nginx
server {
listen 8000;
server_name _;
rewrite_log on;
location / {
rewrite ^/old/(.*) /new/$1 break;
rewrite ^/new/(.*) /pages/$1;
#根目录
root /home/AdminLTE-3.2.0;
#首页
index index.html index2.html index3.html;
}
location /pages/1.txt {
return 200 "this is rewrite test!";
}
}
```
{{< admonition type="info" title="说明">}}
执行完第一条`rewrite`语句后地址变为`http://192.168.153.131:8000/new/1.txt`,阻止执行第二条`rewrite`语句,以新路径`/new/1.txt`去匹配`location`, 先匹配到`location /`, 有匹配到`location`里的`rewrite ^/new/(.*) /pages/$1`规则,重定向到`/pages/1.txt`匹配到了`location /pages/1.txt` ,最终返回了`this is rewrite test!`
{{< /admonition>}}
> 例3:使用last
```nginx
server {
listen 8000;
server_name nginx-dev;
rewrite_log on;
location / {
rewrite ^/old/(.*) /new/$1 last;
rewrite ^/new/(.*) /pages/$1;
#根目录
root /home/AdminLTE-3.2.0;
#首页
index index.html index2.html index3.html;
}
location /pages/1.txt {
return 200 "this is rewrite test!";
}
}
```
{{< admonition type="info" title="说明">}}
执行完第一条后地址变为`http://192.168.153.131:8000/new/1.txt`,阻止执行第二条rewrite语句,以新路径`/new/1.txt`执行`location /`下剩余指令,没有发现`/home/AdminLTE-3.2.0/new/1.txt`文件,返回`404 not found`
{{< /admonition>}}
### 其他一些指令
**1. gzip压缩**
默认情况下,`nginx`仅使用压缩`MIME`类型是`text/html`的响应。若要使用其他`MIME`类型压缩响应,可以使用`gzip_types`指令并列出其他类型。
```nginx
gzip on; # 打开gzip压缩
gzip_types text/plain application/xml; # 添加压缩文件类型
gzip_min_length 1000; # 指定压缩响应的最小长度,
```
**2. sendfile**
`sendfile`指令通常用于上传和下载文件的请求中。
默认情况下,`nginx`处理文件传输本身,并在发送之前将文件复制到缓冲区中。启用`sendfile`指令可消除将数据复制到缓冲区的步骤,直接将一个文件复制到另一个文件。
将`sendfile`指令与`tcp_nopush`指令一起使用。这使`nginx`能够在获得数据块后立即在一个数据包中发送`HTTP`响应标头。
```nginx
location /download {
sendfile on;
tcp_nopush on;
...
}
```
**3. try_files**
`try_files`指令可用于检查指定的文件或目录是否存在;如果不存在,则重定向到指定位置。
如下,如果原始`URI`对应的文件不存在,`nginx`将内部重定向到`/home/zfp/myweb/1.txt`
```nginx
server {
listen 6002 default_server;
listen [::]:6002 default_server;
root /home/zfp/myweb;
index index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ /1.txt;
}
location /nginx_status {
stub_status on;
}
}
```
最后一个参数也可以为状态码
在下面的示例中,如果指令的所有参数都无法解析为现有文件或目录,则会返回404错误。
```nginx
try_files $uri $uri/ =404;
```
**4. error_page**
为错误指定显示的页面。值可以包含变量。
```nginx
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
```
### nginx小技巧
{{< admonition >}}
本节主要介绍一些使用nginx的技巧,不介绍具体语法
{{< /admonition>}}
#### 1.http和https共用一个端口
{{< admonition type="question" title="场景">}}
假设用户原来的请求为http://localhost:6501,现在升级到了https,希望使用原请求能跳转到https://localhost:6501
{{< /admonition>}}
**方案1:**
使用错误页将`http`请求重定向为`https`请求,这样就解决了原先的http请求无缝切换为https请求
```nginx
error_page 497 https://$http_host$request_uri;
```
**方案2:**
>之前是6501端口监听http请求,6500端口监听https请求
>
>现在是6501监听到http请求将强制使用https,不需要用户开放6500端口来处理https请求
在使用`TCP(stream)`代理转发流量时,可以使用`ssl_preread_protocol`变量区分SSL/TLS和其他协议。
`nginx.conf`中添加下列配置:
```nginx
stream {
upstream web {
server 192.168.153.131:6502; # 多使用一个端口来处理http请求
}
upstream https {
server 192.168.153.131:6500;
}
log_format basic 'ssl_version: $ssl_preread_protocol | upstream: $upstream';
access_log /var/log/nginx/nginx-access.log basic ;
# 根据不同的访问协议转发给不同的上游
map $ssl_preread_protocol $upstream {
"" web;
"TLSv1.3" https;
default https;
}
# HTTPS and HTTP on the same port
server {
listen 6501; # 监听6501端口
proxy_pass $upstream;
ssl_preread on; # 开启ssl_preread功能
}
}
```
监听6501端口,根据不同请求进行转发:
`http`请求转发到`http://192.168.153.131:6502`, `https`请求转发到`https://192.168.153.131:6500`
修改原来的`server`配置
```nginx
server {
listen 6502;
server_name localhost;
return 301 https://192.168.153.131:6501;
}
server {
# listen 6501 default_server;
listen 6500 default_server ssl http2;
root /home/zfp/myweb;
index index.html index.htm;
# error_page 497 https://$http_host$request_uri;
ssl_certificate /etc/nginx/conf.d/server.crt;
ssl_certificate_key /etc/nginx/conf.d/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_password_file /etc/nginx/conf.d/server.pass;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
```
{{< admonition type="quote" title="参考文档">}}
[1.正向代理与反向代理【总结】](https://www.cnblogs.com/anker/p/6056540.html)
[2.nginx一小时入门教程](https://www.yuque.com/wukong-zorrm/cql6cz)
[3.Nginx的安装及配置详解](https://pythondjango.cn/python/tools/5-nginx-configuration/)
{{< /admonition>}}