Nginx 反向代理

Nginx 反向代理

引言

Nginx 如何变成一个代理服务器?Nginx 又如何将客户端的请求转发给其他的服务器?本内容将学习 Nginx 的反向代理知识。


反向代理概述

关于正向代理和反向代理,在 Nginx - 介绍 已经通过一张图详细的介绍过了,简而言之就是正向代理代理的对象是客户端,反向代理代理的是服务端,这是两者之间最大的区别。

Nginx 即可以实现正向代理,也可以实现反向代理。

我们先来通过一个小案例演示下 Nginx 正向代理的简单应用。

正向代理

先提需求:

image

  1. 服务端的设置:
1
2
3
4
5
6
7
8
9
10
11
12
http {
log_format main 'client send request=>clientIp=$remote_addr serverIp=$host';
server{
listen 80;
server_name localhost;
access_log logs/access.log main;
location / {
root html;
index index.html index.htm;
}
}
}
  1. 使用客户端访问服务端:http://192.168.200.133,打开日志查看结果

image

  1. 代理服务器设置:
1
2
3
4
5
6
7
server {
listen 82;
resolver 8.8.8.8; # 设置 DNS 的 IP,用来解析 proxy_pass 中的域名
location / {
proxy_pass http://$host$request_uri; # proxy_pass 实现正向代理
}
}

proxy_pass 后面有讲解。

  1. 客户端配置代理服务器的 IP(192.168.200.146)和 Nginx 配置监听的端口(82)

image

  1. 设置完成后,再次通过浏览器访问服务端

image

通过对比,上下两次的日志记录,会发现虽然我们是客户端访问服务端,但是使用了代理,那么服务端能看到的只是代理发送过去的请求,这样就使用 Nginx 实现了正向代理的设置。

但是 Nginx 正向代理,在实际的应用中不是特别多,所以我们简单了解下,接下来我们继续学习 Nginx 的反向代理,这是 Nginx 比较重要的一个功能。

反向代理语法配置

Nginx 反向代理模块的指令是由 ngx_http_proxy_module 模块进行解析,该模块在安装 Nginx 的时候已经自动加载到 Nginx 中了,接下来我们把反向代理中的常用指令一一介绍下:

  • proxy_pass:配置代理的服务器地址
  • proxy_set_header:转发给被代理服务器时,设置一些请求头信息
  • proxy_redirect:防止客户端可以看到被代理服务器的地址

这里只介绍三个指令,关于反向代理的指令非常多,想要了解更多,请前往 Nginx 反向代理文档

proxy_pass

该指令用来设置被代理服务器地址,可以是主机名称、IP 地址加端口号形式,没有默认值。

语法 默认值 位置
proxy_pass <URL>; location

URL:为要设置的被代理服务器地址,包含传输协议(httphttps://)、主机名称或 IP 地址加端口号、URI 等要素。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
proxy_pass http://www.baidu.com;

# 例子
location /server {
# 结尾不加斜杠
proxy_pass http://192.168.200.146;
# 访问的是:http://192.168.200.146/server/index.html

# 结尾加斜杠
proxy_pass http://192.168.200.146/;
# 访问的是:http://192.168.200.146/index.html
}

实例

准备两台服务器或者按照 Linux 系统的虚拟机,这里是 192.168.200.133192.168.200.146,为了方便,我们称前者为服务器 A,后者为服务器 B。

  1. 在服务器 A 的 Nginx 配置文件添加如下内容:
1
2
3
4
5
6
7
8
9
10
11
http {
# ......
server {
listen 8080;
server_name localhost;
location / {
proxy_pass http://192.168.200.146;
}
}
# ......
}

当客户端请求服务器 A http://192.168.200.133,它会转发给服务器 B,此时的服务器 A 就是一个代理的角色。

访问服务器 A,我们看到 Nginx 的欢迎界面其实是服务器 B 的 Nginx,可以在服务器 B 的 Nginx 欢迎页面添加新的内容:I am 146,再次访问服务器 A,效果如图:

image

总结来说:

  • 正向代理相当于在客户端浏览器里直接访问目标域名,但是背地里请求会从配置好的代理服务器发出去,对服务端来说它只知道代理服务器请求了提出了这次请求。
  • 反向代理时相当于在客户端浏览器里直接访问代理服务器,然后代理服务器根据内部配置好的规则选择相应的服务返回给客户端,此时客户端并不清楚毒品的源头是谁,只知道它是从proxy那拿了货。

在编写 proxy_pass 的时候,后面的值要不要加 /?

这是看情况的。

接下来通过例子来说明提到的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 80;
server_name localhost;
location / {
# 下面两个地址加不加斜杠,效果都一样,因为 location 后的 / 会添加在代理地址后面
proxy_pass http://192.168.200.146;
proxy_pass http://192.168.200.146/;
}
}

server{
listen 80;
server_name localhost;
location /server {
# 下面两个地址必须加斜杠,因为 location 后的 /server 会添加在代理地址后面,第一个将没有 / 结尾
#proxy_pass http://192.168.200.146;
proxy_pass http://192.168.200.146/;
}
}
# 上面的 location:当客户端访问 http://localhost/server/index.html
# 第一个 proxy_pass 就变成了 http://localhost/server/index.html
# 第二个 proxy_pass 就变成了 http://localhost/index.html 效果就不一样了。
  • 第一个 location(第 4 行代码):当客户端访问 http://localhost/index.html,两个 proxy_pass 效果是一样的,因为 location 后的 / 会添加在代理地址后面,所以有没有 /,效果都一样。

  • 第一个 location(第 14 行代码):当客户端访问 http://localhost/server/index.html,这个时候,第一个 proxy_pass 就变成了 http://192.168.200.146/server/index.html,第二个 proxy_pass 就变成了 http://192.168.200.146/index.html 效果就不一样了

    如果不以 / 结尾,则 location 后的 /server 会添加在地址后面,所以第一个 proxy_pass 因为没有 / 结尾而被加上 /server,而第二个自带了 / ,所以不会添加 /server

上面的例子仅仅针对:访问任意请求如 /server 时,想要代理到其他服务器的首页,则加 /,否则你如果真的想访问 /server 下的资源,那么不要加 /

所以加了 / 后,请求的是服务器根目录下的资源。

proxy_set_header

该指令可以更改 Nginx 服务器接收到的客户端请求的请求头信息,然后将新的请求头发送给代理的服务器。默认值是发送代理服务器的地址和 close。

image

语法 默认值 位置
proxy_set_header <field> <value>; proxy_set_header Host $proxy_host; proxy_set_header Connection close; http、server、location

需要注意的是,如果想要看到结果,必须在被代理的服务器上来获取添加的头信息。

实例

被代理服务器:服务器 B 192.168.200.146 的 Nginx 配置文件内容:

1
2
3
4
5
6
server {
listen 8080;
server_name localhost;
default_type text/plain;
return 200 $http_username; # 获取代理服务器发送过来的 http 请求头的 username 值
}

代理服务器: 服务器 A 192.168.200.133 的 Nginx 配置文件内容:

1
2
3
4
5
6
7
8
server {
listen 8080;
server_name localhost;
location /server { # 访问 /server 触发代理
proxy_pass http://192.168.200.146:8080/; # 配置服务器 B 的地址
proxy_set_header username TOM; # 发送 key 为 username,value 为 TOM 的请求头给服务器 B
}
}

访问测试

image

客户端访问的是服务器 A,服务器 A 会将请求转发给服务器 B,服务器 B 返回打印 TOM 的页面给服务器 A,服务器 A 最后返回给客户端。

proxy_redirect

该指令是用来重置头信息中的『 Location 』和『 Refresh 』的值,防止客户端可以看到被代理服务器的地址。

因为客户端看到的返回结果是『 Location 』和『 Refresh 』的值,所以在到达代理服务器的时,将两个值修改掉,防止客户端直接看到被代理服务器的地址。

语法 默认值 位置
proxy_redirect redirect replacement;
proxy_redirect default;
proxy_redirect off;
proxy_redirect default; http、server、location

为什么要用该指令?

首先说明一下思路:客户端通过代理服务器 A 访问服务器 B 的资源,但是服务器 B 不存在该资源,则会报错。此时我们不希望它直接返回报错页面给客户端,我们希望服务器 B 返回的是它的欢迎页面。那么如何做呢?

  • 首先在服务器 B 进行判断是否存在资源,不存在则返回自己的欢迎页面,即重定向到自己的欢迎页面地址并返回,此时浏览器的地址将会发生改变
  • 代理服务器 A 收到服务器 B 的欢迎页面和地址,但是我们不能直接返回给客户端,因为它会暴露服务器 B 的地址,这是重定向的原因

image

  • 此时用到 proxy_redirect 指令,重置服务器 B 返回过来的『 Location 』和『 Refresh 』值,将两个值改为代理服务器 A 的某个地址
  • 因为改为了代理服务器 A 的某个地址,所以代理服务器 A 根据这个地址又去获取理服务器 B 的欢迎页面地址,返回给客户端

很绕,简单总结下:客户端通过 A 找 B 不存在的资源,B 不想返回报错页面,于是重定向到自己的欢迎页面地址并返回给 A,A 收到了页面和地址(正常情况不要接收地址,只接收页面),发现不能暴露 B 的地址,于是修改接收的 B 的地址为自己的某一个地址,这个地址会重新发送请求去获取 B 的欢迎页面地址,然后返回给客户端。

