nginx(八十二)try
作者:mmseoamin日期:2023-12-11

一  try_files、content阶段、outfilter再探

①  哪些人适合阅读

适合: 拿nginx作为'静态服务器'网站'前端'人员必备,后端程序员可以'选择性的忽略'
备注: 该篇一个'阶段性'的总结,后续'不再'探讨该部分
补充: 按照'try_files'、'content'阶段的'模块指令'的'执行顺序'讲解
强调: 本文'不具有实际意义',只是试图从'应用层现象'来解读运行机制

①  相关博客系列

备注:学习要有一个'过程',不可能'一蹴而就',下面的博客记录了我的'成长史',某一个时刻就'豁然开朗'

root、alias、index、try_files关系指令再探

precontent阶段的模块学习 核心try_files指令

content阶段index和autoindex模块学习

content阶段static模块学习

filter模块处理

章亦春大神的nginx权威参考

③  content阶段涉及模块指令处理顺序

1) 本次'只探讨'try-files、content阶段,附加'filter'系列模块
2) content 阶段是'所有请求处理阶段中'最为'重要'的一个
特点: 因为运行在'这个阶段'配置指令一般都肩负着生成"内容(content)"并输出'HTTP 响应'的使命
强调: 当存在"content handler 内容处理程序"的指令时,'静态资源服务模块'并'不会'起作用
3) 绝大多数 'nginx 模块'在向 'content 阶段' 主动'注册配置指令'时
重点: 本质上是在'当前的 location 配置块'中'注册'所谓的"内容处理程序(content handler)"
常见: ngx_echo模块 ["echo指令"]、ngx_proxy ["proxy_pass"]
强调: 
 
  [1]、每一个 location 只能有一个"内容处理程序"
  [2]、当在 location 中同时'使用多个模块'的 content 阶段指令时
  [3]、只有'其中一个模块'能成功注册"内容处理程序"
4) 如果location中'没有'使用在 content 阶段'注册的'模块指令,也即没有'content handler'
  [1]、处理权便'自动'落到了在 content 阶段"垫底"的那 '3个'静态资源服务模块
  [2]、首先运行的 'ngx_index' 和 'ngx_autoindex' 模块
  特点: 如果'当前请求的uri'是'以 / 结尾',就会使用对应'index'和'autoindex'指令
  [3]、否则直接'弃权',将处理权转给了最后运行的 'ngx_static' 模块
  特点: ngx_static 模块根据 '(root|alias) 指令'将'请求$uri' 映射为'文件系统'路径 
  备注: 这个模块主要实现'服务静态文件'的功能,也是nginx作为'web服务器'的核心
  [4]、在确认'磁盘中'文件'存在'后,将它们的内容分别作为响应体输出,并自动设置'相应'响应头
  备注: 如果'文件不存在',则返回'404',这才是'资源不存在'
5) 有的'模块'指令是'默认提供的',有的模块'没有指定',则'跳过'这个模块
备注:有些指令作用'对应地方'也能改变'11个阶段'
6) nginx '变量'则经常扮演着在'指令间'乃至'模块间'传递'小份数据'的角色
7) ngx_index、ngx_autoindex、ngx_static模块执行'顺序'和'处理特点'对比:
  [1]、ngx_index 和 ngx_autoindex 模块都'只会'作用于那些$uri以'/结尾'的请求
  [2]、对于'不以/结尾的请求'则会直接'忽略',同时把处理权移交给'ngx_static 模块'
  [3]、而'ngx_static 模块'则刚好相反,直接'忽略'那些 $uri 以 '/ 结尾'的请求
    1、'真正'把相应的'内容(正常|异常)'发送出去,还是得靠这个 'ngx_static 模块'来完成
    2、所作的操作:即把'该文件的内容'作为响应体数据输出,并设置相应的'响应头'

④  try_files指令再探

知识铺垫

nginx(八十二)try,第1张

1)precontent'阶段':
  特点: 这个阶段'专门'用于实现标准配置指令'try_files'的功能,'不'支持nginx模块注册处理程序
  默认: '没有'指定'try_files',所以'precontent'阶段默认'不存在'
