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;
}
例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,可以将用户重定向至官网首页
京东商城的错误页跳转就是此实现的,在京东输入错误的页面,总是能跳转到首页
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
如果别人只链接了自己网站图片或某个单独的资源,而不是打开了网站的整个页面,如频繁访问图片、视频等,这就是盗链
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
}
}
}