https://zhuanlan.zhihu.com/p/84539204

本文首发于公众号:js-mindmap

一,序言

“晚上九点,办公室里烟雾缭绕,工作进度依然没有什么进展。王二胖打开了十来个页面,一篇篇技术文章打开,关闭,Nginx不停的重启测试,在试过十来篇技术文章中的方案,经过两个小时的测试之后,王二胖终于找到了一个解决301错误跳转的可行解决方案。时间已经到了晚上十一点多。”

这样的场景,在我们的办公室里天天可见。互联网上有很多Nginx 301问题处理方案的错误解答,比如自动加斜杠,端口丢失,暴露内部端口号等,极多量的文章基本就是人云亦云,没有完全弄明白Nginx如何处理301状态码的。甚至对于一些关键性的配置信息的解释是完全错误的。本着源代码就是最正确的文档的原则,我阅读了一遍Nginx处理301问题的相关源代码。

最终发现,Nginx处理301 Moved Permanently的逻辑相当简单,只有六种分支。

二,Nginx的301状态码处理逻辑设计

让我们先看看Nginx的逻辑设计是怎么样的。HTTP协议中3xx开头的状态响应码都是表示重定向的响应。根据RFC的定义301 Moved Permanently302 Found303 See Other307 Temporary Redirect

301是永久重定向。如果使用Nginx作为HTTP 服务器,那么当用户输入一个不存在的地址之后,基本上会有两种情况,返回404状态码,或者301状态码。404 Not Found不做讨论,只是说下301 Moved Permanently的处理过程。

首先的关键问题是:页面重定向功能会在什么样的情况下被触发?

答案是:Nginx负责设置301 Moved Permanently状态码。但nginx.conf控制Nginx如何处理301 Moved Permanently状态码!换句话说,要不要进行页面重定向,和怎么重定向,完全是用户配置的结果!六种分支的选择完全是根据用户的配置来决定的。

根据源代码,Nginx的算法逻辑设计是分成两个部分的。第一部分是设置状态码,第二部分是对应状态码的实际响应处理。

Nginx和浏览器之间的通讯过程,比如一次正常的HTTP 访问过程,如下图。

从逻辑顺序上来说,Nginx会先设置好状态码,然后根据状态码来构造Response Header和Body,最后发送给浏览器,让浏览器渲染页面内容。

301 Moved Permanently状态码和200 OK状态码的处理过程是一致的。Nginx主动设置301 Moved Permanently状态码只有一种情况,当用户输入了一个url地址,最后的部分是一个文件目录。比如 http://www.test.com/index, Nginx在运行过程中没有找到index这个文件,但发现了index是个目录。于是本次访问的状态码就会被设置成301 Moved Permanently。

但注意!设置成301 Moved Permanently,不一定会导致浏览器重定向。从HTTP定义来说,导致浏览器触发重定向操作是因为浏览器收到了一个Locationresponse header;

让我们来看看Location的定义说明,很明确的说明了Location的作用。

The Location response header indicates the URL to redirect a page to. It only provides a meaning when served with a3xx (redirection) or 201(created) status response.

Nginx在Response Header里写入一个Location之后。浏览器可以根据Location来控制重定向过程。逻辑过程如上图。而且nginx.conf文件中的配置将影响到Location URL的生成方式。

三,nginx.conf中配置项的作用

nginx.conf文件在哪个环节起作用呢?答案就是设置Location之前。

一般情况下,我们会在nginx.conf中配置absolute_redirect ,server_name_in_redirect和port_in_redirect,以便到达个性化的重定向功能。这三个配置项的作用是很多人明白的,但对于逻辑顺序很少有文章提到。

四,重定向三配置

absolute_redirect ,server_name_in_redirect和port_in_redirect三个设置项中,根据Nginx的源代码中Response Header处理算法逻辑,Nginx能够控制重定向的关键配置项是:absolute_redirect,在整个Nginx代码中,absolute_redirect在控制在Response Header如何增加Location url。

absolute_redirect设置成On,则生成absolute url作为Location url。absolute_redirect设置成Off,则生成relative url作为 Location url。

absolute url是包含完整信息的url,比如http://www.test.com:8080/index/1.html 这样的URL地址relative url 则省略了服务器名字和端口号,比如 /index/1.html

因为relative url没有端口号,没有Host名字,所以absolute_redirect 设置On的时候,server_name_in_redirect和port_in_redirect两项设置才会起作用。

我花了点时间仔细阅读了Nginx的相关源代码,并画了流程图。

从以上逻辑过程, absolute_redirect,server_name_in_redirect,port_in_redirect 三项配置,共同控制了生成字符串 “Location: http://server_name:port/test/”的结果。