这里要明白 B 返回的是重定向后的欢迎页面,重定向后,浏览器地址栏会变成重定向的地址,所以 A 才会以自己的地址转发获取到 B 的地址,所以最后浏览器显示 A 的地址,看到的却是 B 的欢迎页面。

代码:

服务端 B 192.168.200.146

1
2
3
4
5
6
7
server {
listen 8081;
server_name localhost;
if (!-f $request_filename){
return 302 http://192.168.200.146; # 2.如果请求的资源不存在,则重定向到服务器 B
}
}

代理服务端 A 192.168.200.133

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 8081;
server_name localhost;
location / {
proxy_pass http://192.168.200.146:8081/; # 1.转发给服务器 B
proxy_redirect http://192.168.200.146 http://192.168.200.133; # 3.修改服务器 B 的地址
}
}
# 该 server 去请求服务器 B 的欢迎页面
server {
listen 80;
server_name 192.168.200.133;
location / {
proxy_pass http://192.168.200.146; # 4.重新发送请求给服务器 B,获取欢迎页面
}
}

第 6 行代码,当服务器 B 返回的是 http://192.168.200.146,为了不让它出现在浏览器的地址栏上,我们需要利用 proxy_redirect 将它修改为代理服务器 A 的地址,这个地址会以自己的地址重新访问服务器 B 的欢迎页面,最后返回给客户端。

该指令的三组选项

  • proxy_redirect redirect replacement;

    • redirect:被代理服务器返回的 Location 值
    • replacement:要替换 Location 的值
  • proxy_redirect default;

    • default:相比较第一组选项,default 仅仅提供了 redirectreplacement 的默认值

      将本范围 location 块的 uri 变量作为 replacement。

      将 proxy_pass 变量作为 redwadairect

1
2
3
4
5
6
7
8
9
10
server {
listen 8081;
server_name localhost;
location /server {
proxy_pass http://192.168.200.146:8081/;
proxy_redirect default; # redirect 是 proxy_pass 的值:http://192.168.200.146:8081/
# replacement 是 location 后的值:/server
# 等价于:proxy_redirect http://192.168.200.146:8081/ /server
}
}
  • proxy_redirect off;

    关闭 proxy_redirect 的功能

反向代理实战

image

服务器 1,2,3 存在两种情况

  • 第一种情况: 三台服务器的内容不一样
  • 第二种情况: 三台服务器的内容是一样

第一种情况

  • 如果服务器 1、服务器 2 和服务器 3 的内容不一样,那我们可以根据用户请求来分发到不同的服务器。

    服务器有限,只能以三个端口模拟三台服务器,实际上是一个 IP 对应一个服务器。

    代理服务器配置文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 代理服务器
server {
listen 8082;
server_name localhost;
location /server1 {
proxy_pass http://192.168.200.146:9001/; # 代理 server1
}
location /server2 {
proxy_pass http://192.168.200.146:9002/; # 代理 server2
}
location /server3 {
proxy_pass http://192.168.200.146:9003/; # 代理 server3
}
}

服务器配置文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 服务器
# server1
server {
listen 9001;
server_name localhost;
default_type text/html;
return 200 '<h1>192.168.200.146:9001</h1>'
}
# server2
server {
listen 9002;
server_name localhost;
default_type text/html;
return 200 '<h1>192.168.200.146:9002</h1>'
}
# server3
server {
listen 9003;
server_name localhost;
default_type text/html;
return 200 '<h1>192.168.200.146:9003</h1>'
}
  • 如果服务器 1、服务器 2 和服务器 3 的内容是一样的,该如何处理?

    请看负载均衡的相关内容,里面将进行详细的介绍。

斜杠总结

这里将发送 http://192.168.199.27/frx/xu 请求。

不带字符串情况

案例 localtion proxy_pass 匹配
1 /frx http://192.168.199.27 /frx/xu
2 /frx/ http://192.168.199.27 /frx/xu
3 /frx http://192.168.199.27/ //xu
4 /frx/ http://192.168.199.27/ /xu

若 Nginx 会将原请求路径原封不动地转交给其他地址,如案例 3 和 4。

proxy_pass 的 ip:port 后加了 /,代表去除掉请求和 location 的匹配的字符串,不加则追加全部请求到地址后面。

带字符串情况

案例 localtion proxy_pass 匹配
1 /frx http://192.168.199.27/bing /bing/xu
2 /frx/ http://192.168.199.27/bing /bingxu
3 /frx http://192.168.199.27/bing/ /bing//xu
4 /frx/ http://192.168.199.27/bing/ /bing/xu

proxy_pass 的 ip:port 后加了字符串,Nginx 会将匹配 location 的请求从「原请求路径」中剔除,再不匹配的字符串拼接到 proxy_pass 后生成「新请求路径」,然后将「新请求路径」转交给其他地址。

案例 2 中,proxy_pass 的 ip:port 后接了字符串,因此将 location 的 /frx/ 从原请求路径 /frx/xu 中剔除,变为 xu,然后将 xu 拼接到 http://192.168.1.48/bing 后生成了新请求,因此其他地址收到的请求就是 /bingxu

Nginx安全控制

关于 Web 服务器的安全是比较大的一个话题,里面所涉及的内容很多,Nginx 反向代理是如何来提升 Web 服务器的安全呢?

答案是:安全隔离。

什么是安全隔离

通过代理分开了客户端到应用程序服务器端的连接,实现了安全措施。在反向代理之前设置防火墙,仅留一个入口供代理服务器访问。

image

如何使用SSL对流量加密

翻译成大家能熟悉的说法就是将我们常用的 http 请求转变成 https 请求,那么这两个之间的区别简单的来说两个都是 HTTP 协议,只不过 https 是身披 SSL 外壳的 http.

HTTPS 是一种通过计算机网络进行安全通信的传输协议。它经由 HTTP 进行通信,利用 SSL/TLS 建立全通信,加密数据包,确保数据的安全性。

  • SSL(Secure Sockets Layer)安全套接层
  • TLS(Transport Layer Security)传输层安全

上述这两个是为网络通信提供安全及数据完整性的一种安全协议,TLS 和 SSL 在传输层和应用层对网络连接进行加密。

总结来说为什么要使用 https:

  • http 协议是明文传输数据,存在安全问题,而 https 是加密传输,相当于 http + SSL,并且可以防止流量劫持。

Nginx 默认不支持 https 开头的协议,如果要想使用 SSL,需要满足一个条件即需要添加一个模块 --with-http_ssl_module,而该模块在编译的过程中又需要 OpenSSL 的支持,OpenSSL 我们在 Nginx 安装 已经准备好了。

添加SSL支持

完成 --with-http_ssl_module 模块的增量添加。

  1. 将原有 /usr/local/nginx/sbin/nginx 进行备份
1
2
cd /usr/local/nginx/sbin
mv nginx nginx.backup
  1. 查看 configure arguments 的配置信息,拷贝出来
1
2
3
nginx -V

# 拷贝 configure arguments 后面的数据
  1. 进入 Nginx 的安装目录,执行 make clean 清空之前编译的内容
1
2
3
cd /root/nginx/core/nginx-1.20.2

make clean
  1. 使用 configure 来配置参数,添加 ngx_http_ssl_module 模块,记得加上第(2)步拷贝的配置信息
1
./configure --with-http_ssl_module # 记得添加 configure arguments 后的数据
  1. 通过 make 模板进行编译
1
make
  1. 将 objs 下面的 nginx 可执行文件移动到 /usr/local/nginx/sbin
1
mv /opt/nginx/core/nginx-1.20.2/objs/nginx /usr/local/nginx/sbin
  1. 在源码目录(安装包目录)下执行 make upgrade 进行升级,这个可以实现不停机添加新模块的功能
1
2
cd /opt/nginx/core/nginx-1.20.2
make upgrade

SSL相关指令

该模块的指令都是通过 ngx_http_ssl_module 模块来解析的,我们上一步已经添加了。

这里只介绍常用的几个指令,了解更多指令请前往 ngx_http_ssl_module 模块文档

ssl 指令用来在指定的服务器开启 HTTPS,默认关闭。可以使用 listen 443 ssl,这种方式更通用些。

语法 默认值 位置
ssl <on |off>; ssl off; http、server

ssl 默认监听的是 443 端口,所以使用下面的指令和 ssl on 效果一致,因为下面的指令能突出 sll 的监听端口,所以建议使用它。

1
2
3
server{
listen 443 ssl;
}

ssl_certificate 指令是为当前这个虚拟主机指定一个带有 PEM 格式证书的证书。

语法 默认值 位置
ssl_certificate <file>; http、server

ssl_certificate_key 指令用来指定 PEM secret key 文件的路径

语法 默认值 位置
ssl_ceritificate_key <file>; http、server

ssl_session_cache 指令用来配置用于 SSL 会话的缓存

语法 默认值 位置
ssl_sesion_cache <off | none | [builtin[:size]] [shared:name:size]> ssl_session_cache none; http、server

