上一篇博文对nginx最常用功能的server及location的匹配规则进行了讲解,这也是nginx实现控制访问和反向代理的基础。掌握请求的匹配规则算是对nginx有了入门,但是这些往往还是不能满足实际的需求场景,例如请求url重写、重定向等等,这都需要对请求的path进行修改操作的,匹配规则是不能独自完成实际需求的,这就需要掌握nginx的另一个常用功能rewrite,下面就来说说这个常用功能。

Rewrite规则

rewrite功能就是,使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。

rewrite只能放在server{}, location{}, if{} 中,并且只能对域名后边传递的参数外的字符串起作用,例如 http://baidu.com/a/we/index.php?id=1&u=str 只对/a/we/index.php重写。语法:

rewrite regex replacement [flag];

如果相对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用proxy_pass反向代理。

表面上看rewrite和location功能有点像,都能实现跳转,主要区别在于rewrite是在同一域名内更改获取资源的路径,而location是对一类路径做控制访问或反向代理,可以proxy_pass到其他机器。很多情况下rewrite也会写在location里,它们的执行顺序是:

  • 执行server块的rewrite指令
  • 执行location匹配
  • 执行选定的location中的rewrite指令

如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件;循环超过10次,则返回500 Internal Server Error错误。

2.1 flag标志位

  • last : 停止执行当前ngx_http_rewrite_module的指令集,但是会继续走一遍请求匹配对应server或者location;
  • break : 停止执行当前ngx_http_rewrite_module的指令集,请求就此完成。
  • redirect : 返回302临时重定向,地址栏会显示跳转后的地址
  • permanent : 返回301永久重定向,地址栏会显示跳转后的地址

因为301和302不能简单的只返回状态码,还必须有重定向的URL,这就是return指令无法返回301,302的原因了。

对于上面的flag,有几点需要强调一下:

  • lastbreak对url的重写不会改变地址栏的地址

也就是说,nginx虽然对请求url进行了重写,但是地址栏不会有任何明显的改变,仍然显示nginx重写前的地址;这与redirectpermanent不同。

  • lastbreak的处理策略不同

二者都会终止当前ngx_http_rewrite_module的指令集的执行,但是 last 立即发起新一轮的 请求匹配 而 break 则不会。

  • redirectpermanent会终止后续nginx指令的执行

nginx在rewrite遇到flag是二者时,后续的指令是不会执行的。

server {
listen 8080;
location = /test {
break;
return 200 $request_uri;
proxy_pass http://127.0.0.1:8080/other;
}
location / {
return 200 $request_uri;
}
}

上面例子中,我们访问 curl 127.0.0.1:8080/test,会发现,return 200 $request_uri语句没有执行,而proxy_pass指令被执行了。这是因为:

return指令属于ngx_http_proxy_module模块,它会被break终止掉;而rewrite模块它是ngx_http_proxy_module的指令,不会被break给中断掉。

2.2 if指令与全局变量

if判断指令

语法为if(condition){...} ,对给定的条件condition进行判断。如果为真,大括号内的rewrite指令将被执行,if条件(conditon)可以是如下任何内容:

  • 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
  • 直接比较变量和内容时,使用=!=
  • ~正则表达式匹配,~*不区分大小写的匹配,!~区分大小写的不匹配

-f!-f用来判断是否存在文件

-d!-d用来判断是否存在目录

-e!-e用来判断是否存在文件或目录

-x!-x用来判断文件是否可执行

例如:

if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;
} //如果UA包含"MSIE",rewrite请求到/msid/目录下 if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
set $id $1;
} //如果cookie匹配正则,设置变量$id等于正则引用部分 if ($request_method = POST) {
return 405;
} //如果提交方法为POST,则返回状态405(Method not allowed)。return不能返回301,302 if ($slow) {
limit_rate 10k;
} //限速,$slow可以通过 set 指令设置 if (!-f $request_filename){
break;
proxy_pass http://127.0.0.1;
} //如果请求的文件名不存在,则反向代理到localhost 。这里的break也是停止rewrite检查 if ($args ~ post=140){
rewrite ^ http://example.com/ permanent;
} //如果query string中包含"post=140",永久重定向到example.com location ~* \.(gif|jpg|png|swf|flv)$ {
valid_referers none blocked www.jefflei.com www.leizhenfang.com;
if ($invalid_referer) {
return 404;
} //防盗链
}

