Nginx 代理:掌握 proxy_pass 的正确姿势
Nginx是一款高性能的HTTP和反向代理服务器,以其稳定性、丰富的功能集、简单的配置文件以及低资源占用而闻名。 Nginx的主要应用场景包括:
- 静态资源服务器:Nginx可以高效地提供静态文件,如HTML、CSS和JavaScript文件。
- 反向代理:Nginx可以作为前端代理,将客户端的请求转发到后端的服务器,并将后端服务器的响应返回给客户端。
在Nginx的反向代理配置中,一个看似不起眼的字符可能会对请求的转发路径产生重大影响。本文将探讨代理地址设置中的几种情况及其对应的行为。
Nginx中如何配置反向代理
Nginx能够作为一个反向代理来终结来自于客户端的请求,并且向上游服务器打开一个新的请求。
在nginx中这个功能需要使用location指令来实现。
location指令可以用在虚拟服务器server部分,并且意味着提供来自客户端的URI或者内部重定向访问。除少数情况外,location也可以被嵌套使用,它们被作为特定的配置尽可能地处理请求。
location定义如下:
location [modifier] uri {...}
modifier会影响location的处理及优先级。
或者是命名location:
location @name {...}
命名location仅对内部访问重定向,在进入一个location之前它会保留被请求的URI部分。命名location只能够在server级别定义。
location修饰符及处理方式:
- = 使用精确匹配并且终止搜索。即一旦匹配上使用=修饰的location,下面的其它location配置就无效了。
- ~ 区分大小写的正则表达式匹配。
- ~*不区分大小写的正则表达式匹配。
- ^~ 如果该location是最佳的匹配,那么对于匹配这个location的字符串不再进行正则表达式检测。注意,这不是一个正则表达式匹配----它的目的是优先于正则表达式匹配。
当使用^~修饰符时,即使有其他更精确的匹配,也会选择这个location块。在这种情况下,即使有其他匹配规则,URI也不会被替换。
易错点澄清:何时替换uri?
为了更好地响应客户端请求,可以根据请求的URI、http客户端参数或者一些约定的逻辑进行拆分。通过代理服务器,请求的原始URL中的任何部分都能够以这种方式进行转换。
其中,把请求代理到上游服务器配置中,最重要的是proxy_pass指令。该指令用来设置被代理服务器的地址,可以是主机名称、IP地址加端口号等形式。
其语法结构为:
proxy_pass URL;
其中,URL为要设置的被代理服务器的地址,包含传输协议、主机名称或IP地址+端口、URI等要素。传输协议通常是http或https。指令同时还接受以unix开始的UNIX-domain套接字路径。例如:
proxy_pass http://127.0.0.1:8080;
proxy_pass https://www.ai-as.net/newUri;
proxy_pass http://unix:/tmp/aias.socket:/uri/;
明白了proxy_pass指令的使用,我们来解释大家经常讨论的一个问题,就是proxy_pass指令的URL变量末尾是否加斜杠“/”的问题。
先说结论:
如果proxy_pass后面的URL包含URI,
带有URI部分的proxy_pass指令
将会使用该URI替代request_uri中与location 指令uri相同的部分。
后面示例有详细说明。
URL中没有包含URI的指定示例:
location /uri {
proxy_pass http://www.ai-as.net;
}
其它情况在nginx看来,
proxy_pass后面的URL都包含了URI,即使只加了一个斜杠“/”,
即斜杠“/”也是URI。
例如,下面例子中的/uri,在请求传递到上游服务器时将会被替换为newUri。
location /uri {
proxy_pass http://www.ai-as.net/newUri ;
}
以nginx/1.26.0为例,proxy_pass中URL有四种情况,nginx收到请求后,重新发起请求的URL如下图所示:
场景1:proxy_pass http://www.ai-as.net;
1.1浏览器请求的URL: http://127.0.0.1/proxy_pass_no_dir_without_slash/
1.2Nginx中的conf/nginx.conf中的配置:
location /proxy_pass_no_dir_without_slash/ {
proxy_pass http://www.ai-as.net ;
}
1.3Nginx重新发起请求的URL: http://www.ai-as.net/proxy_pass_no_dir_without_slash 原因:proxy_pass的URL http://www.ai-as.net上没有包含资源路径URI。
1.4示例:
% curl http://127.0.0.1/proxy_pass_no_dir_without_slash/
nginx debug日志:
场景2:proxy_pass http://www.ai-as.net/;
2.1浏览器请求的URL: http://127.0.0.1/proxy_pass_no_dir_with_slash/
2.2Nginx中的conf/nginx.conf中的配置:
location /proxy_pass_no_dir_with_slash/ {
proxy_pass http://www.ai-as.net/ ;
}
2.3Nginx重新发起请求的URL: http://www.ai-as.net/
原因:proxy_pass的URL http://www.ai-as.net上包含资源路径“/”。
2.4示例:
curl http://127.0.0.1/proxy_pass_no_dir_with_slash/
nginx debug日志:
场景3:proxy_pass http://www.ai-as.net/dir;
3.1浏览器请求的URL: http://127.0.0.1/proxy_pass_dir_without_slash/
3.2Nginx中的conf/nginx.conf中的配置:
location /proxy_pass_dir_without_slash/ {
proxy_pass http://www.ai-as.net/dir;
}
3.3Nginx重新发起请求的URL: http://www.ai-as.net/dir
原因:proxy_pass的URL http://www.ai-as.net上包含资源路径“/dir”。
3.4示例:
curl http://127.0.0.1/proxy_pass_dir_without_slash/
nginx debug日志:
场景4:proxy_pass http://www.ai-as.net/dir/ ;
4.1浏览器请求的URL: http://127.0.0.1/proxy_pass_dir_with_slash/
4.2Nginx中的conf/nginx.conf中的配置:
location /proxy_pass_dir_with_slash/ {
proxy_pass http://www.ai-as.net/dir/ ;
}
4.3Nginx重新发起请求的URL: http://www.ai-as.net/dir/
原因:proxy_pass的URL http://www.ai-as.net上包含资源路径“/dir/ ”。
4.4示例:
% curl http://127.0.0.1/proxy_pass_dir_with_slash/
nginx debug日志:
5、使用到的nginx/nginx.conf
% cat conf/nginx.conf
#user nobody;
worker_processes 1;
error_log logs/error.log debug;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
location /proxy_pass_no_dir_without_slash/ {
proxy_pass http://www.ai-as.net ;
}
location /proxy_pass_no_dir_with_slash/ {
proxy_pass http://www.ai-as.net/ ;
}
location /proxy_pass_dir_without_slash/ {
proxy_pass http://www.ai-as.net/dir;
}
location /proxy_pass_dir_with_slash/ {
proxy_pass http://www.ai-as.net/dir/ ;
}
# error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
}
查看的日志文件:logs/error.log
% tail -f logs/error.log
2024/05/19 23:23:59 [debug] 66640#0: *38 http keepalive handler
2024/05/19 23:23:59 [info] 66640#0: *38 kevent() reported that client 127.0.0.1 closed keepalive connection
2024/05/19 23:23:59 [debug] 66640#0: *38 close http connection: 4
2024/05/19 23:23:59 [debug] 66640#0: *38 event timer del: 4: 13660789921
2024/05/19 23:23:59 [debug] 66640#0: *38 reusable connection: 0
2024/05/19 23:23:59 [debug] 66640#0: *38 free: 0000000000000000
2024/05/19 23:23:59 [debug] 66640#0: *38 free: 00007FD0E8105850, unused: 152
2024/05/19 23:23:59 [debug] 66640#0: timer delta: 1
2024/05/19 23:23:59 [debug] 66640#0: worker cycle
2024/05/19 23:23:59 [debug] 66640#0: kevent timer: -1, changes: 0
留个小问题: 你的nginx error.log日志中示例中的debug日志吗?
小结
Nginx作为一款功能强大的反向代理服务器,其location和proxy_pass指令提供了灵活的配置选项。然而,在使用过程中,需要注意一些易错点,如proxy_pass中的斜杠问题、多次重写以及特殊场景下的URI不替换。通过仔细检查和理解这些细节,可以避免常见的配置错误,确保Nginx正确地转发请求。
巩固小作业
下面的这个location配置效果是一致的吗? 配置1:
location / {
proxy_pass http://www.ai-as.net;
}
配置2:
location / {
proxy_pass http://www.ai-as.net/;
}
REFERENCE
https://www.bilibili.com/video/BV1GJ4m187Em/?vd_source=028ee6e6a7217b4a4237d439b7fd348d
通过debug日志定位问题 https://time.geekbang.org/course/detail/100020301-79641