选项介绍:

  • off:严格禁止使用会话缓存:Nginx 明确告诉客户端会话不能被重用

  • none:禁止使用会话缓存,Nginx 告诉客户端会话可以被重用,但实际上并不在缓存中存储会话参数(任性,言语同意用,行为取消用)

  • builtin:内置 OpenSSL 缓存,仅在一个工作进程中使用。缓存大小在会话中指定。如果未给出大小,则等于 20480 个会话。使用内置缓存可能会导致内存碎片

  • shared:所有工作进程之间共享缓存,缓存的相关信息用 name 和 size 来指定,同 name 的缓存可用于多个虚拟服务器

    name 是允许缓存的数据名,size 是允许缓存的数据大小,以字节为单位

例如:

1
ssl_session_cache builtin:1000 shared:SSL:10m;

10m 的 m 是兆。

ssl_session_timeout 指令用于开启 SSL 会话功能后,设置客户端能够反复使用储存在缓存中的会话参数时间,默认值超时时间是 5 秒

语法 默认值 位置
ssl_session_timeout <time>; ssl_session_timeout 5m; http、server

ssl_ciphers 指令指出允许的密码,密码指定为 OpenSSL 支持的格式

语法 默认值 位置
ssl_ciphers <ciphers>; ssl_ciphers HIGH:!aNULL:!MD5; http、server

可以直接在 Linux 系统上使用 openssl ciphers 查看 OpenSSl 支持的格式

1
openssl ciphers

ssl_prefer_server_ciphers 指令指定是否服务器密码优先客户端密码,默认关闭,建议开启。

语法 默认值 位置
ssl_perfer_server_ciphers <on | off>; ssl_perfer_server_ciphers off; http、server

SSL证书生成

方式一:使用阿里云/腾讯云等第三方服务进行购买免费版

需要购买域名进行证书的绑定,否则证书无法使用。

image

image

image

image

接着在右边弹窗进行域名绑定,填完写域名和个人信息,进入到验证信息

image

点击验证,不成功则去自己的域名解析列表查看,如下,点击添加记录,进行配置,或者已经看到记录类型是 TXT,记录值和上图一样的,则说明成功。

image

提交审核后,点击下载

image

下载 Nginx 服务器的证书

image

下载压缩包进行加压后,得到 .pem 证书和 .key 证书,把两个证书上传到 Linux,进行配置,往下看。

方式二:使用 OpenSSL 生成证书

这个方式适用于学习阶段,实际开发阶段使用方式一

  1. 先要确认当前系统是否有安装 OpenSSL
1
openssl version

安装下面的命令进行生成(一步一步来)

  1. 创建 /root/cerr 目录并进入
1
2
3
mkdir /root/cert

cd /root/cert
  1. 指定加密算法和加密方式,生成 server.key
1
2
3
4
5
# genrsa 是加密算法,des3 是加密方式,-out 代表输入长度为 2048 的 server.key
openssl genrsa -des3 -out server.key 2048

# 然后它会让你注册 server.key 的密码
# 输入密码 ......
  1. 根据你注册的 server.key 密码,生成 server.csr 文件,生成后它会让你注册你的基本信息,因为是个人的,所以信息随便填写
1
2
3
openssl req -new -key server.key -out server.csr

# 这里注册你的基本信息,信息随便填写

image

  1. 备份 server.key
1
cp server.key server.key.org
  1. 重新生成 server.key 文件,并输入刚才注册的密码
1
2
3
openssl rsa -in server.key.org -out server.key

# 会让你重新输入注册密码
  1. 生成 server.crt 文件
1
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
  1. 最后使用 ll 查看目录下是否生成相应的文件:

image

SSL实例模板(通用)

Nginx 的置文件添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server {
listen 80;
# ......
}
server {
listen 443 ssl; # 开启 SSL 功能
server_name localhost; # 如果是购买的域名,这里加上该域名

ssl_certificate /root/cert/server.cert; # 生成的 cert 或者 pem 证书路径,根据需求修改
ssl_certificate_key /root/cert/server.key; # 生成的 key 证书路径,根据需求修改
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; # 表示使用的加密套件的类型
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; # 表示使用的TLS协议的类型
ssl_prefer_server_ciphers on;

location / {
root html;
index index.html index.htm;
}
}

其实这个模板就是 Nginx 解压目录的 conf 目录下的 nginx.conf 内容。

建议

配置 SSL 证书时候,不要修改原来的 server 模块(listen 80 模块),应该新建一个 server 模块。

解决默认 http 问题。

配置完 SSL 证书还远远不安全。如果直接输入 www.frx.com,会默认加上『 http:// 』 而不是『 https:// 』,如何修改为『 https:// 』呢,我们利用 Rewrite 功能

1
2
3
4
5
6
7
8
9
10
11
server {
listen 443 ssl;
server_name www.frx.com; # 如果是 www.frx.com 发送请求

location / {
# ......
rewrite ^(.*)$ https://www.frx.com$1; # 则改为 https 方式
# ......
}
# ......
}

反向代理系统调优

反向代理值 Buffer 和 Cache。

Buffer 翻译过来是「缓冲」,Cache 翻译过来是「缓存」。

image

客户端通过代理服务器向被代理服务器获取数据后,代理服务器在获取的数据存储在缓存「瓶子」里,客户端再次获取相同资源时,直接从缓存「瓶子」里获取数据,不需要向被代理服务器获取数据,减轻压力。

相同点:

  • 两种方式都是用来提供 IO 吞吐效率,都是用来提升 Nginx 代理的性能。

不同点:

  • 缓冲主要用来解决不同设备之间数据传递速度不一致导致的性能低的问题,缓冲中的数据一旦此次操作完成后,就可以删除
  • 缓存主要是备份,将被代理服务器的数据缓存一份到代理服务器,这样的话,客户端再次获取相同数据的时候,就只需要从代理服务器上获取,效率较高,缓存中的数据可以重复使用,只有满足特定条件才会删除

Proxy Buffer 相关指令

  • proxy_buffering 指令用来开启或者关闭代理服务器的缓冲区,默认开启。

    语法 默认值 位置
    proxy_buffering <on | off>; proxy_buffering on; http、server、location
  • proxy_buffers 指令用来指定单个连接从代理服务器读取响应的缓存区的个数和大小。

    语法 默认值 位置
    proxy_buffers <number> <size>; proxy_buffers 8 4k | 8K;(与系统平台有关) http、server、location
    • number:缓冲区的个数
    • size:每个缓冲区的大小,缓冲区的总大小就是 number * size
  • proxy_buffer_size 指令用来设置从被代理服务器获取的第一部分响应数据的大小。保持与 proxy_buffers 中的 size 一致即可,当然也可以更小。

    语法 默认值 位置
    proxy_buffer_size <size>; proxy_buffer_size 4k | 8k;(与系统平台有关) http、server、location
  • proxy_busy_buffers_size 指令用来限制同时处于 BUSY 状态的缓冲总大小。

    语法 默认值 位置
    proxy_busy_buffers_size <size>; proxy_busy_buffers_size 8k | 16K; http、server、location
  • proxy_temp_path 指令用于当缓冲区存满后,仍未被 Nginx 服务器完全接受,响应数据就会被临时存放在磁盘文件上的该指令设置的文件路径下

    语法 默认值 位置
    proxy_temp_path <path>; proxy_temp_path proxy_temp; http、server、location

    注意 path 最多设置三层。

  • proxy_temp_file_write_size 指令用来设置磁盘上缓冲文件的大小。

    语法 默认值 位置
    proxy_temp_file_write_size <size>; proxy_temp_file_write_size 8K | 16K; http、server、location

网站调优模板(通用)

1
2
3
4
5
proxy_buffering on;
proxy_buffers 4 64k;
proxy_buffer_size 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;

反向代理问题

反向代理有一个严重的问题,那就是反向代理的网站需要的 css、js、png 等静态文件默认是从 Nginx 里获取,显然一个你的服务器的代理了如淘宝、京东等网站,那么淘宝、京东等网站的静态文件是从你的服务器里获取,但是服务器根本没有,那么我们如何让代理的网站以及网站需要的 css、js、png 等静态文件一同获取呢,添加如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name localhost;

location ~ .* { # 如果不是代理网站的根路径,请自行修改
proxy_pass http://127.0.0.1:8081; # 代理的网站地址
# 将网站的静态文件也代理过来
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;
}
}

Nginx 静态资源访问

Nginx 静态资源访问

引言

如何访问 Nginx 的静态资源?这其中涉及到了 Nginx 的核心功能 Rewrite 重写技术,本内容将讲解处理访问静态资源的相关知识。


Nginx的跨域问题

跨域问题,我们主要从以下方面进行解决:

  • 什么情况下会出现跨域问题
  • 实例演示跨域问题
  • 具体的解决方案是什么

同源策略

浏览器的同源策略:是一种约定,是浏览器最核心也是最基本的安全功能,如果浏览器少了同源策略,则浏览器的正常功能可能都会受到影响。

同源:协议、域名(IP)、端口相同即为同源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
http://192.168.200.131/user/1
https://192.168.200.131/user/1
# 不满足同源

http://192.168.200.131/user/1
http://192.168.200.132/user/1
# 不满足同源

http://192.168.200.131/user/1
http://192.168.200.131:8080/user/1
# 不满足同源

http://www.nginx.com/user/1
http://www.nginx.org/user/1
# 不满足同源

http://192.168.200.131/user/1
http://192.168.200.131:8080/user/1
# 不满足同源