全局变量

下面是可以用作if判断的全局变量:

  • $args : #这个变量等于请求行中的参数,同$query_string
  • $content_length : 请求头中的Content-length字段。
  • $content_type : 请求头中的Content-Type字段。
  • $document_root : 当前请求在root指令中指定的值。
  • $host : 请求主机头字段,否则为服务器名称。
  • $http_user_agent : 客户端agent信息
  • $http_cookie : 客户端cookie信息
  • $limit_rate : 这个变量可以限制连接速率。
  • $request_method : 客户端请求的动作,通常为GET或POST。
  • $remote_addr : 客户端的IP地址。
  • $remote_port : 客户端的端口。
  • $remote_user : 已经经过Auth Basic Module验证的用户名。
  • $request_filename : 当前请求的文件路径,由root或alias指令与URI请求生成。
  • $scheme : HTTP方法(如http,https)。
  • $server_protocol : 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
  • $server_addr : 服务器地址,在完成一次系统调用后可以确定这个值。
  • $server_name : 服务器名称。
  • $server_port : 请求到达服务器的端口号。
  • $request_uri : 包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。
  • $uri : 不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。
  • $document_uri : 与$uri相同。

例如:

例:http://localhost:88/test1/test2/test.php
$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test2/test.php
$document_uri:/test1/test2/test.php
$document_root:/var/www/html
$request_filename:/var/www/html/test1/test2/test.php

2.3 常用正则

  • . : 匹配除换行符以外的任意字符
  • ? : 重复0次或1次
  • + : 重复1次或更多次
  • * : 重复0次或更多次
  • \d :匹配数字
  • ^ : 匹配字符串的开始
  • $ : 匹配字符串的结束
  • {n} : 重复n次
  • {n,} : 重复n次或更多次
  • [c] : 匹配单个字符c
  • [a-z] : 匹配a-z小写字母的任意一个

小括号()之间匹配的内容,可以在后面通过$1来引用,$2表示的是前面第二个()里的内容。正则里面容易让人困惑的是\转义特殊字符。

2.4 rewrite实例

例1:

