nginx常用代理配置

本篇博客仅以个人学习记录使用
原文链接: https://blog.csdn.net/lzc4869/article/details/79195357

反向代理

nginx 反向代理 就是说把跨域的 url 通过本地代理的方式,变成同域的请求,如此来解决跨域问题
该配置下 通过http://localhost/html5/路径下的文件去请求http://localhost/request/的相关接口就相当于去请求http://localhost:8888/login/相关的接口

最简反向代理配置

在 http 节点下,使用 upstream 配置服务地址,使用 server 的 location 配置代理映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
upstream my_server {
server 10.0.0.2:8080;
keepalive 2000;
}
server {
listen 80;
server_name 10.0.0.1;
client_max_body_size 1024M;

location /my/ {
proxy_pass http://my_server/;
proxy_set_header Host $host:$server_port;
}
}

通过该配置,访问nginx地址http://10.0.0.1:80/my的请求会被转发到my_server服务地址http://10.0.0.2:8080/。

需要注意的是,如果按照如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
upstream my_server {
server 10.0.0.2:8080;
keepalive 2000;
}
server {
listen 80;
server_name 10.0.0.1;
client_max_body_size 1024M;

location /my/ {
proxy_pass http://my_server;
proxy_set_header Host $host:$server_port;
}
}

那么,访问nginx地址http://10.0.0.1:80/my的请求会被转发到my_server服务地址http://10.0.0.2:8080/my。这是因为proxy_pass参数中如果不包含url的路径,则会将location的pattern识别的路径作为绝对路径。

重定向报文代理

即便配置了 nginx 代理,当服务返回重定向报文时(http code 为 301 或 302),会将重定向的目标 url 地址放入 http response 报文的 header 的 location 字段内。用户浏览器收到重定向报文时,会解析出该字段并作跳转。此时新的请求报文将直接发送给服务地址,而非 nginx 地址。为了能让 nginx 拦截此类请求,必须修改重定向报文的 location 信息。

1
2
3
4
5
6
location /my/ {
proxy_pass http://my_server;
proxy_set_header Host $host:$server_port;

proxy_redirect / /my/;
}

使用 proxy_redirect 可以修改重定向报文的 location 字段,例子中会将所有的根路径下的 url 代理到 nginx 的/my/路径下返回给用户。比如服务返回的重定向报文的 location 原始值为/login,那么经过 nginx 代理后,用户收到的报文的 location 字段为/my/login。此时,浏览器将会跳转到 nginx 的/my/login 地址进行访问。

需要注意的是,服务返回的重定向报文的 location 字段有时会填写绝对路径(包含服务的 ip/域名和端口),有时候会填写相对路径,此时需要根据实际情况进行甄别

1
2
3
4
5
6
location /my/ {
proxy_pass http://my_server;
proxy_set_header Host $host:$server_port;

proxy_redirect http://my_server/ http://$host:$server_port/my/;
}

上述配置便是将 my_server 服务的根路径下的所有路径代理到 nginx 地址的/my/路径下。当 nginx 配置只有一个 server 时,http://host:server_port前缀可以省略。

报文数据替换

使用 nginx 代理最牛(dan)逼(sui)的情况就是 http 响应报文内写死了服务地址或 web 绝对路径。写死服务地址的情况比较少见,但也偶尔存在。最棘手的是写死了 web 绝对路径,尤其是绝对路径都没有公共前缀。举个例子来说:

一般的 web 页面会包含如下类似路径:

1
2
3
4
/public:用于静态页面资源,如js脚本/public/js,样式表/public/css,图片/public/img等
/static:和/public类似
/api:用于后台服务API接口
/login:用于登录验证

对于这样的服务,可能的代理配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
location /my/ {
proxy_pass http://my_server/;
proxy_set_header Host $host:$server_port;

proxy_redirect / /my/;
}
location /login/ {
proxy_pass http://my_server/public;
proxy_set_header Host $host:$server_port;
}
location /public/ {
proxy_pass http://my_server/public;
proxy_set_header Host $host:$server_port;
}
location /api/ {
proxy_pass http://my_server/api;
proxy_set_header Host $host:$server_port;
}

由于 web 页面或静态资源内写死了类似的绝对路径,那么对于用户来说,通过页面内的链接进行跳转时,都会请求到 nginx 服务对应的路径上。一旦存在另一个服务也包含类似的路径,也需要 nginx 进行代理,那么矛盾就出现了:访问 nginx 的同一个路径下的请求究竟转发给哪一个服务?