http://www.nginx.org:80/user/1
http://www.nginx.org/user/1
# 满足同源

跨域问题

简单描述下:

有两台服务器分别为 A、B,如果从服务器 A 的页面发送异步请求到服务器 B 获取数据,如果服务器 A 和服务器 B 不满足同源策略,则就会出现跨域问题。

跨域案例

出现跨域问题会有什么效果?接下来通过一个需求来给大家演示下:

image

  1. Nginx 的 html 目录下新建一个 a.html
1
vim /usr/local/nginx/html/a.htm

添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<head>
<meta charset="utf-8">
<title>跨域问题演示</title>
<script src="jquery.js"></script>
<script>
$(function(){
$("#btn").click(function(){
$.get('http://192.168.200.133:8080/getUser',function(data){
alert(JSON.stringify(data));
});
});
});
</script>
</head>
<body>
<input type="button" value="获取数据" id="btn"/>
</body>
</html>
  1. 在 nginx.conf 配置如下内容
1
vim /usr/local/nginx/conf/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server{
listen 8080;
server_name localhost;
location /getUser{
default_type application/json;
return 200 '{"id":1,"name":"TOM","age":18}';
}
}
server{
listen 80;
server_name localhost;
location /{
root html;
index index.html;
}
}
  1. 通过浏览器测试访问

image

解决方案

使用 add_header 指令,该指令可以用来添加一些头信息。

语法 默认值 位置
add_header <name> <value> …… http、server、location

此处用来解决跨域问题,需要添加两个头信息,分别是

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods

Access-Control-Allow-Origin:直译过来是允许跨域访问的源地址信息,可以配置多个(多个用逗号分隔),也可以使用 * 代表所有源。

Access-Control-Allow-Methods:直译过来是允许跨域访问的请求方式,值可以为 GET、POST、PUT、DELETE ……,可以全部设置,也可以根据需要设置,多个用逗号分隔。

具体配置方式:

1
2
3
4
5
6
location /getUser {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;
default_type application/json; # return 的格式是 json
return 200 '{"id":1,"name":"TOM","age":18}';
}

静态资源防盗链

什么是资源盗链

资源盗链指的是此内容不在自己服务器上,而是通过技术手段,绕过别人的限制将别人的内容放到自己页面上最终展示给用户。以此来盗取大网站的空间和流量。简而言之就是用别人的东西成就自己的网站。

提供两种图片进行演示:

  • 京东:https://img14.360buyimg.com/n7/jfs/t1/101062/37/2153/254169/5dcbd410E6d10ba22/4ddbd212be225fcd.jpg
  • 百度:https://pics7.baidu.com/feed/cf1b9d16fdfaaf516f7e2011a7cda1e8f11f7a1a.jpeg?token=551979a23a0995e5e5279b8fa1a48b34&s=BD385394D2E963072FD48543030030BB

我们在 html 目录下准备一个页面 a.html,在页面上利用 img 标签引入这两个图片:

image

访问:http://192.168.200.133/a.html 来查看效果

image

从上面的效果,可以看出来,下面的图片地址添加了防止盗链的功能,京东这边我们可以直接使用其图片。

防盗链实现原理

了解防盗链的原理之前,我们得先学习一个 HTTP 的头信息 Referer,当浏览器向 Web 服务器发送请求的时候,一般都会带上 Referer,来告诉浏览器该网页是从哪个页面链接过来的。

image

后台服务器可以根据获取到的这个 Referer 信息来判断是否为自己信任的网站地址,如果是则放行继续访问,如果不是则可以返回 403(服务端拒绝访问)的状态信息。

防盗链实现实例

在本地模拟上述的服务器效果图:

image

Nginx 防盗链的具体实现:

valid_referers 指令:Nginx 会通过查看 Referer 自动和 valid_referers 的内容进行匹配,如果匹配到了就将 $invalid_referer 变量置 0,如果没有匹配到,则将 $invalid_referer 变量置为 1,匹配的过程中不区分大小写。

所以我们可以在配置文件判断 $invalid_referer 是否等于 1(true),即没有匹配到 ,等于则返回 403。

语法 默认值 位置
valid_referers <none | blocked | server_names | string> …… server、location
  • none:如果 Header 中的 Referer 为空,允许访问
  • blocked:在 Header 中的 Referer 不为空,但是该值被防火墙或代理进行伪装过,如不带『 http:// 』 、『 https:// 』等协议头的资源才允许访问。
  • server_names:指定具体的域名或者 IP
  • string:可以支持正则表达式和 * 的字符串。如果是正则表达式,需要以 ~ 开头表示

例如:

1
2
3
4
5
6
7
8
9
10
location ~ *\.(png|jpg|gif){
valid_referers none blocked www.baidu.com 192.168.91.200;

# valid_referers none blocked *.example.com example.* www.example.org ~\.google\.;

if ($invalid_referer){
return 403;
}
root /usr/local/nginx/html;
}

上方代码如果没有匹配上 www.baidu.com192.168.91.200,则 $invalid_referer 为 1(true),返回 403,代表不允许获取资源。

Nginx 配置文件支持 if 判断,但是 if 后面必须有空格。

问题:如果图片有很多,该如何批量进行防盗链?可以针对目录进行防盗链。

针对目录防盗链

假设 html 目录下有一个 images 目录,里面专门放防盗链的图片。

配置如下:

1
2
3
4
5
6
7
8
9
10
location /images {
valid_referers none blocked www.baidu.com 192.168.199.27;

# valid_referers none blocked *.example.com example.* www.example.org ~\.google\.;

if ($invalid_referer){
return 403;
}
root /usr/local/nginx/html;
}

只需将 location 的地址改成一个目录,这样我们可以对一个目录下的所有资源进行防盗链操作。

问题:Referer 的限制比较粗,比如浏览器发送请求时恶意加一个 Referer,上面的方式是无法进行限制的。那么这个问题改如何解决?

此时我们需要用到 Nginx 的第三方模块 ngx_http_accesskey_module,第三方模块如何实现盗链,如何在 Nginx 中使用第三方模块的功能,在后面有讲解。

Rewrite功能配置

Rewrite 是 Nginx 服务器提供的一个重要基本功能,是 Web 服务器产品中几乎必备的功能。主要的作用是用来实现 URL 的重写。

*warning *

Nginx 服务器的 Rewrite 功能的实现依赖于 PCRE 的支持,因此在编译安装 Nginx 服务器之前,需要安装 PCRE 库。Nginx 使用的是ngx_http_rewrite_module 模块来解析和处理 Rewrite 功能的相关配置。

地址重写与地址转发

重写和转发的区别:

  • 地址重写浏览器地址会发生变化而地址转发则不变
  • 一次地址重写会产生两次请求而一次地址转发只会产生一次请求
  • 地址重写到的页面必须是一个完整的路径而地址转发则不需要
  • 地址重写因为是两次请求,所以 request 范围内属性不能传递给新页面,而地址转发因为是一次请求所以可以传递值
  • 地址转发速度快于地址重写

set指令

该指令用来设置一个新的变量。

语法 默认值 位置
set <$key> <value>; server、location、if
  • variable:变量的名称,该变量名称要用 $ 作为变量的第一个字符,且不能与 Nginx 服务器内置的全局变量同名。
  • value:变量的值,可以是字符串、其他变量或者变量的组合等。

例如:

1
2
3
4
5
6
7
8
9
10
server {
listen 8081;
server_name localhost;
location /server {
set $name TOM;
set $age 18;
default_type text/plain;
return 200 $name=$age;
}
}

访问 https://192.168.200.133:8081:server,返回结果如图:

image

Rewrite常用全局变量

变量 说明
$args 变量中存放了请求 URL 中的请求指令。比如 http://192.168.200.133:8080?arg1=value1&args2=value2 中的『 arg1=value1&arg2=value2 』,功能和 $query_string 一样
$http_user_agent 变量存储的是用户访问服务的代理信息(如果通过浏览器访问,记录的是浏览器的相关版本信息)
$host 变量存储的是访问服务器的 server_name 值
$document_uri 变量存储的是当前访问地址的URI。比如 http://192.168.200.133/server?id=10&name=zhangsan中的『 /server 』,功能和 $uri 一样
$document_root 变量存储的是当前请求对应 location 的 root 值,如果未设置,默认指向 Nginx 自带 html 目录所在位置
$content_length 变量存储的是请求头中的 Content-Length 的值
$content_type 变量存储的是请求头中的 Content-Type 的值
$http_cookie 变量存储的是客户端的 cookie 信息,可以通过 add_header Set-Cookie 'cookieName=cookieValue' 来添加 cookie 数据
$limit_rate 变量中存储的是 Nginx 服务器对网络连接速率的限制,也就是 Nginx 配置中对 limit_rate 指令设置的值,默认是 0,不限制。
$remote_addr 变量中存储的是客户端的 IP 地址
$remote_port 变量中存储了客户端与服务端建立连接的端口号
$remote_user 变量中存储了客户端的用户名,需要有认证模块才能获取
$scheme 变量中存储了访问协议
$server_addr 变量中存储了服务端的地址
$server_name 变量中存储了客户端请求到达的服务器的名称
$server_port 变量中存储了客户端请求到达服务器的端口号
$server_protocol 变量中存储了客户端请求协议的版本,比如 『 HTTP/1.1 』
$request_body_file 变量中存储了发给后端服务器的本地文件资源的名称
$request_method 变量中存储了客户端的请求方式,比如『 GET 』,『 POST 』等
$request_filename 变量中存储了当前请求的资源文件的路径名
$request_uri 变量中存储了当前请求的 URI,并且携带请求参数,比如 http://192.168.200.133/server?id=10&name=zhangsan 中的 『 /server?id=10&name=zhangsan 』