2) 语法: try_files file1 file2 ... '(uri|=code)'
  本文: 对'try_files'各种行为的探究
  备注: 支持'@location'这种'uri'
  补充: 这里的'file1 ,file(n-1)'等指的是'资源对象(file|directory)'
  最佳实践: try_files $uri $uri/ @wzj;
3) 细节点:'try_files'通过'alias|root'、'$uri'、'location'来判断文件系统对象是否存在
4) 探究案例:
 [1]、文件资源'存在'
 [2]、文件'不'存在,'目录资源'存在
 [3]、前'N-1'个都不存在,'uri|=code'进行匹配
 备注: 读者应该'注意'各个案例的'前提条件[上下文]'
 观察: 'debug日志'输出,并'分析'

nginx(八十二)try,第2张

案例1:try_files file '文件'存在时,使用'content handler'和'默认content'行为'对比'实验

nginx(八十二)try,第3张

+++++++++++++++++++++ "分割线" +++++++++++++++++++++
案例2: try_files file '目录资源对象'存在时
备注: 使用'content handler'和'默认content'行为'对比'实验

nginx(八十二)try,第4张

nginx(八十二)try,第5张

++++++++++++++++ "分析过程" ++++++++++++++++
1) nginx 在 try-files 阶段发现'第一个参数 /wzj' 对应的'文件 file'不存在
2) 就会转向检查'第二个参数 /wzj/'对应的'文件系统对象'
3) 由于'此目录'存在,并且'$uri'不是以'/'结尾的
细节: 此时nginx就会把当前请求'uri 改写'为第'二个参数的值(不带末尾/)',即$uri为'/wzj'
注意: try_files '原始'参数值是 /wzj/,但 try_files 会自动'去除末尾'的'斜杠/'字符
4) 由于是一个'目录'资源,nginx会进行'301重定向',并返回'Location: $uri/'
过程:此时$uri不是以'/'结尾,所以'index、autoindex'指令对应的模块不起作用,ngx_static处理
强调:
  [1]、实际'try_files 非internal命中',还是会向下'处理','index、autoindex'被忽略
  [2]、ngx_static会不是以'/'结尾的'$uri'映射的'文件'或'目录'做不同处理
备注: 理解上可以认为'约等于' 'rewrite ^ $uri/ permanent;'这种'相对'重定向
5) 接着'基于新的url',从客户端重新发起'重定向'请求
6) 在'try_files'由于'第二个参数 /wzj/'匹配判断是'目录',并且$uri是以'/'结尾
7) 此时转交到'ngx_index'模块,由于'此时映射的系统文件不存在',继续'ngx_autoindex'模块
8) 由于上述处理'/'结尾的'$uri'的两个模块没作用,'ngx_static'模块又处理不了以'/'结尾$uri
9) 此时返回'默认'内置的'403'的页面

nginx(八十二)try,第6张

说明: 下面的是'重定向'请求的'error.log'日志
备注: 同时给出了'$uri以/'结尾,并且'try_files directory'匹配的场景

nginx(八十二)try,第7张 

对比实验: 基于'案例2',前提'条件不变',修改'相对重定向'的策略,观察结果

双层nginx 转发下的 try_files 指令 301 返回错误的 location 地址    try_files导致301

nginx(八十二)try,第8张  

对比实验: 基于'案例2',前提'条件不变',使用'content handler'
强调: 
  1) 即使是'try_files $uri/'判断'目录资源存在',也会继续进行'content'阶段的处理
  2) 所不同的是经过模块注册的'content handler',还是'nginx内置默认的content'

nginx(八十二)try,第9张

对比实验: 基于'案例2',前提'条件不变',基于'Location 301'使用'exact location'

nginx(八十二)try,第10张

案例3: 最后一个'uri'起作用
备注: 
  1) '普通uri'         --> '$args'会丢失     -->   'rewrite ^ /java? last'
  2) '命名location'    --> '$args'不会丢失    -->  'rewrite ^ /java last'