server_name_in_redirect 控制URL中的Server Name,也就是Host使用哪个值,port_in_redirect控制URL中的port是使用哪个值。通过这三个配置项,最终决定了Nginx返回给浏览器的Location内容。

用MindMap来表达就是

备注:header_in.server是nginx源代码中的变量,指向用户输入的url的服务器名字部分。根据以上脑图,我们可以很清楚的看到,最终我们只有六个分支结果。

五,案例分析

依据上面的分析,我们具体举个非常疑难的例子,看看如何解决问题。

1,问题:http://www.test.com:888/index被错误的重定向至http://www.test.com/index/这种情况多见于使用NAT做端口映射,或者是用容器来运行Nginx。内部服务器或者容器中nginx监听的是80端口号

而我们的期望答案是:http://www.test.com:888/index重定向至http://www.test.com:888/index/

2,分析和解答:假设Nginx使用默认设置

absolute_redirect:on

server_name_in_redirect:off

port_in_redirect :on

这个问题看似丢失了端口号,实际上是暴露了nginx的内部端口号。这种情况下,port_in_redirect不管设置成on或者off都不会起作用。

port_in_redirect = on,nginx监听80端口,默认80端口号不需要在url中设置【3号分支】,结果是http://www.test.com/index/

port_in_redirect = off,nginx不设置port数据【5号分支】,结果还是http://www.test.com/index/

我们会发现,在这里无论怎么调整server_name_in_redirect【1,2号分支】和port_in_redirect的on,off都是无效的。因为1号至5号分支,都没有包含这种情况。解决问题的方向错了,解决方案当然就是错误的。

实际的解决方案有两个: rewrite【分支以外】 absolute_redirect:off【六号分支】 rewrite是通过脚本来控制nginx的运行过程,不在上面的配置分支中。具体操作可以参考下面的设置

rewrite [^/]$ $scheme://$http_host$uri/ permanent;备注:rewrite的操作方式依据不同的目录结构,可能略有不同,请根据实际情况来设置。

absolute_redirect:off,就是六号分支absolute_redirect:off之后,Nginx返回Location:/index/,但这个方式也许会带来其他问题。

六, 结语:文章分析到这里基本上已经没有什么内容好说的了。上面的思维导图已经把所有的解决问题的分支全部展现出来了。但最后,有感于互联网上中文技术文章的抄袭现象,我想说源代码就是最好的文档,作为程序员不要人云亦云。有问题,多看源代码!

七,参考资料:

nginx源代码

src\http\v2\ngx_http_v2_filter_module.c

src\http\ngx_http_header_filter_module.c

谢谢阅读

本文完成于2019-09-26

--------------------------

本文首发于公众号:js-mindmap

我是丁长老,十多年开发经验。个人微信号nine-ding,欢迎加我微信瞎聊。如需转发文章请加微信号获取转发授权。