例如

1
2
3
4
5
6
7
8
9
10
11
server {
listen 8081;
server_name localhost;
location /server {
root /usr/local/nginx/abc;
set $name TOM;
set $age 18;
default_type text/plain;
return 200 $name=$age=$args=$http_user_agent=$host=$document_root;
}
}

访问:http://192.168.200.133:8081/server?username=JERRY&gender=1

效果如图:

image

可以把访问的信息记录在日志中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
http{
# ......
log_format main '$remote_addr - $request - $status - $request_uri - $http_user_agent';
server {
listen 8081;
server_name localhost;
location /server {
access_log logs/access.log main;
root /usr/local/nginx/abc;
set $name TOM;
set $age 18;
default_type text/plain;
return 200 $name=$age=$args=$http_user_agent=$host=$document_root;
}
}
}

访问:http://192.168.200.133:8081/server?username=JERRY&gender=1

然后查看日志,效果如图:

image

if指令

该指令用来支持条件判断,并根据条件判断结果选择不同的 Nginx 配置。

语法 默认值 位置
if (condition) { … } server、location

if 和括号之间要有空格,condition 为判定条件,可以支持以下写法:

  • 变量名。如果变量名对应的值为空或者是 0,if 都判断为 false,其他条件为 true。
1
2
3
if ($param){

}
  • 使用『 = 』和『 != 』比较变量和字符串是否相等,满足条件为 true,不满足为 false
1
2
3
if ($request_method = POST){
return 405;
}

注意:POST 和 Java 不太一样的地方是字符串不需要添加引号。

  • 使用正则表达式对变量进行匹配,匹配成功返回 true,否则返回 false。变量与正则表达式之间使用『 ~ 』,『 * 』,『 ! 』,『 !~* 』来连接。
    • 『 ~ 』代表匹配正则表达式过程中区分大小写,进行模糊匹配
    • 『 ~* 』代表匹配正则表达式过程中不区分大小写,进行模糊匹配
    • 『 !~ 』和『 !~* 』刚好和上面取相反值,如果匹配上返回 false,匹配不上返回 true,进行模糊匹配
1
2
3
if ($http_user_agent ~ MSIE){
# $http_user_agent 的值中是否包含 MSIE 字符串,如果包含返回 true
}
  • 判断请求的文件是否存在使用『 -f 』和『 !-f 』
    • 当使用『 -f 』时,如果请求的文件存在返回 true,不存在返回 false。
    • 当使用『 !-f 』时,如果请求文件不存在,但该文件所在目录存在返回 true,文件和目录都不存在返回 false,如果文件存在返回 false。
1
2
3
4
5
6
if (-f $request_filename){
# 判断请求的文件是否存在
}
if (!-f $request_filename){
# 判断请求的文件是否不存在
}

例如:用户访问的页面不存在,则返回一个友好的提示

1
2
3
4
5
6
7
8
location / {
root html;
default_type text/html;
# 判断请求的文件是否不存在
if (!-f $request_filename){
return 200 '<h1>不好意思,文件资源找不到!</h1>';
}
}
  • 判断请求的目录是否存在使用『 -d 』和『 !-d 』

    当使用『 -d 』时,如果请求的目录存在,返回 true,如果目录不存在则返回 false。

    当使用『 !-d 』时,如果请求的目录不存在但该目录的上级目录存在则返回 true,该目录和它上级目录都不存在则返回 false,如果请求目录存在也返回false。

break指令

该指令用于中断当前相同作用域中的其他 Nginx 配置。与该指令处于同一作用域的 Nginx 配置中,位于它前面的指令配置生效,位于后面的指令配置无效。并且break还有另外一个功能就是终止当前的匹配并把当前的URI在本location进行重定向访问处理。

语法 默认值 位置
break; server、location、if

例子:

1
2
3
4
5
6
7
8
9
10
11
location /testbreak {
default_type text/plain;
set $username TOM;
if ($args){
set $username JERRY;
break;
set $username ROSE;
}
add_header username $username;
return 200 $username;
}

不带参数访问:http://192.168.200.133:8081/testbreak

效果如图:

image

带参数访问:http://192.168.200.133:8081/testbreak/1

效果如图:

image

return指令

该指令用于完成对请求的处理,直接向客户端返回响应状态代码。在 return 后的所有 Nginx 配置都是无效的。

语法 默认值 位置
return <code> [text];
return <code> <URL>;
return <URL>;
server、location、if
  • code:返回给客户端的 HTTP 状态代理。可以返回的状态代码为 0 ~ 999 的任意 HTTP 状态代理
  • text:返回给客户端的响应体内容,支持变量的使用和 JSON 字符串
  • URL:跳转给客户端的 URL 地址。

例如:

1
2
3
4
5
6
7
8
location / {
default_type text/plain;
return 200 "欢迎使用 Nginx";
}

location /baidu {
return 302 https://www.baidu.com;
}

此时访问 Nginx,就会在页面看到这句话:欢迎使用 Nginx。

如果访问 /baidu,则跳转到 https://www.baidu.com

rewrite指令

该指令通过正则表达式的使用来改变 URI。可以同时存在一个或者多个指令,按照顺序依次对 URL 进行匹配和处理。

URL 和 URI 的区别:

  • URI:统一资源标识符
  • URL:统一资源定位符
语法 默认值 位置
rewrite regex replacement [flag]; server、location、if
  • regex:用来匹配 URI 的正则表达式

  • replacement:匹配成功后,用于替换 URI 中被截取内容的字符串。如果该字符串是以 『 http:// 』或者『 https:// 』开头的,则不会继续向下对URI 进行其他处理,而是直接返回重写后的 URI 给客户端。

    例如:(括号的值会作为 $1 的值)^ 代表匹配输入字符串的起始位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ......
listen 8081;
location /rewrite {
rewrite ^/rewrite/url\w*$ https://www.baidu.com;
rewrite ^/rewrite/(test)\w*$ /$1; # 如果是 /rewrite/testxxx,则重写 url 为 test
rewrite ^/rewrite/(demo)\w*$ /$1; # 如果是 /rewrite/demoxxx,则重写 url 为 demo
}
location /test { # 重写后的 url 如果为 test,触发 location
default_type text/plain;
return 200 test_sucess;
}
location /demo { # 重写后的 url 如果为 demo,触发 location
default_type text/plain;
return 200 demo_sucess;
}

访问 http://192.168.200.113/8081/rewrite/urlxxx,跳转到 https://www.baidu.com

访问 http://192.168.200.113/8081/rewrite/testxxx,返回 test_sucess。

访问 http://192.168.200.113/8081/rewrite/demoxxx,返回 demo_sucess。

flag:用来设置 Rewrite 对 URI 的处理行为,可选值有如下:

  • last:终止继续在本 location 块中处理接收到的后续 URI,并将此处重写的 URl 作为一个新的 URI,使用各 location 块进行处理。该标志将重写后的 URI 重写在 server 块中执行,为重写后的 URI 提供了转入到其他 location 块的机会。重写地址后访问其他的 location 块,浏览器地址栏 URL 地址不变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ......
listen 8081;
location /rewrite {
rewrite ^/rewrite/(test)\w*$ /$1 last; # 如果是 /rewrite/testxxx,则重写 url 为 test
rewrite ^/rewrite/(demo)\w*$ $1 last; # 如果是 /rewrite/demoxxx,则重写 url 为 demo
}
location /test { # 重写后的 url 如果为 test,触发 location
default_type text/plain;
return 200 test_sucess;
}
location /demo { # 重写后的 url 如果为 demo,触发 location
default_type text/plain;
return 200 demo_sucess;
}

访问 http://192.168.200.113/8081/rewrite/testxxx,返回 test_sucess。

访问 http://192.168.200.113/8081/rewrite/demoxxx,返回 demo_sucess。

单次访问不明显,多次访问,last 只处理第一个。

  • break:将此处重写的 URl 作为一个新的 URI,在本块中继续进行处理。该标志将重写后的地址在当前的 location 块中执行,不会将新的 URI 转向其他的 location 块。仅仅重写地址,不会触发其他 location 块,浏览器地址栏 URL 地址不变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ......
listen 8081;
location /rewrite {
rewrite ^/rewrite/(test)\w*$ /$1 break; # 如果是 /rewrite/testxxx,则重写 url 为 test
rewrite ^/rewrite/(demo)\w*$ $1 break; # 如果是 /rewrite/demoxxx,则重写 url 为 demo

# /test 和 /demo 就在当前块进行处理,所以会在当前的 location 块找到如下 html 页面:
# /usr/local/nginx/html/test/index.html
# /usr/local/nginx/html/demo/index.html
}
location /test { # 重写后的 url 如果为 test,触发 location
default_type text/plain;
return 200 test_sucess;
}
location /demo { # 重写后的 url 如果为 demo,触发 location
default_type text/plain;
return 200 demo_sucess;
}

