Skip to content

Nginx:请求匹配、路径映射与代理语义 ​

先明确主配置、conf_prefix、Host/SNI 和上游地址,再对照配套示例理解同一请求的状态、上游目标与访问日志。

前置是请求与配置上下文。本篇按一次请求的决策顺序解释,适用1.28/1.29共同指令,不采用更新手册中才出现的predicate location等扩展。以下为根据手册推导的行为,需在目标实例验证。

地址端口、server_name与默认站点 ​

Nginx server_name 匹配不是先扫描所有域名:先按目标地址和listen端口找候选server,再按Host选择名字;没有名字命中时用该监听的default_server,未显式配置时通常是首个server。下划线不是拒绝任意域名的魔法名字,示例依赖default_server加return 404;另一个server才接收lab.local。server_name不是域名白名单鉴权,也不保证Host绝对等于最初TLS的SNI。

名字匹配可分精确名、前置通配符、后置通配符、按配置次序的正则名。普通教学站优先明确精确名,避免正则歧义。TLS证书在HTTP请求头之前选择;默认TLS参数应放在共享http或正确的默认server,避免握手阶段取到另一组协议配置。官方请求处理、名字匹配

location匹配与规范化URI ​

Nginx location 匹配使用规范化路径,不用带查询串的原始目标。$request_uri保留原始请求URI及参数;$uri是当前规范化URI,内部重定向后可以改变。解码%XX、处理点段和合并相邻斜线会影响匹配,不能把原始字符串前缀检查当成路径授权。

对本课的扁平配置,先看精确匹配=,命中即结束;否则记住最长前缀;该前缀带^~时跳过正则;其他情况按书写顺序取第一个匹配正则,若无则用记住的前缀。普通前缀并非书写在前就优先。嵌套location会引入逐层查找,不能把本课口诀无条件套进任意嵌套树。

示例 /healthz精确命中,/api/status选^~ /api/,/.git/config选隐藏路径正则,/app/lesson选/app/。^~ /api/会跳过隐藏路径正则,因此后端必须自己约束它收到的路径;本例上游仅精确接受几个固定目标,不把路径变成本地文件。不要用一个^~公开目录假定外层正则仍保护其中的隐藏文件。静态发布目录只放公开产物,密钥与源码不得存入其中。核心模块location

请求阶段与内部重定向 ​

Nginx 请求阶段可按读取请求、server rewrite、location查找、location rewrite、访问控制、内容生成、响应过滤和日志理解。return可提前结束,proxy_pass负责代理内容,add_header参与输出;把它们按配置文件行号想成普通顺序程序会出错。访问控制与URI变更可能交互,需要针对最终处理位置考虑权限。

Nginx 内部重定向不向浏览器返回Location,而是用新URI重新选择location;index找到目录首页和try_files最后的URI都可能触发。rewrite ... last会重新找location,break通常停留在当前位置继续,不能随意替换。重复跳回原路径会达到内部重定向上限,常表现为500和cycle错误。外部301/308则由客户端发起另一次请求。本例仅用固定URI内部回退,不拼用户输入构造跳转。

root与alias的文件映射 ​

Nginx root 把完整URI接在目录后;root /tmp/m5-nginx/html时,/app/index.html对应/tmp/m5-nginx/html/app/index.html。Nginx alias替换匹配部分:概念上的location /images/配alias /srv/pictures/会把/images/a.png映射为/srv/pictures/a.png。二者不是换名字的同一种写法。

示例采用精确location = /download/guide.txt和固定alias文件,避免把任意后缀变成文件路径。若以后使用目录alias,location前缀与alias目录都应有配对的尾斜线,并审阅规范化、正则捕获、符号链接和权限边界;不要使用location /images配alias /srv/pictures/这种边界不一致的写法。公开目录不含私密文件,即使路径路由写错也不应暴露密钥。autoindex off只关闭目录列表,不阻止猜到文件名后的读取。

