深度硬核文:Nginx的301重定向处理过程分析

一,序言

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

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

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

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

  让我们先看看Nginx的逻辑设计是怎么样的。
  HTTP协议中3xx开头的状态响应码都是表示重定向的响应。根据RFC的定义
  301 Moved Permanently
  302 Found
  303 See Other
  307 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定义来说,导致浏览器触发重定向操作是因为浏览器收到了一个Location response header;

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

The Location response header indicates the URL to redirect a page to. It only provides a meaning when served with a 3xx (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



  写多不如写精!
  我是丁长老,十多年开发经验。
  欢迎关注我的技术公众号。
  个人微信号nine-ding,欢迎加我微信瞎聊。
  如需转发文章请加微信号获取转发授权。

原文地址:https://www.cnblogs.com/jsmindmap/p/11602897.html

时间: 2024-11-07 10:59:21

深度硬核文:Nginx的301重定向处理过程分析的相关文章

ngin配置301重定向设置方法和nginx子目录301重定向

这篇文章主要介绍了ngin配置301重定向设置方法和nginx子目录301重定向,需要的朋友可以参考下nginx版本为1.1.19. 若域名由a.com转移到了b.com,并对两个域名有所有权,可以:复制代码 代码如下:server{ server_name a.com; return 301 $scheme://b.com$request_uri; } 重启服务器,然后看一下成功与否:复制代码 代码如下:curl -I a.com nginx子目录301重定向设置方法 如我想把www.b.co

利用nginx的301重定向到另外服务器

需求: 如果本地没有这个url就rewrite到另外服务器上去请求: 实现: server { listen       8080;    if (!-e $request_filename ){    rewrite ^/(.*)$ http://192.168.2.42/$1 permanent; } } 说明: 当本地web上没有这个资源的时候,就跳转到192.168.2.42服务器上去请求: nginx的rewrite指令中可以通过设置该条rewrite的flag来对该规则进行说明.一般

Nginx 301重定向域名

为何要使用301重定向 在网站建设中需要网页重定向的情况很多:如网页目录结构变动,网页重命名.网页的扩展名改变.网站域名改变等.如果不做重定向,用户的收藏和搜索引擎数据库中的旧地址只能让访客得到一个404错误信息页面,访问流量白白丧失.不仅如此,之前该页面的一切积累(比如PR值)就都白费了. 301重定向不仅能使页面实现自动跳转,对于搜索引擎来说,也可能可以传递PR值. nginx重定向规则详细介绍 http://www.jefflei.com/post/1015.html rewrite命令n

Nginx 301重定向设置二种方法

进行了301重定向,把www.sunrisenan.com和sunrisenan.com合并,并把之前的域名也一并合并. 有两种实现方法,第一种方法是判断nginx核心变量host(老版本是http_host): 第一种方法: server { server_name www.sunrisenan.com sunrisenan.com; if ($host != 'www.sunrisenan.com' ) { rewrite ^/(.*)$ http://www.sunrisenan.com/

Nginx 301重定向的配置

301重定向是很常见的需求,比如访问 4311.com,自动跳到 www.4311.com.或者倒过来,访问 www.4311.com跳到 4311.com.Nginx 中配置 301 重定向(301 redirect)很容易,下面介绍下方法. 打开 nginx.conf 文件,找到你的 server 配置段: 1 server { 2     listen       80; 3     server_name  nowamagic.net www.nowamagic.net; 4     #

nginx 301重定向几种写法

nginx 301重定向写法域名设置从http强制跳转到https server {listen 80;server_name 100tt.com www.100tt.com 100tt.me 100tt.vip 100tt.org;rewrite ^(.*)$ https://$host$1 permanent;} 或者以下写法,从一个域名跳到另一个域名,但是这些域名都是nginx配置上有的域名 server {server_name 666.com www.666.com;if ($host

php 实现301重定向跳转实例代码

本文主要介绍php 实现301重定向跳转,通过实例代码让大家更好的理解重定向的方法,有需要的小伙伴可以参考下 在php中301重定向实现方法很简单我们只要简单的利用header发送301状态代码,然后再用header进行跳转,效果与apache,iis,nginx都是一样的效果哦. 一:更推荐这种方法,因为它可以把http://www.jb51.net原来所有的url都转到http://jb51.net新的地址上 代码如下: <?php $the_host = $_SERVER['HTTP_HO

一套硬核安防视频云平台的炼成

导读 三大特点.五大重点,一套硬核安防视频云平台是这样炼成的 很多人会困惑,云计算究竟是什么? 它是可以被看作为商品的一种计算能力,可以在线上被无限制流通,就如水.电.煤气一样,可以被人们方便地取用,且价格相较低廉. 十多年前,无论是中国.美国,亚洲.欧洲,无论是大企业还是小公司,建设企业 IT 架构,为公司的业务和战略做信息支撑的思路都如出一辙——购买服务器,只是多或少的问题. 今天来看,这类 IT 基础设施的布局是一项非常不必要的高额支出,因为他们可以通过租用“公有云”的云计算服务来获取性价

IIS7.5+WebConfig实现页面伪静态和301重定向

IIS7.5+WebConfig实现页面伪静态和301重定向 使用URLRewriter组件在windows 2003 +iis 6.0下配置伪静态的文章网络上一大堆.但在iis7.0或iis 7.5 环境下配置的网站基本上没有讲解的,就算有也是一些无用的代码段,今天好不容易配置成功了,发个日记共享一下经验. 文档下载:http://files.cnblogs.com/files/dunitian/IIS7.5_WebConfig%E5%AE%9E%E7%8E%B0%E9%A1%B5%E9%9D