1)  try_files 指令本质上只是'有条件'地改写当前'请求的 URI'
2)  这里说的"条件"其实就是文件系统上的'对象(file|directory)是否存在'
3)  当"条件"都不满足时,它就会无条件地发起一个指定的"内部跳转"
备注: 除了无条件地发起'内部跳转'之外, try_files指令还支持直接返回指定状态码'HTTP 错误页'

nginx(八十二)try,第11张

nginx(八十二)try,第12张

案例4: 最后一个'=code'起作用,观察'add_header'行为
说明: 从语义上来说,一般使用'404'返回指定状态码的 'HTTP 错误页'
思考: try_files可以指定'哪些'状态码? 
备注: 自己尝试过'401'、'301(没有Location)'、使用'20(0|6)'不报错但'阻塞'、'204'是ok
遗留: 如果指定'error_page' 相同'状态码'是否能补获?  --> "会"

nginx(八十二)try,第13张

nginx(八十二)try,第14张

⑤  index指令再探

思考1: 什么场景会触发'index'指令的执行?  --> 假定在一个'location'内
  前提: 没有其它模块注册'content handler',并且'$request_uri'以'/'结尾
  [1]、'$request_uri'中以'/'结尾,但'没有'指定'try_files'
    补充: 'try_files directory'映射系统资源'存在',虽然会改变'$uri',但是会去掉末尾的'/'
  强调: 上述是'触发 index'的充分不必要条件,并不意味着'index ...'能命中,只是'有机会'执行
  附加: ngx_index 和 ngx_autoindex 模块都只会作用于那些 '$request_uri以/结尾'的请求
思考2: index指令是'如何执行'的?
  [1]、依次判定'指定的首页文件'是否存在?  --> 基于'(root|alias)'判定
  [2]、如果全都'不存在'则进行'ngx_autoindex'模块,要不返回'文件列表',要么返回'403'
  [3]、如果存在'指定的首页文件',则基于该'$uri$index_value'发起'internal'内部跳转
    备注: 此时index 后续的'value'不再执行,并且'该次内部请求'的'后续阶段'也不再执行
问题1: 为什么我配置的'index'首页却不生效?
  原因: 
       1) 由于'location'优先级原因,进不到之前的'location'中导致异常
       2) index 指定的'首页'
  场景: 'ngx_index 模块'主要用于在'文件系统目录中'自动查找'指定的首页'文件
  思考: 如何才能真正'设置首页'与我们的'预期相符'呢?
案例:
  1)案例1:'index .. 匹配不上',观察行为   --> 才有可能轮到'autoindex'指令执行
  2)案例2:'index .. 匹配上',只使用一个'location'观察行为
  3)案例3:'index .. 匹配上','定义新的 location',观察匹配行为

merge_slashes 

nginx(八十二)try,第15张

 nginx(八十二)try,第16张

1) 'try_files directory[/kafka/]' 改变了'$uri',同时导致301 'Location $uri/'
2) 继续发起请求'/kafka/'进行'index index.html'匹配,'文件'存在
3) index改变'$uri'为'/kafka/index.html',此时不以'/'结尾,autoindex即使配置也无法处理
4) 最后交给'ngx_static'处理

nginx(八十二)try,第17张 

与'案例1'的对比实验: 由于'location的'优先级,导致非预期的'首页'

nginx(八十二)try,第18张 

与'案例1'的对比实验: index 配置的值以'/'结尾,同时'autonindex on'开启观察
 1) 第一次'index /wzj/' 查找系统资源为'目录'
   强调: 只有'index file'匹配,才会改变'$uri'
 2) 导致'$uri'变为'/kafka/wzj/',继续进行'internal'重定向行为
   官方: 只有'index (file|dir)'不存在,才会执行'autoindex',index'优先级'高
   
   大白话: 只要'index (file|dir)'匹配上,永远不会执行'autoindex'
       
 3) 通过'add_header或error.log'观察
   说明: 'index dir'场景

nginx(八十二)try,第19张 

nginx(八十二)try,第20张 

与上述的'对比'实验: 配置文件'不变',改变'前提条件',请求url'不变'
说明: 注意'autoindex on'执行的'条件',是一个'备胎'

nginx(八十二)try,第21张 

nginx(八十二)try,第22张