nginx(八十一)rewrite模块指令再探之(三)重定向

一  return和rewrite重定向再探

 ①  前言

多种重定向跳转方式的差异

nginx与Location响应头细节探讨

本为'不涉及'讨论如下的'绝对'重定向

1) return 301 http://www.wzj.com:6443/url?name=wzj

2) rewrite ... http://www.wzj.com:6443/url 

2) rewrite ... http://www.wzj.com:6443/url  permanent

②  return和rewrite相对重定向探究

1) return 比 rewrite 效率更'高',但是'rewrite'可以利用'正则'完成更复杂功能

  补充: 'rewrite'会隐式的带查询参数,可以通过'?'进行调整;但是'return'必须显示指定$args

2) 重点: 只探究'相对'路径时,哪些因素影响重定向'Location'形式?

  思考方向:'$scheme、$host、$port、$args'

  涉及手段:'return code'、'rewrite ... ...["不涉及协议"] permanent|redirect'

  强调:建议'显示'指定'全路经'的重定向

3) 补充: 暂时'不考虑'以下场景

  [1]、proxy模块涉及'proxy_redirect',以及后端'30(1|2|7|8)',及自定义Location响应头

  [2]、'目录资源'导致301重定向: 默认'目录资源'nginx'301'重定向;'try_files $uri/'导致

port_in_redirect off的两个应用场景   nginx 的port_in_redirect 问题解决

1) Location'响应头'常见的组成: 

  [1]、$scheme://$host:$port$request_uri --> "全路径",最'常见'的形式

  [2]、$request_uri                      --> "带查询参数,相对路径"

  [3]、$uri                              --> "纯uri"

备注1: 实际可以加上'#'锚点数据,但是一般'不指定,最终返回的Location响应头是相对的还是绝对'

备注2: '$status'和'Location'共同作用'重定向'

2) absolute_redirect['总开关'] on  

  备注: relative url没有'协议'、'主机名'、'端口号'

3) server_name_in_redirect['host来源'] off

  需求:可以为被代理服务器发出的'相对重定'向增加'主机名'

  存在的'问题'1: 原始是通过'ip正向代理访问'的,可能'域名'无法解析、防火墙'不通'

  存在的'问题'2: 获取的'$server_name'是一个带正则的'字符串',客户端'无法'解析'所谓'域名

4) port_in_redirect['port来源'] on  --> "是否携带端口"、"携带谁的端口"

  1、默认的情况下,是会在重定向的url后'自动添加nginx 处理server块当前站点监听'的端口

  2、会对服务器造成'潜在'的威胁,'后端端口暴露'出来,可能会被人直接对这个端口进行诸如CC类攻击

5) 补充'说明'

  1、这几种'配置'方式不能显示随心所欲的'指定'

  2、通过改变'port'、'host',可能导致'重定向后'匹配一个新的'server'块

6) 本次关注'$schme',经常会出现'如下报错':

400 Bad Request The plain 'HTTP' request was sent to 'HTTPS' port

根因: 'nginx'对应的'端口'监听的是'https',但是却用'http'访问

  场景1、nginx正常'配置 https 443',但是用户错误'http://www.wzj.com:443'访问

  场景2、nginx的'proxy_pass'是'https',但是后端只支持'http',导致转发'报错'

  $scheme是'http',但是'listen port'为443

7) 浏览器如何处理'多个'Loation响应头 --> "待验证"

案例1: 'rewrite ^ kafka redirect;' 和 'rewrite ^ /kafka redirect;' 等价

备注: 客户端['浏览器'、'curl']根据'上次'请求的'scheme、host、port'+'相对url'发起请求

案例2: 多个重复'Location'响应头,浏览器和'curl'命令行'处理行为'不一样

③  The palin HTTP request was sent to HTTPS port 

目的: 必须明确'原理',也即'为什么'重定向后'https'变成了'http'?

openssl非交互的生成自签名证书   利用openssl -config生成自签名证书

需求:openssl'非交互'生成'私钥'和'证书'

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \

-keyout server.key -out server.crt -subj \

"/C=CN/ST=HeNan/L=XinYang/O=devops/OU=unicorn/CN=ssl.wzj.com"

说明: 对于'自签名'证书,'curl'访问必须使用'(-k|--secure)'声明

补充: https非'443[6443]'端口的时候,必须指定'port_in_redirect on','不要修改'默认行为

报错原因:'HTTP请求'被发送到'HTTPS端口'

排错手段: 看'配置文件'、看'请求方式'是否错误
​
#版本1.15.0及以下
listen 443;
ssl on;
 
