一 官方rewrite模块
1)这个模块作用于'SERVER_REWRITE'和'REWRITE'阶段,前者的优先级'较高'
2)根据rewrite模块出现在'server{}'还是'location{}',判断属于'哪个'阶段
① 模块涉及的指令
② 基本简介
pcre下载地址
pcretest测试正则表达式
二 return指令细讲
① return
说明: 首先'讲解'这个指令的原因是,我们经常拿来'做测试'使用
备忘录: 返回'文本(text)'的时候,经常忘记加'code' --> 易错: return 200 'ok'
影响重定向的几个指令
nginx 444 状态码 nginx自定义的状态码
444: nginx的444状态被服务器'直接断开'连接,'不再向客户端返回'消息,简单粗暴
307: 使用'源请求'使用的method,而且会携带'源请求的body'作为重定向请求的包体
② 小程序验证码
背景: '百度、微信等'小程序跳转'华为云、腾讯云、阿里云'时候,必须验证
说明: default_type 设置'text/plain',是'避免'浏览器下载
实际: 根据'txt'后缀会自动根据'mine.type'返回'对应的Content-Type
③ 优先级问题
(1)执行阶段优先级
'动作类'指令:无法'合并'
(2)同一阶段同一模块不同指令的优先级
+++++++++ "对比实验" +++++++++
同一阶段同一模块不同指令的优先级:按照在'配置文件'中出现的'先后'顺序
(3)return和error_page优先级
说明: 默认'error_page'只拦截'静态',不拦截'动态(后端返回的)'的指定'错误'状态码
是否'补获'后端错误: 'proxy_intercept_errors'开关指令
1)error_page是'少有'的、集成到'http框架中'的指令,'它不归属'任何阶段
2)仅当可能'返回错误指令'时,才会'使用'到它
3)所有阶段'都可能'返回响应,例如access、preaccess、rewrite等等,'error_page'可能会补获
+++++++++ "对比实验" +++++++++
说明: 'return code'的优先级比相同code 'error_page code' 优先级高
(4)return和echo指令的优先级
场景: 这两个'经常'用来做临时'调试'
解读:echo指令在'content阶'段执行,而'return'总在content阶段'之前'
④ 常用案例
需求1: http'跳转'到https
server {
listen 80;
server_name www.wzj.com;
return 301 https://$http_host$request_uri;
}
+++++++++++++++ "分割线" +++++++++++++++
需求2:临时'调试'
return 200 $name $request_uri;
default_type application/json;
return 200 '{"name":"aming","id":"100"}'; # 返回json的形式
+++++++++++++++ "分割线" +++++++++++++++
需求3:请求后缀'过滤'
location ~ .*\.(sh|bash)?$ {
return 403;
# deny all;
}
+++++++++++++++ "分割线" +++++++++++++++
需求4:网站被'黑'了,凡是在'百度点击到本网站'的请求,全部都跳转到了一个'赌博'网站
if ($http_referer ~ 'baidu.com') {
return 200
"<html><script>window.location.href='//$host$request_uri';</script></html>";
}
三 rewrite指令细讲
① rewrite
[1]、在一个 URL 请求中,rewrite 如果'匹配到' regex,那么 URL 就会'替换'成 replacement
[2]、如果'不考虑'flag,匹配规则'多个rewrite'是'顺序执行',即使匹配到了,仍然'会继续'匹配下去
[3]、如果 replacement 包含 "http://", "https://", or "$scheme"
--> 那么匹配会'立即终止',并直接'重定向地址'给客户端
++++++++++ 'rewrite'哪些是重定向,哪些是'内部'转发 ++++++++++
1、涉及'https'、'http'、'$scheme' 都是'外部'重定向
https|http|$scheme 这三种,默认是'redirect(302)',可以自己指定'permanent(301)'
特点:一旦匹配上,立即终止'处理','直接返回'给客户端301、302,'不再进行'find_location阶段
细节:客户端的'地址栏'会发生变化
原因:浏览器根据'301、302'的状态码+'Location'响应头再发一次请求
curl -Lkv 查看详细'交互'过程
2、如果'replacement'不是上述的,或者'不是'返回给客户端'301、302'响应状态码,则是'内部'跳转
案例讲解
(1)案例1 重定向
+++++++++ '补充' +++++++++
1)一旦'重定'向了,该'location 块'中后续的其它'rewrite脚本指令集'不再执行
2)细节点: 'add_header'还是会被执行
(2)案例2 break
体会: 'break'标志位的作用 -->只是'跳过当前'的rewrite阶段,并执行'本请求后续'的执行阶段
说明: location中包含'rewrite'模块多个'脚本指令的set集合'
++++++++++ "对比实验" ++++++++++
(3) nginx rewrite 中last break flag区别
++++++++++++++++++"假定rewrite在location中"++++++++++++++++++
[1]. last 和 break一样 它们都会'终止'此 location 中'其它rewrite模块指令'的执行
[2]. 但是 last 立即发起'新一轮'的 location 匹配 而 break 则'不会'
补充: 'break'跳过'当前的rewrite'阶段,并执行本请求'输出'阶段
++++++++++++++++++++++++"分割线"++++++++++++++++++++++++
[1]. flag 参数'如果'是 'redirect' 或 'permanent',立刻'中止规则匹配',进行 '3xx' 跳转
[2]. 如果在 location 中配置 'flag 是 last',立刻'跳出本 location' 的匹配
-->同时会顺序'继续搜寻其他' location 的匹配,如果还'没匹配'到,还会继续'搜寻本' location
[3]. 而 break '跳出本' location 后就'不会再匹配'其它 location 了
案例汇总
1. 如果'rewrite'同一个上下文'有多个'这样的规则:无'https、http、$scheme',也无'flag'标识
2. 匹配会按照'rewrite'指令出现的'先后'顺序依次进行,匹配到一个后'不会终止';
--> 而是'继续往下'匹配,直到返回'最后一个匹配上'的为止
+++++++++++++++++"分割线1"+++++++++++++++++
+++++++++++++++++"分割线2"+++++++++++++++++
+++++++++++++++++"分割线3"+++++++++++++++++
请求: http//rewrite.wzj.com/break/xx
输出: 'break page'
涉及: static和echo 模块'优先'级;root指令属于'static'模块
分析:
1. break是'跳过当前请求'的rewrite阶段,并'继续执行本请求'的'其他'阶段
2. 很明显,对于/break 对应的'content阶段'的输出为 echo "break page"
3. content阶段,可以简单理解为'产生数据输出的阶段'
4. echo指令也是'运行在content阶段',一般情况下content阶段只能'对应一个'输出指令,如同一个location'配置两个echo',最终只会有一个echo指令被执行
5. 当然如果你把'/break/'里的'echo 指令注释',然后再次访问/break/xx会'报404','static'模块起作用
6. 虽然/break/xx'被重定向'到/test/xx,但是break指令'不会重新开启一个新的请求'继续匹配,所以nginx是'不会匹配到下面'的/test/这个location
7. 在echo指令'被注释'的情况下,/break/ 这location里'只能执行'nginx'默认'的content指令,即'尝试找/test/xx这个html页面'并输出起内容,事实上,这个页面'不存在',所以会报404的错误
++++++++++++++ "分割线" ++++++++++++++
请求: http//rewrite.wzj.com/last/xx
输出: 'test page'
分析:
1. last与break最大的'不同'是,last会'重新发起'一个新请求,并'重新匹配'location
2. 所以对于/last,重新匹配请求以后'会匹配到/test/',所以最终'对应的content阶段'的输出是test page;
echo模块源码安装
(4) 死循环场景
(5)查询参数
1. rewrite /a.html /new permanent -->重定向'带原始参数'的地址
2. rewrite /a.html /new? permanent -->重定向'不带'参数
3. rewrite /a.html /new?age=18 permanent -->重定向'追加'查询参数
4. rewrite /a.html /new?id=$arg_id&name=$arg_name? permanent 重定向'带指定'参数
说明: '4'实质是'自定义查询参数'
return, rewrite, and try_files对比 辅助参考
各种奇怪的rewrite需求
问题引入: rewrite'301重定向'导致'查询参数重复'
"根因探讨": '$request_uri' 比'$uri'多了查询参数,rewrite默认就带'查询'参数,导致'重复'
++++++++++++++++++++ "以下三种等价解决策略" ++++++++++++++++++++
1) rewrite ^ https://ceshi2.wzj.com$request_uri? --> "推荐"
说明: 通过'?'改变'rewrite'查询参数的'默认'行为
2) rewrite ^ https://ceshi2.wzj.com$uri
说明: 'rewrite'默认自动携带'查询参数'
3) rewrite ^(?<capture>.*)$ https://ceshi2.wzj.com$capture --> "常见"
补充1: rewrite会导致html5的锚点或vue的hash路由模式下'#'数据丢失,因为'#'属于客户端数据
补充2: rewrite之后可以进行'#'透传;eg: rewrite ^ https://ceshi2.wzj.com$uri#first
③ rewrite的应用场景
④ rewrite使用正则的注意事项
注意1: 正则中使用 "}" or ";" ,必须使'(用单|双)'引号,因为'}、;'在nginx.conf有'特殊'含义
注意2: url如果含有' 空格',推荐使用使'(用单|双)'引号
四 break指令细讲
① break
疑惑点: 结束'该作用域'下剩余的指令,还是'只是该rewrite模块的'指令?
(1)配置demo
( 2)不带参数请求
(3)带参数请求
由于URL中'&、&'不转义,curl请求最好加上'单引号',如果想使用'shell变量'则使用"双引号"
遗留:带参数同时配置中的'break'关闭看看效果
效果:整个请求就'被终止'了,后续的指令都'不'执行了,直接返回'404'
nginx的11个阶段
五 rewrite_log指令细讲
① rewrite_log
error.log开启'notice'级别,默认是'error'
六 if指令细讲
① if
遗留: 简单记住在if里'只有rewrite的模块指令'是'安全'的
if指令'掌握'两个点:'自定义[set]'和'内置(模块提供)'变量和'正则'形式
掌握:if (condition) {} 中'condition'的场景
② if 的 and else or 等价用法
++++++++++++++ '官方'案例 ++++++++++++++
$request_filename:当前'连接请求'的文件路径,由'root'或'alias指令'与URI请求生成
相关案例参考
if ( $http_cookie ~* "name=wzj") -->cookie'数据太大',优点不明显
if (remotr_addr ~* ^172\.25\.2\.*) -->限定'部分ip'访问
++++++++++++ server块中 ++++++++++++
if ($request_uri ~ /vnc/include/(.+)) {
set $parms ;
# 体会这个'含义',个人感觉是终止'server中执行rewrite的'阶段
break;
}
location /vnc {
proxy_pass http://www.wzj.com/$parms
}
高级:nginx 剔除 $args 变量中任意指定参数
七 set指令细讲
① set
细节点: 除了'变量$args'可以被'修改'以外,其'对应的子变量值'都只是'只读',而'不能'进行修改
补充: rewrite正则、set'自定义'、map、location正则、server_name -->可以'产生变量'
作用域问题
② set细节补充
++++++++ "set细节补充" ++++++++
1) 两个含义:'创建'变量,给变量'赋值',但是在'不同的阶段'
2) 创建变量是在'加载'配置的时候 --> "reload|restart"是否报错
3) 赋值变量是当'请求处理上下'文需要时'触发'
eg: proxy_pass http://http_target ,用户在请求的时候没有传递'target'请求头则报错
4) 直接调用一个'未被创建'的变量,会导致配置'无法'启动
5) '变量名'是整个配置文件可见,但'变量的值'是基于'每个独立HTTP请求'的上下文
③ set变量作用域
1) 变量名是'整个配置文件'可见,但'变量的值'是基于每个独立请求的上下文
解读:变量是'全局'的
2) rewrite导致nginx'内部location跳转'情形下,此时请求还属于同一个请求,因此变量值是不变的
3) 同一个上下文,'多次set同一个变量',使用配置中最后一个set值
细节: rewrite模块return有'额外影响','什么'影响?
④ uninitialized_variable_warn
set 与 变量 set变量与子请求
八 高级正则
① 普通补获
捕获 (exp) :匹配exp,并捕获文本到'自动命名、、...'的组里 --> "常见"
+++++++++++ 'http跳转到https' +++++++++++
server {
listen 80;
server_name www.wzj.com ;
rewrite ^(.*) http://www.wzj.com permanent;
}
② 命名捕获
(?<name>exp):匹配exp,并'捕获文本'到名称为'name的组'里,也可以写成(?'name'exp)
说明:能'看懂'即可
③ 非补获
1. 语法:(?:pattern)
2. 解读:'非获取匹配','匹配'pattern但'不获取'匹配结果,'不进行存储'供以后使用
3. 这在使用或字符"(|)"来组合一个'模式'的'各个部分'是很有用
4. 例如:'industr(?:y|ies)'就是一个比'industry|industries'更简略的表达式
++++++++++ "案例讲解" ++++++++++
if ($uri ~* \.(?:html|css|js|json)$){
proxy_pass http://static/wzj.com
}
+++++++++ nginx自行对"/"处理 +++++++++
~* ^\/wzj\/(.+?\.(?:html|css|js))$
说明:'/'不需要跟'shell'正则一样转义,nginx会'自行'处理
.*、.*?、.+?的含义
④ 零宽度断言
shell常用正则表达式
四个'非捕获组'用于匹配pattern'或者'不匹配pattern位置'之前'或'之后'的内容,匹配的结果'不包括'pattern
补充:(?#comment),这种类型的分组'不对'正则表达式的处理产生任何影响,用于'提供注释'让人阅读
案例一
location ~* (?!.*/wzj/favicon).*(\.ico)$ {
rewrite ^(.*) http://rewrite.wzj.com/static/wzj/favicon/favicon.ico permanent;
# 备注:rewrite 'https://' --> 所以下面'return 301'不会执行
return 301;
}
[1]. $request_uri 是'待匹配'的字符串
http://rewrite.wzj.com'/aa/bb/cc.ico'
http://rewrite.wzj.com'/wzj/favicon/favicon.ico'
http://rewrite.wzj.com'/wzj/favicon/favicon.txt'
[2]. (?!.*/wzj/favicon).*(\.ico)$ 是'正则模式'
遗留点:'回溯'的过程?
⑤ 贪婪和非贪婪模式
浅谈nginx反向代理中神奇的斜线 整理的案例
九 相关参考
break和last理解
rewrite官方的一些案例
websocket官方配置
internal 指令限制访问图片资源文件
nginx internal 语法 – SRE笔记
重写与重定向的关系和配置