nginx的rewrite重写及防盗链

rewrite相关:

利用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

模块的指令:

if指令:

官方文档: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 文件、目录、软链接是否存在

注意:

  • $变量的值为空、为0时,if判断为false
  • nginx 1.0.1之前$变量的值如果以0开头的任意字符串会返回false
例:

结合前一篇的文章中的第三方模块: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';
	}
}

set指令:

普通的变量赋值

break指令:

与其他编程语言一样,break用于打断本次
用于server、location、if段,if和location中会跳出本次

例:
location /x {
	echo 'one';
	if ($scheme ~* http) {
		echo '123';
		break;
	}
	echo 'two';
	break;
	echo 'three';
}

return指令:

用于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直接跳转到百度
	}
}

rewrite_log指令:

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;
	}
}

rewrite指令:

用于server、location、if
通过正则表达式的匹配来改变URI,可以同时存在一个或多个指令,按照顺序依次对URI进行匹配,rewrite主要是针对用户请求的URL或者是URI做具体处理
正则表达式与shell的类似

官方文档:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite

注意:

  • 如果在同一级配置块中存在多个rewrite规则,那么会自下而下逐个检查;
  • 被某条件规则替换完成后,会重新一轮的替换检查,隐含有循环机制,但不超过10次
  • 如果超过10次,提示500响应码,[flag]所表示的标志位用于控制此循环机制,如果替换后的URL是以http://或https://开头,则替换结果会直接以重定向返回给客户端, 即永久重定向 301

格式:

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,后两种是代理型:

  • 跳转型指由客户端浏览器重新对新地址进行请求
  • 代理型指是在WEB服务器内部实现跳转

一般做域名重写使用跳转型,做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错误的给用户
例1: 域名永久和临时重定向
location / {
	rewrite /a http://www.qq.com redirect;
	rewrite /b http://www.baidu.com permanent;
}

image

例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;
	}
}
例4: 自动跳转https

目前主流网站的实现方式都是此,比如访问: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;
		}
	}
}
例5:当用户访问到公司网站的时输入了一个错误的URL,可以将用户重定向至官网首页

京东商城的错误页跳转就是此实现的,在京东输入错误的页面,总是能跳转到首页
image

server {
	location / {
		if (!-e $request_filename){
			rewrite (.*) http://$host/index.html;
		}
	}
}
例6: 根据不同的客户端浏览器重定向
server {
	location / {
		if ( $http_user_agent ~* MSIE ){	#ie浏览器使用此url资源
			rewrite ^(.*)$ /msie/$1 break;
		}
		if ( $http_user_agent ~* chrome ){	#谷歌浏览器使用此url资源
			rewrite ^(.*)$ /chrome/$1 break;
		}
	}
}
例7: 生产场景更换目录访问方式

要求:/20201002/static --> /static?id=20201002

location / {
	rewrite ^/(\d+)/(\.+)/ /$2?id=$1 last;
	#或者
	rewrite ^/(\d+)/(.*)/ /$2?id=$1 last;
}
例8: 多级目录访问方式转换

要求: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
image

如果别人只链接了自己网站图片或某个单独的资源,而不是打开了网站的整个页面,如频繁访问图片、视频等,这就是盗链

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
		}
	}
}
posted @ 2022-02-13 14:28  suyanhj  阅读(111)  评论(0编辑  收藏  举报