http {
# 定义image日志格式
log_format imagelog '[$time_local] ' $image_file ' ' $image_type ' ' $body_bytes_sent ' ' $status;
# 开启重写日志
rewrite_log on; server {
root /home/www; location / {
# 重写规则信息
error_log logs/rewrite.log notice;
# 注意这里要用‘’单引号引起来,避免{}
rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4;
# 注意不能在上面这条规则后面加上“last”参数,否则下面的set指令不会执行
set $image_file $3;
set $image_type $4;
} location /data {
# 指定针对图片的日志格式,来分析图片类型和大小
access_log logs/images.log mian;
root /data/images;
# 应用前面定义的变量。判断首先文件在不在,不在再判断目录在不在,如果还不在就跳转到最后一个url里
try_files /$arg_file /image404.html;
}
location = /image404.html {
# 图片不存在返回特定的信息
return 404 "image not found\n";
}
}

对形如/images/ef/uh7b3/test.png的请求,重写到/data?file=test.png,于是匹配到location /data,先看/data/images/test.png文件存不存在,如果存在则正常响应,如果不存在则重写tryfiles到新的image404 location,直接返回404状态码。

例2:

rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;

对形如/images/bla_500x400.jpg的文件请求,重写到/resizer/bla.jpg?width=500&height=400地址,并会继续尝试匹配location。

例3:

ssl部分页面加密

2.5 rewrite需要注意的问题

上面说过,rewrite的指令规则为:rewrite regex replacement [flag];

rewrite指令用指定的regex来匹配请求的uri,若匹配成功则用replacement来重写请求uri。这里需要注意的replacement字符串的内容:

1、 若replacement以http://https://或者$scheme开头,则告诉nginx这是重定向操作(flag默认为redirect),nginx则停止处理后续内容,并直接重定向返回给客户端。

location / {
# 当匹配 正则表达式 /test/(.*)时 请求将被临时重定向到 http://www.baidu.com/$1
# flag默认为redirect
rewrite /test/(.*) https://www.baidu.com/$1;
return 200 ’ok'; # 此处没有机会执行
}

2、replacement非以上三种情况开头,则就是简单的url重写

location / {
# 当匹配 正则表达式 /test/(.*)时 请求将被临时重定向到 www.baidu.com/$1
# flag无值则rewrite会顺序执行
rewrite /test/(.*) www.baidu.com/$1;
return 200 ’ok'; # 此处因为rewrite顺序执行而得到执行机会
}

对于上面两种情况,还需要特别留意一个redirect端口的问题,先上一个例子:

## server.com机器上nginx的配置如下:
server {
listen 8000;
location /test1/ {
rewrite /test1/index.html http://server1.com/demo/test1 redirect;
}
location /test2/ {
rewrite /test2/index.html /demo/test2 redirect;
proxy_pass http://192.168.1.3:8000;
}
}

当访问http://server.com/test1/index.html时,会命中/test1的location规则,访问server1.com对应内容一直失败,发现重定向后响应头的Location字段值为http://server1.com:8000/demo/test1,带有8000端口,我们并没有配置,表现的比较诡异?

访问http://server.com/test2/index.html时,命中/test2的location规则,同样访问失败,但是访问的重定向后响应头Location字段值为http://server.com:8000/demo/test2,其带有server.com的server_name和8000的端口,更加诡异?

看到上面的现象,疑惑重重;其实这跟nginx的server_name_in_redirectport_in_redirect指令有关:

在绝对路径中,server_name_in_redirectport_in_redirect 指令表示是否将server块中的 server_name 和 listen 的端口作为redirect用, 重定向的完整url地址根据$schemeserver_name_in_redirectport_in_redirect来确定的。

在绝对路径中,server_name_in_redirect默认是禁用的,而port_in_redirect是默认启用的。对于带有$scheme重定向的绝对路径,nginx会从replacement中获取指定的server_name和port来进行重定向:

第一种,若replacement带请求协议http(s),而其中没有指定port的话,nginx会默认取当前server的listen端口作为重定向的端口。这是上面访问http://server.com/test1/index.html时重定向到http://server.com:8000/demo/test2时会携带8000的原因。

第二种,若replacement不带请求协议http(s),而是相对本地服务器的绝对地址的话,如上面访问http://server.com/test2/index.html的情况,此时server_name_in_redirect由于禁用它会去请求的host来作为server_name,取当前server的listen端口作为重定向的端口,最终重定向到http://server.com:8000/demo/test2

或许你会问,访问http://server.com/test2/index.html为什么不会重定向到http://192.168.1.3:8000/demo/test2上?这是因为rewrite的redirect flag会终止后续指令的执行,所以其后的proxy_pass指令不会执行。

参考

前端开发掌握nginx常用功能之rewrite的更多相关文章

  1. 前端开发掌握nginx常用功能之server&location匹配规则

    nginx主要是公司运维同学必须掌握的知识,涉及到反向代理.负载均衡等服务器配置.前端开发尤其是纯前端开发来说对nginx接触的并不多,但是在一些情况下,nginx还是需要前端自己来搞:例如我们公司的 ...

  2. 3.Nginx常用功能介绍

    Nginx常用功能介绍 Nginx反向代理应用实例 反向代理(Reverse Proxy)方式是指通过代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并且从内部网络服 ...

  3. Nginx常用功能配置二

    Nginx常用功能配置二 Nginx location匹配设置 location作用:可以根据用户请求的URI来执行不同的应用,根据用户请求的网站的地址URL匹配. location语法: locat ...

  4. Nginx 常用全局变量 及Rewrite规则详解

    每次都很容易忘记Nginx的变量,下面列出来了一些常用 $remote_addr //获取客户端ip $binary_remote_addr //客户端ip(二进制) $remote_port //客 ...

  5. Nginx常用功能

    3.Nginx常用功能 3.1 反向代理服务器 3.1.1.demo2 a.我在tomcat下部署了一个javaweb项目,tomcat安装的服务器IP为:192.168.37.136,部署的项目在t ...

  6. Nginx常用功能配置一

    Nginx常用功能配置 参数include配置 说明:如果日常工作中server标签存在太多,可以采用include配置模式,Nginx的主配置文件包含的所有虚拟主机的子配置文件会统一放入extra目 ...

  7. nginx常用功能和配置

    nginx常用功能和配置 1.nginx常用功能和配置 1.1 限流 1.2 压力测试工具--Ab 1.2.1安装 1.2.2 测试 1.2.3 返回值 1.3 limit_conn_zone 1.4 ...

  8. Nginx重写功能(rewrite与location)

    一.常用的Nginx 正则表达式 二.访问路由location 2.1location的分类 location 大致可以分为三类: 精准匹配:location = / {} 一般匹配:location ...

  9. Nginx常用伪静态规则(rewrite)-Discuz X3.2-WordPress-PHPCMS-ECSHOP-SHOPE(转)

    当我们从apache服务器转向Nginx服务器的时候,它们的伪静态规则就不一样了,所以你熟悉Nginx服务器的伪静态规则,自己写当然也好.不了解Nginx服务器的伪静态规则的,为方便各位站长,收集了这 ...

随机推荐

  1. Spring常用注解总结(1)

    前言:项目中常用的注解常看常记,总会对自己有些好处,所以在这里分享一下. 使用spring时,可以使用xml配置文件配置相关信息.但是我还是喜欢用注解的方式,因为可以充分利用反射机制获取类结构信息,而 ...

  2. webapp优化

    1. 优化前提: 业务架构与数据库设计 2.  单页web应用  : ExtJs  backbone  ng  avalon 框架: React Native    , ionic  , Mui, m ...

  3. javascript Hoisting变量提升

    1. 看人家举的两个例子,我认为这里的判断是否定义: !var 其实就是 指是否在函数function里面定义了.只有在funciton里面定义了了,js才hoist到最上面去找这个变量的值,否则就按 ...

  4. 《笨方法学Python》加分题32

    注意一下 range 的用法.查一下 range 函数并理解它在第 22 行(我的答案),你可以直接将 elements 赋值为 range(0, 6) ,而无需使用 for 循环?在 python ...

  5. 面试简单整理之spring、spring mvc

    90.为什么要使用 spring? 解决企业应用开发的复杂性,IOC.aop 91.解释一下什么是 aop? 面向切面编程.... 92.解释一下什么是 ioc? 控制反转.. 93.spring 有 ...

  6. c#简单的数据库查询与绑定DataGridView。

    1配置文件 (两种写法) <connectionStrings>    <add name="connStr" connectionString="se ...

  7. Spring事务,Bean生命周期

    一.事务相关: 1.Spring事务基于Spring AOP切面编程: 2.AOP基于代理模式,得到需要开启事务的代码的代理对象: 3.而没有开启事务的Service方法里调用了开启事务 @Trans ...

  8. vue项目运行

    共分为以下六步: 1.安装node.js2.安装cnpm3.安装vue-cli脚手架构建工具4.用vue-cli构建项目5.安装项目所需的依赖6.运行项目 第1步:从node.js官网下载node.j ...

  9. sqlserver CLR sqlserver使用C# dll

    转载地址:https://www.cnblogs.com/Brambling/p/8000911.html

  10. ListView点击事件失效(item里面有button按钮控件)解决方法

    ListView点击事件失效解决方法: 一般出现这个情况,就是你的item里面有按钮的点击事件,你的item里面有button控件,button控件是抢占焦点的,只要在你的item布局里面这样子写就可 ...