前言
nginx复习,深入学习location与反向代理
一开始学的时候就对于location与反向代理末尾/的问题烦恼了很久才有苗条,现在过了一段时间再来复习复习
关于location
location,路由规则,是配置再http的server中的,实际上就是匹配URI
location <sign> <path> {}sign
sign 含义 = 精确匹配(差一点都不行) ^~ 优先匹配,不支持正则 ~ 区分大小写的正则匹配 ~* 不区分大小写的正则匹配 !~ 区分大小写不匹配的正则 !~* 不区分大小写不匹配的正则 空格 普通匹配,不支持正则 优先级
=一旦匹配到立刻停止匹配^~匹配最长的,即全部扫描一遍~,~\*,! ~,!~\一旦匹配到立刻停止匹配空格匹配最长的,即全部扫描一遍
path
关于path末尾有无
/的问题- 如果path末尾有
/如/app/,那么只能匹配/app - 如果path末尾没有
/如/app,那么可以匹配/app,也可以匹配/app/xxx
关键点
虽然path看上去是目录的层级,但是匹配起来不同于目录只需要 开头匹配
例如访问 http://yk.cn/ABVFGTYHJNBG ,location匹配规则只有
/和/A,它会匹配到/A!这个问题当初磨了好久才明白,这个匹配规则只要URI开头匹配上就行,后面不管(当然,请注意符号的优先级)- 如果path末尾有
关于proxy_pass
这是反向代理的核心配置,我的理解就是nginx修改原始请求去访问后端服务器,然后返回给客户端
这里面存在着两个很重要的问题
- nginx是如何修改原始请求的?
- 后端返回的请求如何给真实客户端?
nginx修改原始请求(proxy_pass)
如图,客户端请求的URI(/app/index.html?user=yk)会由proxy_pass进行处理
location路径: /app/
剩余原始URI: index.html?user=yk
| proxy_pass | 抽象意义 | 转发URL |
|---|---|---|
| http://192.168.1.1/ | server_name后有/就已有location路径,加剩余URI | http://192.168.1.1/index.html?user=yk |
| http://192.168.1.1/data/ | server_name后有/就已有location路径,加剩余URI | http://192.168.1.1/data/index.html?user=yk |
| http://192.168.1.1./data | server_name后有/就已有location路径,加剩余URI | http://192.168.1.1./dataindex.html?user=yk |
| http://192.168.1.1 | server_name后无/就无location路径,加全部URI | http://192.168.1.1/app/index.html?user=yk |
这个我也研究了不少时间,才搞明白,可以发现第三个实际上是错误的,也就是说proxy_pass尽可能末尾加/ ,这里给出我之前参考的博客nginx中proxy_pass的作用以及注意事项(不看后悔一生)_proxypass域名-CSDN博客
讲的确实不错,给了我很多启发,但是对于为什么是这样加URI,我做出了更好的解释,就是server_name后有无/,有就加剩余URI,没有就加全部URI,这样记起来更加方便
需要注意,不要与location的匹配弄混了,到了proxy_pass就已经匹配到location了,换句话说就是和location没有关系了
nginx修改后端返回的请求URL(proxy_redirect)
proxy_redirect指令用于重写后端返回的 Location 和 Refresh 响应头,确保客户端重定向时指向代理服务器而非后端服务器,一般用于后端响应301/302跳转
简单介绍一下流程,后端301/302响应,要重定向,给nginx发了一个重定向的URL,nginx就要返回给客户端,但是这个URL客户端是无法使用的,因为它无法直接访问后端服务器,nginx就要修改URL,把他改成nginx的指定的location(有反向代理到该后端服务器的),这样再经过一遍反向代理就行了
这里面也有两个重要的问题
- nginx是怎么修改后端响应的URL的?
- 客户端为什么能够凭这个URL进行重定向?
nginx修改后端响应的URL
这是通过proxy_redirect实现的
proxy_redirect有3个值: default/off/<regex\>
proxy_redirect default
- 当后端返回相对路径(如
Location: /new_path)时,Nginx 会将其拼接为原请求的协议+Host(server_name)+相对路径,例如http://ym.cn/new_path。 - 当后端返回绝对路径(如
Location: http://backend:8080/new_path)时,Nginx 会尝试将backend:8080替换为当前代理服务器的Host(server_name)。
注意,默认拼接会丢弃原始请求的路径和参数,但是一般重定向是需要携带原来的参数的(这个具体看后端需不需要),此时就需要使用显式定义proxy_redirect
# 替换后端绝对路径(这里替换目标可以省略Host) # http://backend:8080/index -> http://yk.cn/app/index proxy_redirect http://backend:8080/ /app/; #等价于(server_name yk.cn) proxy_redirect http://backend:8080/ http://yk.cn/app/; # 替换后端相对路径 # /new_path/index --> http://yk.cn/app/new_path/index proxy_redirect /new_path /app/new_path;# 动态拼接,保留原始参数 # /new_path --> http://yk.cn/new_path?user=yk proxy_redirect /new_path /new_path$is_args$args;可以发现这些配置都是只能替换开头的,固定重定向(后端只有一个重定向响应)可以通过加长替换目标解决,但是如果后端有多个重定向呢?多个重定向的同时还需要添加参数
最简单就是使用多个proxy_redirect解决,但是这样会显得臃肿,更高效的处理方式是使用正则匹配(基础正则)
proxy_redirect ~^/new_path(/?.*)$ /new_path$1$is_args$args;- 当后端返回相对路径(如
客户端使用重定向URL
客户端原始请求URL为
http://ym.cn/data/index?name=user,后端重定向,nginx修改后返回的URL(假设协议为http,携带参数)为http://ym.cn/new_data/index?name=user可以发现,其实访问的域名都是一样的,因为proxy_redirect修改的时候使用原始协议+本server(这里就是ym.cn)+返回URI+原始参数(参数需要显式拼接)进行拼接,访问后就是重新走一遍反向代理流程,与初次请求除了反向代理的URI不同外没有任何区别
后谈
这里我其实还有一个问题没有解决,就是关于动态拼接时如果替换的只是开头的一部分,那么是否会在末尾添加参数?
个人认为不会(纯猜测,没有验证过!!!),因为动态拼接时也可以手动添加参数,如果手动加了参数,nginx如何判断应不应该加参数?应该没有判断,根本不会自动拼接(猜测)
# /new_path/index --> http://yk.cn/new_path?user=yk(index) ??
#这里直接使用捕获组添加参数
proxy_redirect /new_path /new_path$is_args$args;还是说必须使用捕获组才可以将这个index移至/new_path后,这个暂时无法验证,但是尽可能匹配全部URL可以避免犯错
总结
- location末尾加
/ - proxy_pass末尾加
/ - proxy_redirect尽量替换全部的响应URL而不是只替换开头的URL
这些问题我搞明白至少花了一天的时间,有的时候最难的就是一些细节,一旦去验证就要花大把的时间