#版本1.15.0以上
listen 443 ssl;
listen 80;      #这种可以'不同端口'监听同一服务 

问题点: redirect涉及https跳转到http,这是一个'$scheme'非期望原因之一

Kong X-Forwarded-Port 8443 问题

base标签导致The palin HTTP request was sent to HTTPS port

nginx不同版本ssl配置导致The plain HTTP request was sent to HTTPS port

proxy_paas协议错误导致The plain HTTP request was sent to HTTPS port

++++++++++  "不谈网站架构和需求都是流氓"  ++++++++++

问题现象: 请求'proxy1.wzj.com/kafka'

报错: 'Client sent an HTTP request to an HTTPS server.'

网站架构:用户 --> 'http' --> '反向代理' --> 'proxy_paas:http' --> 'nginx' 

案例'模拟'场景: proxy_paas http://ssl.wzj.com:6443 -->但是'后端'只支持'https'协议

补充现象: 被'代理'服务'报错'502,注意观察'error.log'的日志

nginx访问https跳转到http的解决方法 

++++++++++++++ "涉及proxy模块解决$scheme的问题" ++++++++++++++

解决: 通过'nginx'访问https'跳转到http'的解决

方式1:需要'同时修改'nginx配置和程序 --> '不推荐'

1) 在nginx代理中增加一个'proxy_set_header X-Forwarded-Proto $scheme'

 说明: 标志'用户请求'是http还是https

2) 后端获取header决定'跳转'到http/https页面

方式2: nginx代理中配置'proxy_redirect'

proxy_redirect http:// $scheme://; 或 proxy_redirect http:// https://;

场景: nginx的'proxy_pass'是'https',但是后端只支持'http',导致转发'报错'

+++++++++ '题外话' +++++++++

1) Jenkins根据请求头'proxy_ser_header X-Forwarded-Port $server_port'确定重定向端口

2) mesher优先根据'proxy_ser_header X-Forwarded-Port $server_port'来进行'端口转发'

案例2: 不同的'url'访问,'proxy_redirect default'行为导致重定向的'协议$scheme'不一样

测试说明: 由于'proxy_redirect'只剩下'Location: /wzj/kafka',经过'$scheme'等作用

案例3: '上游'Location传递错误的'$scheme',需要'proxy_redirect'对应修改  --> "省略"

遗留: 对于'请求[流]链路'复杂的,需要'多服务协作',或者'抓包定边界'

举例: 如果'上游服务器是nginx',其配置'absolute_redirect off';作为'proxy'的nginx处理?

 你真的理解rewrite吗    章神关于if和set解读

error_page 497 https://$host:$server_port$request_uri;

场景: nginx在'SSL阶段'对自定义的'49x'的特殊处理

nginx同一个端口同时配置支持http和https协议

同一个端口同时支持http和https

利用meta的刷新作用将http跳转到https

三  if和content handler的探究

题外话: 之所以要'看原版英文'解读

  1) '一'是'原汁原味',避免层层传播产生'歧义'

  2) '二'是'掌握'单词',学习计算机'术语'描述

①  if工作原理 

②   知识铺垫:CONTENT阶段模块的执行顺序 

 淘宝对CONTENT阶段的解读     nginx模块执行顺序

关注点:'content handle'、'NGX_HTTP_CONTENT_PHASE'阶段涉及的'模块'和'指令'

1) nginx 'CONTENT'阶段中'默认'服务模块  --> 'static content'  --> '兜底'

  [1]、当一个 location 中'未使用'任何 content 阶段的指令,没有模块注册'内容处理程序'时

  [2]、nginx 一般会在 content 阶段安排'三'个这样的'静态资源服务'模块

  [3]、'依次'是 'index' 模块、'autoindex' 模块、以及 'static' 模块

    1、index模块涉及'index'指令

    2、autoindex模块
   
    3、static模块涉及'root'、'alias'指令

默认'CONTENT'阶段起作用的时机: 
  
    1、location 中'没有'使用运行在 CONTENT 阶段的模块指令

    2、也即'没有'模块'显示注册'这个 location 的"内容处理程序(content handler)"

    3、处理权便'自动'落到了在 CONTENT 阶段'垫底'的那 3 个'静态'资源服务模块

2)  哪些'模快'会显示在CONTENT阶段'注册'内容处理程序? 这些CONTENT阶段模块的执行'顺序'?

  [1]、proxy模块、fastcgi模块、uwsgi模块在 CONTENT 阶段执行

  备注: 一般关注'proxy_pass'指令 

  补充: 将客户端过来的请求体'如果有的话'以'相应协议[转换]'完整的'转发'到后端服务进程

  [2]、ngx_echo 模块

  备注: 涉及'echo'、echo_exec、echo_location

  [3]、ngx_lua模块

  备注:涉及'content_by_lua'、'block_by_lua'等

