【Day4】Nginx实战训练营
12 nginx 的 rewrite 配置 - if
-
域名跳转(重定向)、URL 重写(伪静态)、动静分离(跳转域名,并接入 CDN 实现加速)
·依赖 PCRE 库
·模块:ngx_http_rewrite_module -
Rwrite 相关指令
if (条件) {command} -
if指令
格式:if (条件判断) { 具体的rewrite规则 }
条件举例
条件判断语句由Nginx内置变量、逻辑判断符号和目标字符串三部分组成。
其中,内置变量是Nginx固定的非自定义的变量,如,$request_method, $request_uri等。逻辑判断符号,有 = , != , ~ , ~* , !~ , !~*
! 表示相反的意思,~为匹配符号,它右侧为正则表达式,区分大小写,而~*为不区分大小写匹配。
目标字符串可以是正则表达式,通常不用加引号,但表达式中有特殊符号时,比如空格、花括号、分号等,需要用单引号引起来。
示例1
if ($request_method = POST) //当请求的方法为POST时,直接返回405状态码
{
return 405; //在该示例中并未用到rewrite规则,if中支持用return指令。
}
示例2
if ($http_user_agent ~ MSIE) //user_agent带有MSIE字符的请求,直接返回403状态码
{
return 403;
}
如果想同时限制多个user_agent,还可以写成这样
if ($http_user_agent ~ "MSIE|firefox|spider")
{
return 403;
}
示例3
if(!-f $request_filename) //当请求的文件不存在,将会执行下面的rewrite规则
{
rewrite 语句;
}
示例4
if($request_uri ~* 'gid=\d{9,12}/') //\d表示数字,{9,12}表示数字出现的次数是9到12次,如gid=123456789/就是符合条件的。
{
rewrite 语句;
}
参考:
http://note.youdao.com/noteshare?id=f90a325f7067ccf64d38a404831601b8&sub=70FC34A78563430AAB7C372F371673B3
13 nginx 中的 break 和 last
1、rewrite 中 的 break 和 last
两个指令用法相同,但含义不同,需要放到rewrite规则的末尾,用来控制重写后的链接是否继续被nginx配置执行(主要是rewrite、return指令)。
- 示例1(连续两条 rewrite 规则):
[root@alexis-01 conf.d]# vim www.1.com.conf
server{
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
rewrite /1.html /2.html;
rewrite /2.html /3.html;
}
rewrite_log on 指打开 rewrite log
在 nginx.conf 中,将 error_log 级别改为 notice; 打开
error_log /var/log/nginx/error.log notice
访问测试:
[root@alexis-01 ~]# cat /data/wwwroot/www.1.com/1.html
111111
[root@alexis-01 ~]# cat /data/wwwroot/www.1.com/2.html
222222
[root@alexis-01 ~]# cat /data/wwwroot/www.1.com/3.html
333333
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/1.html
333333
查看 error log 可以发现,请求被 rewrite 到了 3.html
[root@alexis-01 ~]# tail -4 /var/log/nginx/error.log
2019/10/28 21:13:41 [notice] 8689#8689: *11 "/1.html" matches "/1.html", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
2019/10/28 21:13:41 [notice] 8689#8689: *11 rewritten data: "/2.html", args: "", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
2019/10/28 21:13:41 [notice] 8689#8689: *11 "/2.html" matches "/2.html", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
2019/10/28 21:13:41 [notice] 8689#8689: *11 rewritten data: "/3.html", args: "", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
当我们请求 /1.html 时,rewrite 到了 /2.html,然后又从 /2.html rewrite 到了 /3.html,最终访问到的是 /3.html,两条 rewrite 规则先后执行。
2、break 和 last 在 location { } 外部
格式: rewrite xxxxx break;
- 示例2(增加 break ):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
}
当我们请求 /1.html 时,最终访问到的是 /2.html,就终止了
说明 break 在此示例中,作用是不再执行 break 以下的rewrite 规则。
访问测试:
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/1.html
222222
查看错误日志,发现只匹配到了一个,后面就没有继续执行了
[root@alexis-01 ~]# !tail
tail -4 /var/log/nginx/error.log
2019/10/28 21:16:33 [notice] 6934#6934: worker process 8689 exited with code 0
2019/10/28 21:16:33 [notice] 6934#6934: signal 29 (SIGIO) received
2019/10/28 21:16:47 [notice] 8698#8698: *12 "/1.html" matches "/1.html", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
2019/10/28 21:16:47 [notice] 8698#8698: *12 rewritten data: "/2.html", args: "", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
同理,将 break 改为 last ,效果是一样的
[root@alexis-01 ~]# vim /etc/nginx/conf.d/www.1.com.conf
server{
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
rewrite /1.html /2.html last;
rewrite /2.html /3.html;
}
[root@alexis-01 ~]# nginx -s reload
[root@alexis-01 ~]# !curl
curl -x127.0.0.1:80 www.1.com/1.html
222222
但,当配置文件中有 location 时,它还会去执行 location { } 段的配置(请求要匹配该 location)。
- 示例3(break 后面还有 location段):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
location /2.html {
return 403;
}
}
当请求 /1.html 时,最终会返回 403 状态码,说明它去匹配了 break 后面的 location { } 配置。
以上 2 个示例中,可以把 break 替换为 last ,它们两者起到的效果一模一样。
3、当 break 和 last 在location { } 里面时
- 示例4(什么都不加):
server{
listen 80;
server_name www.1.com;
root /data/wwwroot/www.1.com;
rewrite_log on;
location / {
rewrite /1.html /2.html;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
访问测试:
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/1.html
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.16.1</center>
</body>
</html>
当请求 /1.html,跳到 /2.html,又跳到 /3.html,跳出 location 后,进入 location /3.html,然后从 /3.html 最终将会访问 /b.html。
到 /b.html 还未结束,由于匹配到了 /,又到了 location / 下发现两条 rewrite 规则都不匹配,最终发回结果
而 /b.html 不存在,所以访问到了 404
- 示例5(增加 break ):
server{
listen 80;
server_name www.1.com;
root /data/wwwroot/www.1.com;
rewrite_log on;
location / {
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
访问测试
[root@alexis-01 ~]# !curl
curl -x127.0.0.1:80 www.1.com/1.html
222222
当请求 /1.html,最终会访问 /2.html
在 location { } 内部,遇到 break ,本 location { } 内以及后面的所有 location { } 内的所有指令都不再执行。
- 示例6(增加 last):
server{
listen 80;
server_name www.1.com;
root /data/wwwroot/www.1.com;
location / {
rewrite /1.html /2.html last;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
当请求 /1.html,最终会访问 /a.html
在 location { } 内部,遇到 last,本 location { } 内后续指令不再执行,而 rewrite 后的 url 再次从头开始,从头到尾匹配一遍规则。
在 location / 和 location /2.html 之间,会匹配后者,因为后者更精准,到了后一个 location,匹配了 /a.html,然后 /a.html 回去 location / 下去找,发现都不匹配,就直接返回 a.html 内容,而它不存在,所以 404 了。
-
结论
当 rewrite 规则在 location { } 外,break 和 last 作用一样,遇到 break 或 last 后,其后续的 rewrite/return 语句不再执行。但后续有 location { } 的话,还会近一步执行 location { } 里面的语句,当然前提是请求必须要匹配该 location。当 rewrite 规则在 location { } 里,遇到 break 后,本 location { } 与其他 location { } 的所有 rewrite/return 规则都不再执行。
当 rewrite 规则在 location { } 里,遇到 last 后,本 location { } 里后续 rewrite/return 规则不执行,但重写后的 url 再次从头开始执行所有规则,哪个匹配执行哪个。
-
参考:
http://note.youdao.com/noteshare?id=65a4d769f41ae9ae034922b3644284b2&sub=819C0913ECCE4B6396FEB120B1DF92CF
14 nginx 中的 return 用法
- nginx 的 return 指令
该指令一般用于对请求的客户端直接返回响应状态码。在该作用域内 return 后面的所有 nginx 配置都是无效的。
可以使用在 server、location 以及 if 配置中。
除了支持跟状态码,还可以跟字符串或者 url 链接。
1、直接返回状态码
- 示例1:
server{
listen 80;
server_name www.1.com;
return 403;
rewrite /(.*) /abc/$1; //该行配置不会被执行。
}
- 示例2:
server {
.....
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 404;
rewrite /(.*) /aaa.txt; //该行配置不会被执行。
}
//如果下面还有其他配置,会被执行。
.....
}
访问测试
[root@alexis-01 ~]# vim /etc/nginx/conf.d/www.1.com.conf
server{
listen 80;
server_name www.1.com;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 405;
rewrite /(.*) /aaa.txt;
}
}
[root@alexis-01 ~]# nginx -s reload
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 405 Not Allowed
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:16:31 GMT
Content-Type: text/html
Content-Length: 157
Connection: keep-alive
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasssssssswd -I
HTTP/1.1 404 Not Found
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:17:02 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive
因为机器里本来就没有要访问的文件,所以肯定会返回 404,那么把状态码改为 405 测试,如果没有匹配到,那么才是 404
2、返回字符串
- 示例3:
server{
listen 80;
server_name www.1.com;
return 200 "hello";
}
访问测试
[root@alexis-01 ~]# vim /etc/nginx/conf.d/www.1.com.conf
server{
listen 80;
server_name www.1.com;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 200 "hello";
rewrite /(.*) /aaa.txt;
}
}
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:21:23 GMT
Content-Type: application/octet-stream
Content-Length: 5
Connection: keep-alive
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd
hello
说明: 如果要想返回字符串,必须要加上状态码,否则会报错。
还可以支持 json 数据
- 示例4:
location ^~ /alexis {
default_type application/json ;
return 200 '{"name":"alexis","id":"100"}';
}
访问测试
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd
{"name":"alexis","id":"100"}
3、也支持写一个变量
- 示例5:
location /test {
return 200 "$host $request_uri";
}
访问测试
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd
www.1.com /123/.htpasswd
4、返回 url
- 示例6:
server{
listen 80;
server_name www.1.com;
return http://www.1.com/12345678.html;
}
访问测试
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:35:40 GMT
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Location: http://www.1.com/12345678.html
连接之前加状态码也可以
·改为 301
return 301 http://www.1.com/12345678.html;
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 301 Moved Permanently
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:38:34 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: http://www.1.com/12345678.html
·改为 200
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:40:10 GMT
Content-Type: application/octet-stream
Content-Length: 30
Connection: keep-alive
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd
http://www.1.com/12345678.html
注意: return 后面的 url 必须是以 http:// 或者 https:// 开头的。
5、生成场景实战,返回一段代码
背景: 网站被黑了,凡是在百度点击到本网站的请求,全部都跳转到了一个赌博网站。
- 通过nginx解决:
if ($http_referer ~ 'baidu.com')
{
return 200 "<html><script>window.location.href='//$host$request_uri';</script></html>";
}
如果写成:
return http://$host$request_uri;
在浏览器中会提示“重定向的次数过多”。
- 参考:
http://note.youdao.com/noteshare?id=0dae7f742625a2ff14a0f4f59b894a69&sub=74133821946C498D93A114668C11CF14
15 rewrite 规则语法
- rewrite 规则
格式: rewrite regex replacement [flag]
-
rewrite 配置可以在 server、location 以及 if 配置段内生效
-
regex 是用于匹配 URI 的正则表达式,其不会匹配到 $host(域名)
-
replacement 是目标跳转的 URI,可以 http:// 或者 https:// 开头,也可以省略掉 $host,直接写 $request_uri 部分(即请求的链接)
-
flag,用来设置 rewrite 对 URI 的处理行为,其中有 break、last、rediect、permanent,其中 break 和 last 在前面已经介绍过
rediect 和 permanent 的区别在于,前者为临时重定向(302),而后者是永久重定向(301),对于用户通过浏览器访问,这两者的效果是一致的。
但是,对于搜索引擎蜘蛛爬虫来说就有区别了,使用 301 更有利于 SEO。所以,建议 replacemnet 是以 http:// 或者 https:// 开头的 flag 使用 permanent。
- 示例1
location / {
rewrite /(.*) http://www.1.com/$1 permanent;
}
说明:.* 为正则表达式,用 () 括起来,在后面的 URI 中可以调用它,第一次出现的 () 用 $1 调用,第二次出现的 () 用 $2 调用,以此类推。
- 示例2
location / {
rewrite /.* http://www.1.com$request_uri permanent;
}
说明:在 replacement 中,支持变量,这里的 $request_uri 就是客户端请求的链接
- 示例3
server{
listen 80;
server_name www.1.com;
root /data/wwwroot/www.1.com;
index index.html;
rewrite /(.*) /abc/$1 redirect;
}
说明:本例中的 rewrite 规则有问题,会造连续循环,最终会失败,解决该问题有两个方案。
关于循环次数,经测试发现,curl 会循环50次,chrome 会循环80次,IE会循环120次,firefox 会循环20次。
- 示例4
server{
listen 80;
server_name www.1.com;
root /data/wwwroot/www.1.com;
index index.html;
rewrite /(.*) /abc/$1 break;
}
说明:在 rewrite 中使用 break,会避免循环。
- 示例5
server{
listen 80;
server_name www.1.com;
root /data/wwwroot/www.1.com;
index index.html;
if ($request_uri !~ '^/abc/')
{
rewrite /(.*) /abc/$1 redirect;
}
}
说明:加一个条件限制,也可以避免产生循环
访问测试
[root@alexis-01 ~]# vim /etc/nginx/conf.d/www.2.com.conf
server{
listen 80;
server_name www.2.com;
root /data/wwwroot/www.2.com;
location /
{
rewrite /(.*) /abc/$1 redirect;
}
}
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.2.com/1.html -I
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 15:05:56 GMT
Content-Type: text/html
Content-Length: 145
Location: http://www.2.com/abc/1.html
Connection: keep-alive
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.2.com/1.html -L
curl: (47) Maximum (50) redirects followed
//将配置文件中 redirect 改为 break 后
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.2.com/abc/1.html -I -L
HTTP/1.1 404 Not Found
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 15:10:15 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive
正确写法,一般按照 示例 5 写即可
- 参考:
http://note.youdao.com/noteshare?id=309d530e9fbab2b090811b25ec8a565d&sub=EF5AFCDC3C31425F9E88539F832CB914