[转帖]深度硬核文:Nginx的301重定向处理过程分析的更多相关文章

  1. nginx配置301重定向

    1. 简介 301重定向可以传递权重,相比其他重定向,只有301是最正式的,不会被搜索引擎判断为作弊 2. 栗子 savokiss.com 301到 savokiss.me 3. nginx默认配置方 ...

  2. Nginx 301重定向域名

    为何要使用301重定向 在网站建设中需要网页重定向的情况很多:如网页目录结构变动,网页重命名.网页的扩展名改变.网站域名改变等.如果不做重定向,用户的收藏和搜索引擎数据库中的旧地址只能让访客得到一个4 ...

  3. 【Nginx】冰河又一本超硬核Nginx PDF教程免费开源!!

    写在前面 在 [冰河技术] 微信公众号中的[Nginx]专题,更新了不少文章,有些读者反馈说,在公众号中刷 历史文章不太方便,有时会忘记自己看到哪一篇了,当打开一篇文章时,似乎之前已经看过了, 但就是 ...

  4. 阿里P7整理“硬核”面试文档:Java基础+数据库+算法+框架技术等

    现在的程序员越来越多,大部分的程序员都想着自己能够进入大厂工作,但每个人的能力都是有差距的,所以并不是人人都能跨进BATJ.即使如此,但身在职场的我们一刻也不能懈怠,既然对BATJ好奇,那么就要朝这个 ...

  5. 【硬核教程】只需1秒—你也可以有自己的API文档

    Nothing is true. Everything is permitted. 写在前面 先聊聊为什么想到了要用Vuepress来代替原来写在Confluence上的文档. 大意是有个需要其他部门 ...

  6. 重磅硬核 | 一文聊透对象在 JVM 中的内存布局,以及内存对齐和压缩指针的原理及应用

    欢迎关注公众号:bin的技术小屋 大家好,我是bin,又到了每周我们见面的时刻了,我的公众号在1月10号那天发布了第一篇文章<从内核角度看IO模型的演变>,在这篇文章中我们通过图解的方式以 ...

  7. 深度优化LNMP之Nginx (转)

    深度优化LNMP之Nginx Nginx基本安全优化 1.调整参数隐藏Nginx版本号信息     一般来说,软件的漏洞都和版本有关,因此我们应尽量隐藏或清除Web服务队访问的用户显示各类敏感信息(例 ...

  8. Colder框架硬核更新(Sharding+IOC)

    目录 引言 控制反转 读写分离分库分表 理论基础 设计目标 现状调研 设计思路 实现之过五关斩六将 动态对象 动态模型缓存 数据源移植 查询表达式树深度移植 数据合并算法 事务支持 实际使用 展望未来 ...

  9. 【硬核】使用替罪羊树实现KD-Tree的增删改查

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是机器学习的第16篇文章,我们来继续上周KD-Tree的话题. 如果有没有看过上篇文章或者是最新关注的小伙伴,可以点击一下下方的传送门: ...

  10. 十一长假我肝了这本超硬核PDF,现决定开源!!

    写在前面 在 [冰河技术] 微信公众号中的[互联网工程]专题,更新了不少文章,有些读者反馈说,在公众号中刷 历史文章不太方便,有时会忘记自己看到哪一篇了,当打开一篇文章时,似乎之前已经看过了,但就是不 ...

随机推荐

  1. IntelliJ IDEA官方宣布中文汉化包正式发布-intellijidea-guan-fang-xuan-bu-zhong-wen-han-hua-bao-zheng-shi-fa-bu

    title: IntelliJ IDEA官方宣布中文汉化包正式发布 date: 2021-07-27 16:42:21.823 updated: 2021-12-26 17:43:12.204 url ...

  2. Python——第五章:随机模块random

    1.浮点数random.random() 的返回值是在 [0, 1)(左闭右开区间)内的随机浮点数.这意味着它可以取到 0,但不包括 1.所以,random.random() 可以返回 0,但不能返回 ...

  3. 如何用.net制作一个简易爬虫抓取华为应用市场数据

    公司最近要做一款手机,手机需要制作一个应用市场.那么问题来了,自己制作应用市场,数据从哪来呢?作为一个创业型公司.搜集数据变成为了难题. 于是突然想到能不能通过程序去抓取别人应用市场的数据-- 那么我 ...

  4. Spring Cloud(一)Eureka

    单体应用存在的问题 在传统应用程序中,一般都会将整个的应用程序作为一个单独的可执行文件部署到相应的服务器上执行.一般的应用程序结构可能如下图所示: 这种方式的优点很明显,比如:架构简单,服务之间调用逻 ...

  5. Terraform 的开源替代:OpenTofu 宣布 GA!

    OpenTofu 社区于1月10日宣布 OpenTofu 项目 GA,这是 OpenTofu 的首个稳定版本(https://github.com/opentofu/opentofu/releases ...

  6. 斯坦福 UE4 C++ ActionRoguelike游戏实例教程 12.认识GamePlayTag, 实现技能的互斥

    斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论 概述 本篇文章对应Lecture 17 - GameplayTags, 67.67节.本文将会讲述UE4中Gamepl ...

  7. 开发的必杀技:Git 的分支管理

    摘要:作为一名码农,Git的使用就像家常便饭,时时刻刻都要用到,那么Git分支管理也是必须要掌握的. Git 分支管理 几乎每一种版本控制系统都以某种形式支持分支.使用分支意味着你可以从开发主线上分离 ...

  8. AI论文解读:基于Transformer的多目标跟踪方法TrackFormer

    摘要:多目标跟踪这个具有挑战性的任务需要同时完成跟踪目标的初始化.定位并构建时空上的跟踪轨迹.本文将这个任务构建为一个帧到帧的集合预测问题,并提出了一个基于transformer的端到端的多目标跟踪方 ...

  9. 破解数据匮乏现状:纵向联邦学习场景下的逻辑回归(LR)

    摘要:主要介绍了华为云可信智能计算服务(TICS)采用的纵向联邦逻辑回归(LR)方案. 本文分享自华为云社区<纵向联邦学习场景下的逻辑回归(LR)>,作者: 汽水要加冰. 海量训练数据是人 ...

  10. PPT 工作计划PPT 应该怎么样改

    收集素材 页面处理 丰富细节 PPT 工作计划PPT 应该怎么样做