3) nginx 模块在'向 content 阶段注册配置指令'时一些'原理细节'

 [1]、'本质'上是在当前的'location'配置块中'注册'所谓的"内容处理程序(content handler)"

 [2]、每一个 location 只能有'一'个"内容处理程序"

 [3]、因此,当在 location 中同时使用'多个模块的 content 阶段指令'时

 [4]、只有'其中一个模块'能成功注册"内容处理程序",即只能'解析一条'相同的指令

3) 通过'案例'重点讨论'多'个模块同时注册 content 阶段指令?

  注意: echo 、'proxy_paas'、'content_by_lua'同时出现的的'优先级',与'顺序'有关吗?

  强调: 应当'避免'在同一个 location 中使用'多个模'块的 content 阶段指令

content和filter

③  set和proxy_pass杂谈

could not be resolved (3: Host not found)

+++++++++++++  "小结"  +++++++++++++

1) if block模块'没有'content handler

2) 所以if block模块'继承'了'外部'的`proxy_pass`

3) 最后在'if模块'里执行了这个`proxy_pass`,而不是在'外部'执行的`proxy_pass`

案例来源

④  content handler不同层级

说明: 对比'上面案例'进行理解

  1) 此时'if block' 已经有了 'echo [content handler]';

  2) 不会'继承'外层的'proxy_paas'这个'content handler';

⑤  if中使用break

说明: '对比'案例理解

++++++++++++++++  "这个结论待验证,暂时遗留"  ++++++++++++++++

1) proxy模块在嵌入的'多个location间'的配置的'继承'扮演了关键的角色  --> '上面案例'

2) 但是'其它模块[ngx_echo]'可能不会继承location'内嵌'的content handler -->  '???'

备注: 事实上,'大多数'content handler模块,包括upstream都'不支持'继承    -->  '???'

⑥  content handler相同层级

'临时'结论: 

  1) 同一'层级'的'content handler',由于只会'only 有一个 content handler执行'

  2) 最后的'content handler 覆盖'前面的

  备注: 具体'哪一个模块的指令'会胜出是'不确定'的

  3) 常见'content 指令': 'content_by_lua'、'echo'、'proxy_pass'

  4) 补充:echo 可以'使用多次','content_by_lua'、'proxy_pass'不行

    location /wzj {
      echo hello;
      echo world;
    }

  5) 最佳实践: '禁止'在同一个 location 中使用'多个模块的 content 阶段'指令

ngx_header_more模块的more_set_headers指令也会继承if块隐式创建的location

⑦  rewrite last和break标志位

1) last: 停止'当前'这个请求,并根据rewrite匹配的规则'重新'发起一个请求

  备注: 跳过原始请求'location_rewrite'等后续阶段,'新请求'又从'find_location'开始执行

  补充: 新请求'保留'了原始请求的'什么'信息?

2) break:相对last,break并'不会'重新发起一个请求

  备注:只是'跳过'当前的'rewrite阶段',并执行本请求'后续'的执行'阶段'

1) "内部跳转[internal]"本质上其实就是把当前的请求处理阶段'强行倒退'到 'find-config' 阶段

备注:倒退回find-config阶段动作不是发生在rewrite阶段,而是发生在后面的'post-rewrite'阶段

2) 以便'重新'进行请求 'uri[解码后]' 与 'location 配置块'的配对

3) 如果在'server'配置块中直接使用 rewrite 配置指令对请求uri进行改写,则不会涉及'内部跳转'

原因:因为此时uri改写发生在server-rewrite阶段,'早于'执行location配对的find-config阶段

++++++++++ "404的真正原因" ++++++++++

1) 404的原因'不是'没有定义对应的location,而是没有找到'对应的资源'

2) 404的页面是'谁定义的'?

  [1]、nginx'自身'的默认格式还是'nginx 自定义[nginx配置有没有对404报错的处理]'

  [2]、如果'$upstream_status'为404,则是'后端服务'的

3种语言对nginx配置文件进行格式化    nginx配置文件格式化    变量漫谈

nginx的access日志打印16进制原因

⑧  扩展:了解即可

location /test {
    echo_before_body "before...";
    proxy_pass http://127.0.0.1:8080/foo;
    echo_after_body "after...";
}

location /foo {
    echo "contents to be proxied";
}

请求: /test'

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值