和 break 指令类似。假设访问的是 /test,则将 /test 放在当前的 location 块进行处理,哪怕第二个 location 块就是处理 /test 的,它也不会去找第二个 location 块,只在当前块进行处理。所以他会请求 /usr/local/nginx/html/test/index.html

  • redirect:将重写后的 URI 返回给客户端,状态码为 302,指明是临时重定向 URL,主要用在 replacement 变量不是以『 http:// 』或者『 https:// 』开头的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ......
listen 8081;
location /rewrite {
rewrite ^/rewrite/(test)\w*$ /$1 redirect; # 如果是 /rewrite/testxxx,则重写 url 为 test
rewrite ^/rewrite/(demo)\w*$ $1 redirect; # 如果是 /rewrite/demoxxx,则重写 url 为 demo
}
location /test { # 重写后的 url 如果为 test,触发 location
default_type text/plain;
return 200 test_sucess;
}
location /demo { # 重写后的 url 如果为 demo,触发 location
default_type text/plain;
return 200 demo_sucess;
}

特点是重定向,就是浏览的地址栏会发送改变。如发送请求 /testxxx,它会重定向到 /test,触发第二个 location 块,浏览的地址栏也会由 /testxxx 变成 /test

  • permanent:将重写后的 URI 返回给客户端,状态码为 301,指明是永久重定向 URL,主要用在 replacement 变量不是以『 http:// 』或者『 https:// 』开头的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ......
listen 8081;
location /rewrite {
rewrite ^/rewrite/(test)\w*$ /$1 permanent; # 如果是 /rewrite/testxxx,则重写 url 为 test
rewrite ^/rewrite/(demo)\w*$ $1 permanent; # 如果是 /rewrite/demoxxx,则重写 url 为 demo
}
location /test { # 重写后的 url 如果为 test,触发 location
default_type text/plain;
return 200 test_sucess;
}
location /demo { # 重写后的 url 如果为 demo,触发 location
default_type text/plain;
return 200 demo_sucess;
}

redirect 的区别就是状态码为 301,并且是永久重定向。

flag 总结

标记符号 说明
last 本条规则匹配完成后继续向下匹配新的 location URI 规则
break 本条规则匹配完成后终止,不在匹配任何规则
redirect 返回 302 临时重定向
permanent 返回 301 永久重定向
  • break 与 last 都停止处理后续重写规则,只不过 last 会重新发起新的请求并使用新的请求路由匹配location,但 break 不会。所以当请求 break 时,如匹配成功,则请求成功,返回 200;如果匹配失败,则返回 404
  • 服务器配置好 redirect 和 permanent 之后,打开浏览器分别访问这两个请求地址,然后停止 Nginx 服务。这时再访问 redirect 请求会直接报出无法连接的错误。但是 permanent 请求是永久重定向,浏览器会忽略原始地址直接访问永久重定向之后的地址,所以请求仍然成功。(这个验证不能禁用浏览器的缓存,否则即使是 permanent 重定向,浏览器仍然会向原始地址发出请求验证之前的永久重定向是否有效)
  • 对于搜索引擎来说,搜索引擎在抓取到 301 永久重定向请求响应内容的同时也会将原始的网址替换为重定向之后的网址,而对于 302 临时重定向请求则仍然会使用原始的网址并且可能会被搜索引擎认为有作弊的嫌疑。所以对于线上正式环境来讲,尽量避免使用 302 跳转
  • 如果 replacement 以 「 http:// 」或「 https:// 」或「 $scheme 」开始,处理过程将终止,并将这个重定向直接返回给客户端

rewrite_log指令

该指令配置是否开启 URL 重写日志的输出功能,默认关闭。

语法 默认值 位置
rewrite_log <on | off>; rewrite_log off; http、server、location、if

开启后,URL 重写的相关日志将以 notice 级别输出到 error_log 指令配置的日志文件汇总。

1
2
3
4
5
location /rewrite_log {
rewrite_log on; # 开启重写日志
error_log logs /error.log notice; # 切换为 notice 模式,因为只支持这个模式
return 200 '开启了重写日志';
}

Rewrite的案例

域名跳转

问题分析

先来看一个效果,如果我们想访问京东网站,大家都知道我们可以输入 www.jd.com,但是同样的我们也可以输入 www.360buy.com 同样也都能访问到京东网站。这个其实是因为京东刚开始的时候域名就是 www.360buy.com,后面由于各种原因把自己的域名换成了 www.jd.com,虽然说域名改变了,但是对于以前只记住了 www.360buy.com 的用户来说,我们如何把这部分用户也迁移到我们新域名的访问上来,针对于这个问题,我们就可以使用 Nginx 中 Rewrite 的域名跳转来解决。

环境准备

1
vim /etc/hosts

添加内容:

1
2
192.168.200.133 www.360buy.com
192.168.200.133 www.jd.com
  • /usr/local/nginx/html/test 目录下创建一个访问页面 frx.html

添加内容:

1
2
3
4
5
6
<html>
<title></title>
<body>
<h1>欢迎来到我的网站</h1>
</body>
</html>
  • 通过 Nginx 实现当访问 www.frx.com 访问到 frx.html 页面
1
2
3
4
5
6
7
8
server {
listen 80;
server_name www.frx.com;
location / {
root /usr/local/nginx/html/;
index frx.html;
}
}

通过 Rewrite 完成将 www.360buy.com 的请求跳转到 www.jd.com

1
2
3
4
5
server {
listen 80;
server_name www.360buy.com;
rewrite ^/ http://www.jd.com permanent; # 永久重定向
}

问题描述:如何在域名跳转的过程中携带请求的 URI?

比如 www.360buy.com?part=显示器 变成 www.jd.com?part=显示器

  • 修改配置信息
1
2
3
4
5
server {
listen 80;
server_name www.itheima.com;
rewrite ^(.*) http://www.hm.com$1 permanent;
}

括号里是 www.itheima.com 后面出现 0 次或 多次不以 \n(换行)结尾的值,该值赋给 $1。

问题描述:我们除了上述说的只有 www.jd.com、www.360buy.com,其实还有我们也可以通过 www.jingdong.com 来访问,那么如何通过 Rewrite 来实现多个域名的跳转?

  • 添加域名
1
2
3
4
5
# 打开 hosts 文件
vim /etc/hosts

# 添加域名
192.168.200.133 www.jingdong.com
  • 修改配置信息
1
2
3
4
5
server{
listen 80;
server_name www.360buy.com www.jingdong.com;
rewrite ^(.*) http://www.jd.com$1 permanent;
}

多个 server_name 用空格隔开。

域名镜像

上述案例中,将 www.360buy.comwww.jingdong.com 都能跳转到 www.jd.com,那么 www.jd.com 我们就可以把它起名叫主域名,其他两个就是我们所说的镜像域名,当然如果我们不想把整个网站做镜像,只想为其中某一个子目录下的资源做镜像,比如用户可以跳到首页 Web下,而管理员跳转到后台 Web,我们可以在 location 块中配置 Rewrite 功能。

比如:

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name rewrite.myweb.com;
location ^~ /user {
rewrite ^/user(.*) http://www.myweb.com/index$1 last; # 用户跳到首页
}
location ^~ /manage {
rewrite ^/manage(.*) http://www.myweb.com/manage$1 last; # 管理员跳到后台
}
}

独立域名

一个完整的项目包含多个模块,比如购物网站有商品商品搜索模块、商品详情模块已经购物车模块等,那么我们如何为每一个模块设置独立的域名。

需求:

  • http://search.product.com:访问商品搜索模块
  • http://item.product.com:访问商品详情模块
  • http://cart.product.com:访问商品购物车模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server{
listen 80;
server_name search.product.com;
rewrite ^(.*) http://www.shop.com/search$1 last;
}
server{
listen 81;
server_name item.product.com;
rewrite ^(.*) http://www.shop.com/item$1 last;
}
server{
listen 82;
server_name cart.product.com;
rewrite ^(.*) http://www.shop.com/cart$1 last;
}

自动加『/』

有时候访问的地址要求后面以 / 结尾,那么我们需要解决如果用户忘记输入 /,Nginx 就会自动加上 /

通过一个例子来演示问题:

1
2
3
4
5
6
7
8
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html;
}
}

要想访问上述资源,很简单,只需要通过 http://192.168.200.133 直接就能访问,地址后面不需要加 /,但是如果将上述的配置修改为如下内容:

1
2
3
4
5
6
7
8
server {
listen 80;
server_name localhost;
location /frx {
root html;
index index.html;
}
}

这个时候,要想访问上述资源,按照上述的访问方式,我们可以通过 http://192.168.200.133/frx/ 来访问,但是如果地址后面不加斜杠,如 http://192.168.200.133/frx,页面就会出问题。如果不加斜杠,Nginx 服务器内部会自动做一个 301 的重定向,重定向的地址会有一个指令叫 server_name_in_redirect 来决定重定向的地址:

  • 如果该指令为 on

    重定向的地址为:http://server_name/目录名/

  • 如果该指令为 off

    重定向的地址为:http://原URL中的域名/目录名/

