利用ngx_http_rewrite_module模块解析和处理rewrite请求,此功能依靠 PCRE(perl compatible regular expression)
因此编译之前要安装PCRE库,rewrite是nginx服务器的重要功能之一,用于实现URL的重写,URL的重写是非常有用的功能
比如它可以在我们改变网站结构之后,不需要客户端修改原来的书签,也无需其他网站修改我们的链接,就可以设置为访问,另外还可以在一定程度上提高网站的安全性(防盗链)
官方文档:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html
官方文档:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if
不支持if else、if elif等多重判断,一个if一次性,false不继续,true继续
- if (条件匹配) {
- 操作
- }
匹配条件的符号:
支持正则表达式,匹配成功为true
与shell的很相似
! | 非 |
= | 字符串的等于 |
~ | 开启正则表达式,区分大小写 |
~* | 开启正则表达式,不区分大小写 |
-f | 文件是否存在 |
-d | 目录是否存在 |
-x | 文件是否有执行权限 |
-e | 文件、目录、软链接是否存在 |
注意:
结合前一篇的文章中的第三方模块:echo模块测试
- location /x {
- if ($scheme = http) {
- echo '$scheme is http';
- }
- if ($scheme = https) {
- echo '$scheme is https';
- }
- if (!-e $request_filename) {
- echo '$request_filename is not exist';
- }
- }
普通的变量赋值
与其他编程语言一样,break用于打断本次
用于server、location、if段,if和location中会跳出本次
- location /x {
- echo 'one';
- if ($scheme ~* http) {
- echo '123';
- break;
- }
- echo 'two';
- break;
- echo 'three';
- }
用于server、if、location中
return用于完成对请求的处理,并直接向客户端返回响应状态码,比如:可以指定重定向URL(对于特殊重定向状态码,301/302等)或者是指定提示文本内容(对于特殊状态码403/500等),处于此指令后的所有配置都将不被执行
- return [状态码/状态码] 字符或变量或url;
- location /t {
- root /opt/web;
- if ($scheme ~* http) {
- echo 'is http';
- return 666 "not allow http";
- }
- if ($host ~* www.hj.com) {
- return www.baidu.com; #访问www.hj.com直接跳转到百度
- }
- }
ngx_http_rewrite_module模块日志记录到error_log日志文件当中
配置在http、server、location 或 if 中
一般不建议开启,增加了cpu和磁盘消耗
注意:需要错误日志级别为notice
- server {
- error_log /opt/nginx/logs/err.log notice;
- location /t {
- rewrite_log on;
- }
- }
用于server、location、if
通过正则表达式的匹配来改变URI,可以同时存在一个或多个指令,按照顺序依次对URI进行匹配,rewrite主要是针对用户请求的URL或者是URI做具体处理
正则表达式与shell的类似
官方文档:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite
注意:
- rewrite 正则表达式 替换后的内容 [标志位];
正则表达式格式:
. | 除换行符以外的任意字符 |
\w | 字母、数字、下划线、汉字 |
\s | 任意空白符 |
\d | 数字 |
\b | 单词的开始或结束 |
^ | 以什么开始 |
$ | 以什么结束 |
* | 重复0次或多次 |
+ | 重复1次或多次 |
? | 匹配0次或1次 |
(n) | 重复n次 |
重复n次或多次 | |
重复n到m次 | |
*? | 任意次,但尽可能少重复 |
+? | 1次或多次,但尽可能少重复 |
?? | 0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
\W | 除字母、数字、下划线、汉字以外的字符 |
\S | 除空白符以外 |
\D | 除数字以外 |
\B | 除单词开头或结束的位置 |
[^abc] | 除abc以外的字符 |
标志位:
前两种是跳转型的flag,后两种是代理型:
一般做域名重写使用跳转型,做location重写做代理型
- redirect
- 状态码:302,域名的临时调整,之前的域名和url可能还会用,或者新跳转的目标域名和url还会跳转,这种状态码浏览器不会缓存到本地
- 临时重定向,重写完成后以临时重定向方式直接返回重写后生成的新URL给客户端,由客户端重新发起请求
- 用相对路径,或者http://或https://开头
-
- permanent
- 状态码:301,域名永久调整,旧域名不可用,访问时永久跳转到新域名和url,这种状态码浏览器会缓存到本地磁盘
- 重写完成后以永久重定向方式直接返回重写后生成的新URL给客户端,由客户端重新发起请求
-
- break
- 重写完成后,停止对当前URL在当前location中后续的其它重写操作,而后直接跳转至重写规则配置块之后的其它配置;结束循环,建议在location中使用
- 适用于一个URL一次重写
-
- last
- 重写完成后,停止对当前URI所在的location中后续的其它重写操作,而后对新的location的URL启动新一轮重写检查,不建议在location中使用
- 适用于一个URL多次重写,要注意避免出现超过十次以及URL重写后返回500错误的给用户
- location / {
- rewrite /a http://www.qq.com redirect;
- rewrite /b http://www.baidu.com permanent;
- }
例2:break场景:有V1/V2两个版本的网站前端页面并存,旧版本的网站数据在statics,还不能丢失,但是要将访问新版本的请求重写到新的静态资源路径static
- mkdir /opt/web/{static,statics}
- echo old > /opt/web/static/index.html
- echo new > /opt/web/statics/index.html
-
- vim nginx.conf
- server {
- location /statics {
- root /opt/web;
- rewrite ^/statics/(.*) /static/$1 break;
- location /static {
- root /opt/web;
- }
- }
-
- nginx -s reload
- #测试访问,返回的资源来自于: /opt/web/static/资源
- curl -L 127.0.0.1/break
例3:不改变客户端访问方式但是需做多次目的URL重写的场景,使用场景不是很多
- server {
- location /last {
- rewrite ^/last/(.*) /ts1/$1 last; #访问/last跳转访问/ts1
- rewrite ^/ts1/(.*) /ts2/$1 last; #访问/ts1跳转访问/ts2
- }
- location /ts1 {
- echo $uri;
- }
- location /ts2 {
- echo $uri;
- }
- }
目前主流网站的实现方式都是此,比如访问:http://www.baidu.com ,会自动跳转到:https://www.baidu.com
注意:做全站https时,不用写其他二级目录location,有其他需要时写在/的location就行,否则每个二级目录location都要写rewrite
- server {
- location / { #全站https
- if ($scheme = http) {
- rewrite / https://$host permanent;
- }
- }
-
- location /login { #单url做https
- if ($scheme = http) { #必须配合if,否则变成死循环
- rewrite ^/(login)$ https://$host/$1 permanent;
- }
- }
- }
京东商城的错误页跳转就是此实现的,在京东输入错误的页面,总是能跳转到首页
- server {
- location / {
- if (!-e $request_filename){
- rewrite (.*) http://$host/index.html;
- }
- }
- }
- server {
- location / {
- if ( $http_user_agent ~* MSIE ){ #ie浏览器使用此url资源
- rewrite ^(.*)$ /msie/$1 break;
- }
- if ( $http_user_agent ~* chrome ){ #谷歌浏览器使用此url资源
- rewrite ^(.*)$ /chrome/$1 break;
- }
- }
- }
要求:/20201002/static --> /static?id=20201002
- location / {
- rewrite ^/(\d+)/(\.+)/ /$2?id=$1 last;
- #或者
- rewrite ^/(\d+)/(.*)/ /$2?id=$1 last;
- }
要求:www.hj.com/images/20200106/1.jpg --> www.hj.com/index.do?name=images&dir=20200106=&file=1.jpg
- location / {
- if ($host ~* .*\.hj.com\.com) {
- rewrite ^/(.*)/(\d+)/(.*)$ /index.do?name=$1&dir=$2$file=$3 last;
- }
- }
官方文档:https://nginx.org/en/docs/http/ngx_http_referer_module.html
由ngx_http_referer_module模块提供,用于server、location
防盗链基于客户端携带的referer实现,referer是记录上一个网站是谁,相当于记录网页跳转上下文。一般统计此记录进行分析、日志记录、缓存优化
如:www.baidu.com-->www.qq.com时,在www.qq.com查看referer,记录的就是www.baidu.com
如果别人只链接了自己网站图片或某个单独的资源,而不是打开了网站的整个页面,如频繁访问图片、视频等,这就是盗链
- vaild_referers 参数1 参数2 ...; 指定合法的referers
- 参数:
- none 请求报文没有referer首部,用户访问时没有使用搜索引擎,直接输入域名时,没有referer信息。意思是直接引用的链接。合法引用
- blocked 请求报文的referer为空时,合法引用
- server_names referer首部包含本机nginx监听的server_name和公司内其他主机名
- string 自定义指定字符串,可使用通配符“*”,如*.hj.com
- 正则表达式 使用正则表达式匹配字符串,必须以“~”开始,如:~.*\.hj\.com
实际上就是允许哪些跳转路径为合法,为匹配的referer都拒绝
建议把访问日志格式改为json格式的,方便后期做日志分析
- server {
- location ~* \.(jpg|png|jpeg|gif)$ {
- #允许本域名和相关搜索引擎为合法
- valid_referers none blocked server_names
- www.hj.*/img/ ~\.baidu\. ~\.google\.
- ~\.baidu\. ~\.bing\. ~\.so\. ~\.dogedoge\.;
- if ($invalid_referer) {
- return 403 "Forbidden Access"; #不合法的referer都返回403
- }
- }
- }