要解决这个问题,必须在用户收到报文前,将报文的数据中包含的绝对路径都添加统一的前缀,如/my/public,/my/api,/my/login,这样 nginx 代理配置则可以简化为:

1
2
3
4
5
6
7
8
9
10
11
12
location /my/ {
proxy_pass http://my_server/;
proxy_set_header Host $host:$server_port;

proxy_redirect / /my/;
}
location /other/ {
proxy_pass http://other_server/;
proxy_set_header Host $host:$server_port;

proxy_redirect / /other/;
}

nginx 的 ngx_http_sub_module 模块提供了类似的报文数据替换功能,该模块默认不会安装,需要在编译 nginx 时添加–with-http_sub_module 参数,或者直接下载 nginx 的 rpm 包。

使用 sub_filter 对数据包进行替换的语法如下:

1
2
3
4
5
6
7
8
9
location /my/ {
proxy_pass http://my_server/;
proxy_set_header Host $host:$server_port;

sub_filter 'href="/' 'href="/my/';
sub_filter 'src="/' 'src="/my/';
sub_filter_types text/html;
sub_filter_once off;
}

上述配置会将/my/下的所有响应报文内容的 href=”/替换为 href=”/my,以及 src=”/替换为 src=”/my,即为所有的绝对路径添加公共前缀。

注意,如果需要配置多个 sub_filter,必须保证 nginx 是 1.9.4 版本之上的。

一个简单的完整示例

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
27
28
29
30
31
32
33
34
35
36
37
38
#user root owner;   #在mac中获取权限时需要将注释去掉
worker_processes 2; #启动进程,通常设置成和cpu的数量相等
events {
worker_connections 1024; #单个后台worker process进程的最大并发链接数
}
http {
include mime.types; #设定mime类型,类型由mime.type文件定义
default_type application/octet-stream;
#sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy)来输出文件,对于普通应用,
#必须设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime.
sendfile on;
keepalive_timeout 65; #连接超时时间
server {
listen 80; #侦听80端口
server_name localhost; # 定义使用www.xx.com访问
charset utf-8;
location / {
root html;
add_header Cache-Control no-store;
add_header 'Access-Control-Allow-Origin' '*';
index index.html index.htm;
}
location /request/ {
root /;
proxy_set_header Host $host; #请求主机头字段,否则为服务器名称。
proxy_headers_hash_max_size 1024; #存放http报文头的哈希表容量上限,默认为512个字符
proxy_headers_hash_bucket_size 128; #设置头部哈希表大小 默认为64
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ;
proxy_set_header Accept-Encoding "";
proxy_pass http://localhost:8888/login/; #请求替换地址 例如要请求http://localhost:8888/login/ 也就是请求http://localhost/request/
}
location ~ ^/html5/ {
rewrite /html5(.*)$ $1 break; #该指令根据表达式来重定向URI, 路径中存在/html5/的
expires -1; #缓存
root D:\html5; #前端静态文件物理路径 通过http://localhost/html5/来访问D盘html5下的文件夹
}
}
}

一个 React 类基于 History 模式路由的单页应用 nginx 配置

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
server {
listen 80;
# gzip config
gzip on;
gzip_min_length 1k;
gzip_comp_level 9;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";

root /usr/share/nginx/html;

location / {
# 用于配合 browserHistory使用
try_files $uri $uri/ /index.html;

# 如果有资源,建议使用 https + http2,配合按需加载可以获得更好的体验
# rewrite ^/(.*)$ https://preview.pro.ant.design/$1 permanent;

}
//示例模板
location /api {
proxy_pass https://ant-design-pro.netlify.com;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
}

location /api {
//实际配置中,带有前缀的api请求可能会需要配置rewrite,进行前缀的代理转发,实际应用中还可配置多个
rewrite ^.+api/?(.*)$ /$1 break;
proxy_pass http://192.168.50.55:9988;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
}

location /gis {
//实际配置中,带有前缀的api请求可能会需要配置rewrite,进行前缀的代理转发,实际应用中还可配置多个
rewrite ^.+gis/?(.*)$ /$1 break;
proxy_pass http://192.168.50.55:9988;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
}

...
}

server {
# 如果有资源,建议使用 https + http2,配合按需加载可以获得更好的体验
listen 443 ssl http2 default_server;

# 证书的公私钥
ssl_certificate /path/to/public.crt;
ssl_certificate_key /path/to/private.key;

location / {
# 用于配合 browserHistory使用
try_files $uri $uri/ /index.html;

}
location /api {
proxy_pass https://ant-design-pro.netlify.com;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
}