所以就拿刚才的地址来说,访问 http://192.168.200.133/frx 如果不加斜杠,那么按照上述规则:

  • 如果指令 server_name_in_redirect 为 on,则 301 重定向地址变为 http://localhost/frx/,IP 发生改变,地址出现了问题
  • 如果指令 server_name_in_redirect 为 off,则 301 重定向地址变为 http://192.168.200.133/frx/。这个符合我们的期望

注意 server_name_in_redirect 指令在 Nginx 的 0.8.48 版本之前默认都是 on,之后改成了 off,所以现在我们这个版本不需要考虑这个问题,但是如果是 0.8.48 以前的版本并且 server_name_in_redirect 设置为 on,我们如何通过 Rewrite 来解决这个问题?

解决方案

我们可以使用 Rewrite 功能为末尾没有斜杠的 URL 自动添加一个斜杠

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name localhost;
server_name_in_redirect on;
location /frx {
if (-d $request_filename){ # 如果请求的资源目录存在
rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent; # $2 获取第二个括号的值:/
}
}
}

$1 是第一个括号的值,$2 是第二个括号的值。

合并目录

搜索引擎优化(SEO)是一种利用搜索引擎的搜索规则来提供目的网站的有关搜索引擎内排名的方式。我们在创建自己的站点时,可以通过很多中方式来有效的提供搜索引擎优化的程度。其中有一项就包含 URL 的目录层级,一般不要超过三层,否则的话不利于搜索引擎的搜索,也给客户端的输入带来了负担,但是将所有的文件放在一个目录下,又会导致文件资源管理混乱,并且访问文件的速度也会随着文件增多而慢下来,这两个问题是相互矛盾的,那么使用 Rewrite 如何解决这些问题呢?

举例,网站中有一个资源文件的访问路径 /server/11/22/33/44/20.html,也就是说 20.html 存在于第 5 级目录下,如果想要访问该资源文件,客户端的 URL 地址就要写成 http://www.web.com/server/11/22/33/44/20.html,并且在配置文件进行如下配置:

1
2
3
4
5
6
7
server {
listen 80;
server_name www.web.com;
location /server {
root html;
}
}

但是这个是非常不利于 SEO 搜索引擎优化的,同时客户端也不好记。使用 Rewrite 的正则表达式,我们可以进行如下配置:

1
2
3
4
5
6
7
server {
listen 80;
server_name www.web.com;
location /server {
rewrite ^/server-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /server/$1/$2/$3/$4/$5.html last;
}
}

这样配置后,客户端只需要输入 http://www.web.com/server-11-22-33-44-20.html 就可以访问到 20.html 页面了。这里也充分利用了 Rewrite 指令支持正则表达式的特性。

多级域名

当你配置了多级域名,如二级域名 xxx.frxcat.fun,并且静态资源目录恰好和二级域名的 xxx 可以匹配,则可以使用正则表达式进行匹配,日后,如果又多个 xxx,则再创建对应的该目录即可。

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name ~^(.+)?.frxcat.fun$;
index idnex.html;
if ($host = frxcat.fun){
rewrite ^(.*)$ https://www.frxcat.fun$2 permanent;
}
root /data/html/$1/;
}

这样访问 docs.frxcat.fun,自动去 /data/html/docs/ 目录下找到 index.html,如果是 bing.youngkbt.cn,则会去 /data/html/bing/ 目录下找到 idnex.html,以此类推。

if 语句的作用是将 frxcat.fun 重定向到 www.frxcat.fun,这样既解决了网站的主目录访问,又可以增加 SEO 中对 www.frxcat.fun 的域名权重。

防盗链

防盗链之前我们已经介绍过了相关的知识,在 Rewrite 中的防盗链和之前将的原理其实都是一样的,只不过通过 Rewrite 可以将防盗链的功能进行完善下,当出现防盗链的情况,我们可以使用 Rewrite 将请求转发到自定义的一张图片和页面,给用户比较好的提示信息。

下面有两个配置实例:

  • 根据文件类型实现防盗链配置:
1
2
3
4
5
6
7
8
9
10
server{
listen 80;
server_name www.web.com;
locatin ~* ^.+\.(gif|jpg|png|swf|flv|rar|zip)$ {
valid_referers none blocked server_names *.web.com; # server_names 后指定具体的域名或者 IP
if ($invalid_referer){
rewrite ^/ http://www.web.com/images/forbidden.png; # 跳转到默认地址
}
}
}
  • 根据目录实现防盗链配置:
1
2
3
4
5
6
7
8
9
10
11
server{
listen 80;
server_name www.web.com;
location /file {
root /server/file; # 资源在 server 目录下的 file 目录里
valid_referers none blocked server_names *.web.com; # server_names 后指定具体的域名或者 IP
if ($invalid_referer){
rewrite ^/ http://www.web.com/images/forbidden.png; # 跳转到 file 目录下的图片
}
}
}

访问限流

我们构建网站是为了让用户访问它们,我们希望用于合法访问。所以不得不采取一些措施限制滥用访问的用户。这种滥用指的是从同一 IP 每秒到服务器请求的连接数。因为这可能是在同一时间内,世界各地的多台机器上的爬虫机器人多次尝试爬取网站的内容。

1
2
3
4
5
6
7
8
9
# 限制用户连接数来预防 DOS 攻击
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
# 限制同一客户端 ip 最大并发连接数
limit_conn perip 2;
# 限制同一server最大并发连接数
limit_conn perserver 20;
# 限制下载速度,根据自身服务器带宽配置
limit_rate 300k;

链接超时

长时间占着连接资源不释放,最终会导致请求的堆积,Nginx 处理请求效率大大降低。所以我们对连接的控制都要注意设置超时时间,通过超时机制自动回收资源、避免资源浪费。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 客户端、服务端设置
server_names_hash_bucket_size 128;
server_names_hash_max_size 512;
# 长连接超时配置
keepalive_timeout 65;
client_header_timeout 15s;
client_body_timeout 15s;
send_timeout 60s;

# 代理设置
# 与后端服务器建立连接的超时时间。注意这个一般不能大于 75 秒
proxy_connect_timeout 30s;
proxy_send_timeout 120s;
# 从后端服务器读取响应的超时
proxy_read_timeout 120s;

HTML引入

我们编写 .html 文件的时候,难免需要引入 css 和 js 文件,如果是在本地,那么引入非常简单,直接相对路径即可,但是部署到 Nginx 时,相对路径不再是相对 html 文件的目录,所以生产环境和开发环境的引入格式不一样。

在 Nginx 中的 .html 文件,引入 css 和 js,要加上 / 作为开头,/ 代表 Nginx 的根目录,即配置文件 location / 的指定的 root 路径。

比如 Nginx 的配置文件内容如下:

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name localhost;

location / {
root /usr/local/nginx/html; # 静态文件根目录
index idnex.html;
}
}

有一个 aa.html 在 /usr/local/nginx/html/test 目录下,并且 aa.html 引入了 aa.css 和 aa.js,两个静态文件在 aa.html 所在目录的 static 文件夹里。

1
2
3
4
5
/usr/local/nginx/html/test 目录
├── a.html
├── static
│ ├── a.css
│ ├── a.js

在本地环境,我们可以这样写:

1
2
<link rel="stylesheet" href="static/aa.css" />
<script src="static/aa.js"></script>

但是部署到 Nginx 后,这样写会找不到这两个资源,因为 / 触发 location /,进入 /usr/local/nginx/html 目录,而这两个文件在 /usr/local/nginx/html/test/static 目录下,所以我们部署到 Nginx 后,需要修改为:

1
2
<link rel="stylesheet" href="/test/static/aa.css" />
<script src="/test/static/aa.js"></script>

Nginx 基础配置实例

Nginx 基础配置实例

引言

学习了核心配置文件的内容,也仅仅是学习,没有实际例子来巩固的知识容易流失,本内容带你写一个简单的 demo 实例。

每次开机都手动启动 Nginx 服务?每次使用 Nginx 的指令总是要进入 sbin 目录下?本文带你配置 Naginx 服务自启和全局 nginx 指令。

基础配置实例

前面我们已经对 Nginx 服务器默认配置文件的结构和涉及的基本指令做了详细的阐述。通过这些指令的合理配置,我们就可以让一台 Nginx 服务器正常工作,并且提供基本的 Web 服务器功能。

接下来我们将通过一个比较完整和最简单的基础配置实例,来巩固下前面所学习的指令及其配置。

需求

  • 有如下访问:

    http://192.168.199.27:8081/server1/location1 访问的是:index_sr1_location1.html http://192.168.199.27:8081/server1/location2 访问的是:index_sr1_location2.html http://192.168.199.27:8082/server2/location1 访问的是:index_sr2_location1.html http://192.168.199.27:8082/server2/location2 访问的是:index_sr2_location2.html

  • 如果访问的资源不存在,返回自定义的 404 页面

  • 将 /server1 和 /server2 的配置使用不同的配置文件分割,将两个文件文件放到 /home/www/conf.d 目录下,然后在 Nginx 的配置文件使用 include 合并两个文件

  • 为 /server1 和 /server2 各自创建一个访问日志文件

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 创建 404 页面
touch /home/www/404.html

# 创建 conf.d 目录
mkdir /home/www/conf

# 创建两个配置文件
touch /home/www/conf/server1.conf
touch /home/www/conf/server2.conf

# 创建 myweb 目录
mkdir /home/www/myweb