try_files与应用壳回退 ​

Nginx try_files按root或alias构建候选路径,依次检查是否存在;$uri/表示检查目录。最后的=404直接结束,最后的/app/index.html是内部重定向,会再次匹配location。本例普通静态路径缺失返回404,/app/下缺失路径回退应用壳,两者语义不同。

应用壳存在且路径正确才能结束回退;删除app/index.html可能产生循环而非普通404。生产中把缺失CSS、脚本也回成HTML会造成MIME错误和缓存污染,应给资源路径独立的=404策略。本例没有脚本构建产物,只用于展示路由;API的404保留后端语义,不做全站error_page 404 /index.html。核心模块try_files

proxy_pass URI替换规则 ​

Nginx proxy_pass URI含不含路径部分决定转发结果。location /api/配proxy_pass http://lab_backend/时,匹配到的规范化前缀/api/被/替换;/api/status?mode=brief预期转成/status?mode=brief。配proxy_pass http://lab_backend时没有URI部分,原始请求未经改写时通常完整转发/api/status?mode=brief;发生改写时则按手册传递变更后的完整规范化URI。不能把它简单背成“末尾斜线永远删除一级目录”。

正则或命名location通常不能确定可替换前缀,应使用不带URI的proxy_pass。带变量的proxy_pass又有不同的URI与解析器规则,不照搬本例推论。这里固定upstream名字,不以Host、查询参数或用户头拼上游地址,避免任意目标代理。外部重定向、后端绝对URL和Cookie路径另有proxy_redirect/proxy_cookie_path规则,剥掉入口前缀并不会自动修正所有应用URL。官方代理模块

请求头与响应头的信任边界 ​

Nginx proxy_set_header 修改发往上游的请求头,add_header修改回给客户端的响应头。单入口示例用$remote_addr覆盖X-Forwarded-For,不信任客户端自带的链;Host固定为lab-backend以适配这个演示上游。多级可信代理需要先定义可信来源、real_ip与应用解析规则,不能直接取X-Forwarded-For第一项做权限判断。

Nginx add_header 默认仅对规定状态码添加,always扩展到错误响应;传统继承规则是当前层有自己的add_header时不再继承上层这一组。若子location新增Cache-Control,应把必要安全头一起配置或复用专用include。示例把安全头和请求号放server层,子位置不另写add_header;代理隐藏上游同名请求号,避免重复字段。X-Content-Type-Options nosniff须与正确MIME配合,不代表完整浏览器安全策略。官方headers模块

超时、缓冲与重试 ​

Nginx proxy_read_timeout约束相邻读取间的空闲时间,proxy_send_timeout约束相邻写入间的空闲时间;它们不是整个请求的总时限。proxy_connect_timeout用于连接上游,连接、读头、读体分别可能失败。开启响应缓冲时Nginx先接收上游数据到内存或临时文件再发送客户端;request buffering则控制请求体转发方式,流式上传、SSE与大文件需要分别评估,不能用一个buffering off概括。

Nginx proxy_next_upstream决定何时尝试其他上游,而不是保证失败业务不执行。写请求超时可能是已提交但响应丢失;即便代理不重试,客户端也可能重放。示例proxy_next_upstream off明确关闭自动重试,教学上游只读。正式写接口仍需业务幂等键与结果查询;不要用non_idempotent强开重试掩盖故障。多上游时再依据幂等性设置次数、时间预算与故障策略。

推演练习与参考解答 ​

练习:把proxy_pass的尾部/删掉,哪一层出现404?把/app/index.html移走又会怎样?

参考解答:本例上游精确识别/status,收到/api/status便返回unknown target的404;upstream_status有404,而静态缺失通常没有上游地址。应用壳消失时try_files可能反复重定向,error.log出现cycle;应恢复正确文件或改为明确=404,不应增加重定向次数掩盖根因。

继续阅读