# 创建 server1 目录和其子目录以及 index.html 文件
mkdir -p /home/www/myweb/server1/location1
mkdir -p /home/www/myweb/server1/location2

touch /home/www/myweb/server1/location1/index.html
touch /home/www/myweb/server1/location2/index.html

# 创建日志目录和日志文件
mkdir -p /home/www/myweb/server1/logs
touch /home/www/myweb/server1/logs/access.log

# 创建 server2 目录和其子目录以及 index.html 文件
# 和创建 server1 步骤一样,把 1 改为 2 即可

准备相关文件,/homw/www 目录如下:

image

因为 Nginx 自带配置文件的备份,即 nginx.conf.default,所以我们可以直接修改配置文件,但是如果你的配置文件曾经修改过,那么请进行备份。

1
cp /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.backup

备份后,进入 /usr/local/nginx/conf/nginx.conf 配置文件

1
vim /usr/local/nginx/conf/nginx.conf

先清空文件,然后添加如下内容:

1
cache-lifetime="5" :options="{ useUrlFragment: false }"

有注释版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
user www; # 配置允许运行 Nginx 工作进程的用户和用户组
worker_processes 2; # 配置运行 Nginx 进程生成的 worker 进程数
error_log logs/error.log; # 配置 Nginx 服务器运行对错误日志存放的路径
pid logs/nginx.pid; # 配置 Nginx 服务器允许时记录 Nginx 的 master 进程的 PID 文件路径和名称
daemon on; # 配置 Nginx 服务是否以守护进程方法启动

events{
accept_mutex on; # 设置 Nginx 网络连接序列化,解决惊群
multi_accept on; # 设置 Nginx 的 worker 进程是否可以同时接收多个请求
worker_connections 1024; # 设置 Nginx 的 worker 进程最大的连接数
use epoll; # 设置 Nginx 使用的事件驱动模型
}

http{

include mime.types; # 定义 MIME-Type
default_type application/octet-stream;
sendfile on; # 配置允许使用 sendfile 方式运输
keepalive_timeout 65; # 配置连接超时时间

# 配置请求处理日志格式
log_format server1 '===>server1 access log';
log_format server2 '===>server2 access log';

include /home/www/conf/*.conf; # 引用其他 conf 文件
}

无注释版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
user www;
worker_processes 2;
error_log logs/error.log;
pid logs/nginx.pid;
daemon on;

events{
accept_mutex on;
multi_accept on;
worker_connections 1024;
use epoll;
}

http{

include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;


log_format server1 '===>this is server1 access log';
log_format server2 '===>this is server2 access log';

include /home/www/conf/*.conf;
}

第 25 行代码使用 include 将 service1 和service2 的配置文件进行引用。以后无需修改主配置文件,只需要引入子配置文件即可,主配置文件作为默认值,子配置文件的内容会覆盖和主配置文件相同的内容。

进入 server1.conf 文件

1
vim /home/www/conf/server1.conf

server1.conf 文件内容:

1
cache-lifetime="5" :options="{ useUrlFragment: false }"

有注释版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server{
listen 8081; # 配置监听端口和主机名称
server_name localhost;
access_log /home/www/myweb/server1/logs/access.log server1; # 配置请求处理日志存放路径
error_page 404 /404.html; # 配置错误页面

location /server1/location1{ # 配置处理 /server1/location1 请求的 location
root /home/www/myweb;
index index.html; # 这是 server1 下的 location1 的 index.html
}

location /server1/location2{ # 配置处理 /server1/location2 请求的 location
root /home/www/myweb;
index index.html; # 这是 server1 下的 location2 的 index.html
}

location = /404.html { # 配置错误页面转向
root /home/www;
index 404.html;
}
}

无注释版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server{
listen 8081;
server_name localhost;
access_log /home/www/myweb/server1/logs/access.log server1;
error_page 404 /404.html;

location /server1/location1{
root /home/www/myweb;
index index.html;
}

location /server1/location2{
root /home/www/myweb;
index index.html;
}

location = /404.html {
root /home/www;
index 404.html;
}
}

server2.conf 文件内容:

1
cache-lifetime="5" :options="{ useUrlFragment: false }"

有注释版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server{
listen 8082; # 配置监听端口和主机名称
server_name localhost;
access_log /home/www/myweb/server2/logs/access.log server2; # 配置请求处理日志存放路径
error_page 404 /404.html; # 配置错误页面,对404.html做了定向配置

location /server2/location1{ # 配置处理 /server1/location1 请求的 location
root /home/www/myweb;
index index.html; # 这是 server2 下的 location1 的 index.html
}

location /server2/location2{ # 配置处理 /server2/location2 请求的 location
root /home/www/myweb;
index index.html; # 这是 server2 下的 location2 的 index.html
}

location = /404.html { # 配置错误页面转向
root /home/www;
index 404.html;
}
}

无注解版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server{
listen 8082;
server_name localhost;
access_log /home/www/myweb/server2/logs/access.log server2;
error_page 404 /404.html;

location /server2/location1{
root /home/www/myweb;
index index.html;
}

location /server2/location2{
root /home/www/myweb;
index index.html;
}

location = /404.html {
root /home/www;
index 404.html;
}
}

server1下面的location1下面的index.html的内容

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h1>server1下面的loaction1下面的index.html</h1>
</body>
</html>

其他的三个页面把数字改了就可以

404.html内容;

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h1>不好意思,程序小哥正在加紧维修中 ...... </h1>
</body>
</html>

重启 Nginx ,使得配置文件生效

1
nginx -s reload

测试

如果没有关闭防火墙,记得开放 8081 和 8082 端口。

1
2
3
4
5
6
# 开放 8081 和 8082 端口
firewall-cmd --zone=public --add-port=8081/tcp --permanent
firewall-cmd --zone=public --add-port=8082/tcp --permanent

# 重启防火墙
firewall-cmd --reload

打开浏览器分别访问,效果如图所示:

8081 的 server1 的 location1:

image

8081 的 server1 的 location2:

image

8082 的 server2 的 location1:

image

8082 的 server2 的 location2:

image

如果访问一个不存在的 404 请求:

image

日志也会打印,这里演示一个:

1
2
3
4
5
6
7
8
9
10
11
[root@master www]# tail /home/www/myweb/server1/logs/access.log
===>this is server1 access log
===>this is server1 access log
===>this is server1 access log
===>this is server1 access log
===>this is server1 access log
===>this is server1 access log
===>this is server1 access log
===>this is server1 access log
===>this is server1 access log
===>this is server1 access log

操作的问题

经过前面的操作,我们会发现,如果想要启动、关闭或重新加载 Nginx 配置文件,都需要先进入到 Nginx 的安装目录的 sbin 目录,然后使用 Nginx 的二级制可执行文件 nginx 来操作,相对来说操作比较繁琐,这块该如何优化?另外如果我们想把 Nginx 设置成随着服务器启动就自动完成启动操作,又该如何来实现?

这就需要用到接下来我们要讲解的两个知识点:

  • Nginx 服务启停配置
  • Nginx 全局命令配置

服务启停配置

把 Nginx 应用服务设置成为系统服务,方便对 Nginx 服务的启动和停止等相关操作,具体实现步骤:

  • /usr/lib/systemd/system 目录下创建 nginx.service 文件
1
vim /usr/lib/systemd/system/nginx.service

文件添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=nginx web service
Documentation=http://nginx.org/en/docs/
After=network.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PrivateTmp=true

[Install]
WantedBy=default.target

注意:可执行文件 nginx 根据自己的路径进行修改,以及 .conf 配置文件和 .pid 文件的路径。这份内容是基于默认安装目录的。

  • 添加完成后,如果权限有问题需要进行权限设置,没有则忽略这一步
1
chmod 755 /usr/lib/systemd/system/nginx.service
  • 使用系统命令来操作 Nginx 服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 启动 Nginx
systemctl start nginx

# 停止 Nginx
systemctl stop nginx

# 重启 Nginx
systemctl restart nginx

# 重新加载配置文件
systemctl reload nginx

# 查看 Nginx 状态
systemctl status nginx

# 开机启动
systemctl enable nginx

# 关闭开启启动
systemctl disable nginx

全局命令配置

前面我们介绍过 Nginx 安装目录下的二级制可执行文件 nginx 的很多命令,要想使用这些命令前提是需要进入 sbin 目录下才能使用,很不方便,如何去优化,我们可以将该二进制可执行文件加入到系统的环境变量,这样的话在任何目录都可以使用 nginx 对应的相关命令。具体实现步骤如下:

方法一:

  • 修改 /etc/profile 文件
1
2
3
4
vim /etc/profile

# 在最后一行添加
export PATH=$PATH:/usr/local/nginx/sbin

可执行文件 nginx 的路径根据自己的路径修改,这里是默认路径。

  • 使之立即生效
1
source /etc/profile
  • 任意位置执行 nginx 命令,测试成功
1
2
[root@master ~]# nginx -v
nginx version: nginx/1.21.6

方法二:

  • 将可执行文件 nginx 拷贝一份到 /usr/bin 目录下
1
cp /usr/local/nginx/sbin/nginx /usr/bin
  • 任意位置执行 nginx 命令,测试成功
1
2
[root@master ~]# nginx -v
nginx version: nginx/1.21.6