nginx系列(二)之location与rewrite
http://blog.51niux.com/?id=126对nginx做了一些基本的介绍,这里对nginx里面一些经常用到的语法做记录。
一、location配置
location 指令的作用是根据用户请求的URI来执行不同的应用,URI就是根据用户请求到的网址URL进行匹配,匹配成功了进行相关的操作。
location的格式:location [=|~|~*|^~|@] /uri/ { … }
1.1 location分类
一个location可以用prefix string(前缀字符串)定义,也可以通过regular expression(正则表达式来定义)。我们可以通过使用不同的前缀,表达不同的含义,对于不同的前缀可以分为两大类:普通location和正则location,如果你的uri用正则,则你的正则前面必须添加~或者~*。
=:用于普通uri前,要求精确匹配,如果匹配成功,则停止搜索并用当前location处理此请求
~:表示uri包含正则,并且区分大小写
~* :表示uri包含正则,但不区分大小写
^~ : 表示在普通uri前要求Nginx服务器找到普通uri匹配度最高的那个location后,立即处理此请求,并不再进行正则匹配
^~和= : 都可以阻止继续匹配正则location两者的区别:“^~”依然遵守最大前缀原则,然后“=”是需要严格匹配
剩余的正则表达式:
^ :以什么开头的匹配
$ :以什么结尾的匹配
\:转义字符。可以转. * ?等
* :代表任意字符
@:内部访问符,一般用于错误页面等。
+ :匹配前面的子表达式一次或多次
? :匹配前面的子表达式零次或一次
1.2 location的匹配顺序
nginx服务器会首先会检查多个location中是否有普通的uri匹配,如果有多个匹配,会先记住匹配度最高的那个(例如:location /prefix/mid/ {} 和location /prefix/ {} ,对于HTTP 请求/prefix/mid/test.html ,前缀匹配的话两个location 都满足,选哪个?原则是:the most specific match ,于是选的是location /prefix/mid/ {} )。然后再检查正则匹配,这里切记正则匹配是有顺序的,从上到下依次匹配,一旦匹配成功,则结束检查,并就会使用这个location块处理此请求。如果正则匹配全部失败,就会使用刚才记录普通uri匹配度最高的那个location块处理此请求。当普通匹配的最长前缀匹配有符号“^~”的时候,就不会在匹配正则直接使用当前匹配的这个location块处理此请求。
使用符号“=”修饰符可以定义一个精确匹配的URI和位置,如果找到了一个精确的匹配,则搜索终止,例如,如果一个"/”请求频繁发生,定义“location =/"将加快这些请求的处理,一旦精确匹配只有就结束,这样的location显然不能包含嵌套location。“location / {}”是普通的最大前缀匹配,任何的uri肯定是以“/”开头,所以location / {} 可以说是默认匹配,当其他都不匹配了,则匹配默认匹配 。
1.3 location匹配顺序示例测试
location的匹配顺序:
(location =) > (location 完整路径 >) >(location ^~ 路径) >(location ~* 正则) >(location 路径)
location / { return 401; } location = / { return 402; } location /download/ { return 403; } location ^~ /images/ { return 501; } location ~* \.(gif|jpg|jpeg)$ { return 502; }
#location /和location = / 比较,优先匹配的是location = /,从上图中可以看出返回的是402.
#首先location =/ 肯定是不匹配了,所以没它什么事了,然后先优先匹配普通location,就剩location /了,好的先记录上,然后正则匹配了一圈发现都匹配不上,然后192.168.1.103/test也是属于/下面,所以返回401了。
location /download 和location /download/的差别:
#上面这个例子是比较直观的,如果是192.168.1.103/download,还是认为应该去/下面去找,如果是download/,就根据最大前缀,location /download/比location /前缀要大,所以就返回403了。(因为我们这样定义:location /download的话,那么192.168.1.103/download/和192.168.1.103/download没差别,都是返回403)
正则之前的匹配顺序:
#从上图看,首先我们定义了正则/images/和location ~* \.(gif|jpg|jpeg)$,第一个操作是应该匹配前者/images/,第二个操作是匹配结尾是jpg,但是都返回了501,说明正则是从上到下的顺序来匹配。(要是改成location /images/,就是普通匹配了就优先走正则了,就都返回502了。)
location 普通匹配与正则之间的顺序以及与精确匹配之间的顺序:
#从上图测试结果看,第一张图location /download/和location ~* \.(gif|jpg|jpeg)$都匹配上了,返回502.就说明虽然优先记录普通结果中的最大前缀记录,但是还是先看看正则有没有,发现正则可以匹配上就优先走正则了。
#从上图测试结果看,第二张图location = / 和location ~* \.(gif|jpg|jpeg)$都匹配上了,location /当然也匹配上了,但是已经有个location =/了就肯定没它啥事了,然后发现结果依旧返回502,所以可以得出结果是location = /相对比较精确,location = /这种设置就是精确到目录稍微深一点就去匹配别的去了,如:http://192.168.1.103/或者http://192.168.1.103这种,就是类似于域名首页啥的。
location ^~遵循最大前缀原则:
location ^~ /images/ { return 501; } location /images/1/{ return 503; }
#从上图中可以看出location /images/1/的优先级要高于location ^~ /images/,因为前者相对路径较深,“^~”依然遵守最大前缀原则,所以前者优先匹配。
location 精确匹配和最长匹配:
location /images/1/ { return 501; } location = /images/1/{ return 503; }
#从上图的结果看。当精确匹配和最长匹配相同时,匹配的是精确匹配。但是当深入到目录中的时候,精确匹配就匹配不上了,然后就走了正则的jpg等资源返回的502了。
location 最大长度匹配与非^~正则匹配之间的顺序:
location /images/1/ { return 501; } location ~ /images/1/ { return 502; }
#从上图可以看出当最长匹配跟非^~在一起进行匹配的时候,会优先匹配非^~的正则匹配。
1.4 location的一般示例
location = / { #第一种访问首页如:www.51niux.com或者192.168.1.103,就直接跳转到百度首页,当然更多的时候是跳转到自己的后端服务器。 proxy_pass http://wwww.baidu.com/index.php; } location ^~ /static/ { #一般我们都会将静态文件放到一个文件里面,当开头是/static/的目录匹配的话就会去/web/test1/static/目录下面查找资源文件 root /web/test1/static/; } location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ { #当匹配到后缀是jpg、css等的链接请求,就会去/web/test1/res/下面去查找资源,比如:http://192.168.1.103/dada/5.jpg,就会去/web/test1/res/dada/下面找有没有5.jpg。 root /web/test1/res/; } location ~ ^/7[0-9a-z]{ #当匹配到url是/以7开头后面是数字或者小写字母或者是数字和字母的组合,就匹配到了,就可以做一些操作如更改根站点,指向别的机器等。 root /web/test2/res/; } location ~ .*\.(php|php5)?$ { #当后缀是php或者php5的链接请求时候,会将动态请求交给fastcgi,也就是php的本地9000端口去处理,然后将处理后生成的结果以静态内容的形式发送给客户端,结合上面就算动静分离。 root html; #默认的html是nginx软件安装位置下的html目录 fastcgi_pass 127.0.0.1:9000; #这就是php的启动ip加端口 fastcgi_index index.php; #首页为index.php fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; include fastcgi.conf; }
#经过上面的配置,站点的根目录下面除了有个首页配置文件以外,剩下的基本全是目录,不同的链接请求去不同的目录里面去找资源。
#当然如果是一个简单的web服务器,就用location做下简单的动静分离就可以了,但是如果是规模比较大的web集群的话,会采用众多的子域名的形式,比如图片的以img.51niux.com的形式提供图片服务。
博文来自:www.51niux.com
二、rewrite精讲
2.1 rewrite规则介绍
rewrite主要的功能是实现URL的重写,nginx的rewrite规则采用PCRE,Perl兼容正则表达式的语法进行规则匹配,如果需要nginx的Rewrite功能,在编译nginx之前,需要编译安装pcre库。
通过rewrite规则,可以实现规则的URL、根据变量来做URL专项及选择配置。例如,一些使用MVC框架的郑旭只有一个入口,可以通过Rewrite来实现。一些动态URL地址需要伪装成静态HTML,便于搜索引擎抓取,也需要Rewrite来处理。一些由于目录结构、域名变化的旧URL,需要跳转到新的URL,也可以通过rewrite来处理。
ngx_http_rewrite_module 模块实现了对 url 的判断、正则匹配、重定向.
2.2 rewrite指令执行顺序
rewrite语法:rewrite regex replacement flag
默认: none
作用域: server, location, if
nginx rewrite执行执行顺序:
执行server块的rewrite指令
执行location匹配
执行选定的location中的rewrite指令
上述过程可能会嵌套、重复执行,如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件;循环超过10次,则返回500 Internal Server Error错误。
2.3 flag标志位
last : 相当于Apache的[L]标记,表示完成rewrite,停止处理后续rewrite指令集,last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,一般写在server和if中。
break : 停止处理后续rewrite指令集,break终止重写后的匹配,一般使用在的location中。
redirect : 返回302临时重定向,地址栏会显示跳转后的地址。如果replacement不是以http:// 或https://开始,返回302临时重定向
permanent : 返回301永久重定向,地址栏会显示跳转后的地址。
2.4 if指令与全局变量
if语法为:if(condition){...},对给定的条件condition进行判断。如果为真,大括号内的rewrite指令将被执行,if条件(condtion)可以是下列内容:
当表达式是一个变量时。如果值为空或任意以0开头的字符串都会被当作false,否则为true,为true就会执行{}里面的rewrite指令。
直接比较变量和内容时,使用=与!=来匹配是否完全精确相等,~正则表达式的匹配(~*不区分大小写的匹配,!~区分大小写的匹配)。
3. -f和!-f判断文件是否存在,-d和!-d来判断目录是否存在,-e和!-e来判断文件、链接、目录是否存在,-x和!-x来判断文件是否可执行。
nginx的预定义变量也就是if用的变量:
arg_PARAMETER #GET请求中变量名PARAMETER参数的值 args #GET请求中的参数 binary_remote_addr #二进制码形式的客户端地址 body_bytes_sent #传送页面的字节数 content_length #请求头中的Content-length字段 content_type #请求头中的Content-Type字段 cookie_COOKIE #cookie COOKIE的值 document_root #当前请求在root指令中指定的值 document_uri #与$uri相同 host #请求中的主机头(Host)字段,如果请求中的主机头不可用或者空,则为处理请求的server名称 hostname #机器名使用 gethostname系统调用的值 http_HEADER #HTTP请求头中的内容,HEADER为HTTP请求中的内容转为小写,-变为_ sent_http_HEADER #HTTP响应头中的内容,HEADER为HTTP响应中的内容转为小写,-变为_ sent_http_content_type #响应头中的 CONTENT-TYPE 字段 is_args #如果$args设置,值为"?",否则为"" limit_rate #这个变量可以限制连接速率 nginx_version #当前运行的nginx版本号 query_string #与$args相同 remote_addr #客户端的IP地址 remote_port #客户端的端口 remote_user #已经经过 ngx_auth_basic_module 验证的用户名 request_filename #当前连接请求的文件路径,由root或alias指令与URI请求生成 request_body #请求体,主要用于 proxy_pass 或 fastcgi_pass request_body_file #客户端请求主体信息的临时文件 request_completion #如果请求成功,设为"OK";如果请求未完成或者不是一系列请求中最后一部分则设为空 request_method #这个变量是客户端请求的动作,如GET或POST request_uri #包含一些客户端请求参数的原始URI,它无法修改 scheme #所用的协议,比如http或者是https server_addr #服务器地址,在完成一次系统调用后可以确定这个值,如果要绕开系统调用,则必须在listen中指定地址并且使用bind参数 server_name #服务器名称 server_port #请求到达服务器的端口号 server_protocol #请求使用的协议,通常是HTTP/1.0或HTTP/1.1 uri #请求中的当前URI(不带请求参数,参数位于$args),不同于浏览器传递的$request_uri的值,它可以通过内部重定向,或者使用index指令进行修改,如 /foo/bar.html proxy_add_x_forwarded_for # 获取的是客户端的真实ip地址 proxy_host #获取upstream的上游代理名称,例如upstream backend proxy_port #要代理到的端口 proxy_protocol_addr #代理头部中客户端的ip地址,或者是一个空的字符串 upstream_addr #代理到上游的服务器地址信息 upstream_cache_status #proxy的缓存状态,例如这里第一次访问为MISS,第二次访问时为HIT upstream_response_length #上游服务器响应报文的长度 upstream_response_time #上游服务器响应的时间 upstream_status #上游服务器响应的状态码
另外,还有个set指令用来创建自定义变量,语法格式为:set variable value;
博文来自:www.51niux.com
2.5 if与rewrite结合示例
示例1.if根据客户端进行跳转
location / { #这里我们再location里面定义了三个if,注意if不支持if...else这种语法,只支持if一重简单的判断。 root /web/test1; index index.html index.htm; if ($http_user_agent ~ MSIE) { #首先判断客户端的agent信息里面是否包含MSIE,如果条件为真的话,就执行下面的rewrite语句。 rewrite ^(.*)$ /msie/$1 break; #^(.*)$指定的 跳转到root指定的/web/test1目录下的/msie目录,$1代表的是^(.*)$,然后配上flag标记break。 } if ($http_user_agent ~ Firefox) { #这里是匹配浏览器是火狐的 rewrite ^(.*)$ /firefox/$1 break; #指向到了/firefox/目录下面 } if ($http_user_agent ~ Android) { #这里匹配是是手机客户端系统是android的 rewrite ^(.*)$ /android/$1 break; } }
# tree -L 2 /web/test1/ #先来查看一下我们的目录结构,根据下图可以看出,根目录下面只有一个index.html文件。
测试链接分别为(如果都返回200说明rewrite写的是争取的,因为只有匹配到才会去指向的目录里面去找相关文件):
http://192.168.1.103/haha/666.jpg(IE浏览器)
http://192.168.1.103/10.jpg(安卓手机)
http://192.168.1.103/haha/666.jpg(火狐浏览器)
下面是火狐浏览器的测试结果(测试结果是成功的,注意链接标红的地方,虽然我们后台寻找的目录已经发生了改变,但是前台显示的URL并没有发生变化):
查看一下日志信息:
192.168.1.115 - - [14/Jun/2017:15:20:22 +0800] "GET /da/7.jpg HTTP/1.1" "192.168.1.103" 200 109727 17 3 "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)" "-" "-" 0.017 423 - - #IE浏览器的日志信息 192.168.18.196 - - [14/Jun/2017:15:27:01 +0800] "GET /10.jpg HTTP/1.1" "192.168.1.103" 200 54200 18 1 "-" "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; KIW-AL10 Build/HONORKIW-AL10) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/6.0 Mobile Safari/537.36" "-" "-" 0.009 542 - - #手机浏览器的日志信息 192.168.1.115 - - [14/Jun/2017:15:27:15 +0800] "GET /haha/666.jpg HTTP/1.1" "192.168.1.103" 200 109727 20 1 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0" "-" "-" 0.012 393 - - #火狐浏览器的日志信息
示例2:if根据请求url做跳转(简单捋顺一下break、last、permanent)
了解一下permanent:(在location下面有一条if语句)
if ($request_uri ~ sex){ rewrite ^(.*)$ /no-s-e-x.html permanent; }
#从上如可以看出permanent会改变网页URL地址但是会继续向下匹配。
现在我们在同一个location里面有两行判断(了解一下permanent与break):
if ($request_uri ~ sex){ #判断如果请求的url里面带有sex的字样,我们就进行跳转 rewrite ^(.*)$ /no-s-e-x.html permanent; #这里是将所有的内容跳转到根目录下面的一个页面no-s-e-x.html,flag字样是permanent,也就是302状态。 } if ($http_user_agent ~ Firefox) { #紧跟着有一个判断是判断浏览器如果是火狐的话,就返回504状态码,然后将所有的内容指定到根目录下面的/firefox/目录下面,然后break。 return 504; rewrite ^(.*)$ /firefox/$1 break; }
#从上面的判断可以看出首先这个if是从上到下执行的,首先先匹配到了输入的url里面含有sex字符串,然后呢就发送了301的跳转,url发送了变化,然后呢竟然没有结束,会继续向下寻找,看一看这个location里面还有没有其他的if可以匹配上。然后我们下面正好有一条是匹配火狐浏览器的,然后就又匹配上了,因为已经发了跳转,所以交给第二个if的url变成了:192.168.1.103/no-s-e-x.html。
#然后就就有个奇怪的地方了,直接到return 504;就直接结束了,并没有到后面的跳转到/firefox目录阶段。是因为return指定是可以返回状态或者url并结束,注意是返回完就结束了。所以一个if里面不要同时存在return和rewrite两条要执行的操作(last和break的结果一样)。
#然后我们把return 504;注释掉,再次测试的话,会发现如果,/firefox目录下面有no-s-e-x.html就返回200状态码,如果不存在就返回404状态码。
结论:
结论1:permanent可以改变前端页面URL,而break和last不会改变前端URL地址。
结论2:permanent就算匹配成功了依旧会在location里面继续向下匹配,所以如果有permanent跳转页面设置应该尽量放到一个location的最下方。
结论3:一个if语句里面,return和rewrite要单独存在,不要同时存在,如果同时存在只会从上到下匹配第一条执行语句。
现在我们在同一个location里面有两行判断(了解一下last与break):
先让break在上面看下结果(在同一个location下面):
if ($request_uri ~ sex){ rewrite ^(.*)$ /login/no-s-e-x.html break; } if ($http_user_agent ~ Firefox) { rewrite ^(.*)$ /firefox/$1 break; }
查看日志信息:
192.168.1.115 - - [14/Jun/2017:17:59:04 +0800] "GET /sedasxdsadassex/html.html HTTP/1.1" "192.168.1.103" 200 7 15899 1 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0" "-" "-" 0.001 406 0.001 192.168.1.103:80
#为了方便区分,我firefox里面的no-s-e-x.html文件里面的内容是:firefox no sex,这样就能根据网页显示的内容判断最后到底走的是哪一条if规则。
结论:
flag标记中的break,如果上面的if判断条件判断成功了,就执行本条rewrite指令。不会再向下匹配,到我这条就结束了。是什么就返回什么。如果找不到页面就返回404.简单干脆。
然后我们让last再上面查看下结果(同一个location中):
if ($request_uri ~ sex){ rewrite ^(.*)$ /login/no-s-e-x.html last; } if ($http_user_agent ~ Firefox) { rewrite ^(.*)$ /firefox/$1 break; }
#这里就不截图了,比如你火狐浏览器输入:http://192.168.1.103/sexsedasxdsadassex/html.html,页面会返回一个500 Internal Server Error错误。为啥返回这个错误呢,是因为第一个匹配上了然后就转换成了/login/no-s-e-x.html文件,但是后面跟的是last而不是break,这就是不是结束了,这就是开始了,然后就开始在同一个server里面找能跟这个转换后的条件匹配上的location,而且是从上到下找三遍,找不到就返回500错误。
下面我们用一个例子验证一下:
location ~ /log { return 406; } location / { root /web/test1/; index index.html index.htm; if ($request_uri ~ sex){ rewrite ^(.*)$ /login/no-s-e-x.html last; } if ($http_user_agent ~ Firefox) { rewrite ^(.*)$ /firefox/$1 break; } } location ~ /login { return 405; }
#在last上下各有一个location,都是可以匹配上的,我们看一下last当它第一次匹配上要去执行的时候,是不是自上而下,还是先向下查找如果找不到再去上面找。
#从结果上面可以看出,last匹配一个server里面的location的话是自上而下的。注意是同一个server下面,不能跨server。
#那么我们用last稍微干点事情
location ~ /login { #被下面的规则所匹配,把执行任务交给了本location root /web/test1/; if ($http_user_agent ~ Firefox) { rewrite ^(.*)$ /firefox/no-s-e-x.html break; #url是没变化的,原封不动的交给我了,然后我想怎么rewrite就是我的事情了,这里我让他去/firefox下面去返回一个提示页面,不要sex } } location / { root /web/test1/; index index.html index.htm; if ($request_uri ~ sex){ rewrite ^(.*)$ /login/no-s-e-x.html last; } }
示例3、对url进行切割
location ~ ^/photos/ { rewrite "/photos/([0-9]{2})([0-9]{2})([0-9]{2})" http://192.168.1.103/$1/$2/$3/$1$2$3.jpg permanent; #这里应该是break,为了测试方便显示效果这里改成了跳转 }
#注意上面我们可以看到()里面是一个切割块,所以([0-9]{2})([0-9]{2})([0-9]{2}),对应的就是$1,$2,$3。然后就是对{}来说,他们既能用在重定向的正则表达式里,也能用正配置文件里分隔代码块,为了避免冲突,正则表达式里如果带花括号,应该用双引号或者单引号包围。
然后我们进行下测试:
第一个链接:http://192.168.1.103/photos/12345.png #没有匹配上,所以测试结果是没有跳转
第二个链接:http://192.168.1.103/photos/123456.png #匹配上了,测试结果是 http://192.168.1.103/12/34/56/123456.jpg,发现png文件变成了jpg文件
第三个链接:http://192.168.1.103/photos/12345678.png #匹配上了,测试结果是http://192.168.1.103/12/34/56/123456.jpg,发现文件只切割了前六位后两位78不见了
#从上面得到的结果是我们的匹配生效了,只是写的rewrite规则有点low,下面我们稍微改一下,再测试一下:
rewrite "/photos/([0-9]{2})([0-9]{2})([0-9]{1,})\.(png|jpg|gif)$" http://192.168.1.103/$1/$2/$3/$1$2$3.$4 permanent; #注意这里的\.,就是让这个.不是正则的意思,就是一个单纯的.。
然后再次进行测试:
第一个链接:http://192.168.1.103/photos/12345.png #测试结果是: http://192.168.1.103/12/34/5/12345.png
第二个链接:http://192.168.1.103/photos/12345678.png #测试结果是:http://192.168.1.103/12/34/5678/12345678.png
示例4、重定向参数问题
if ($query_string ~* "id=(\d+)$"){ #如果get请求参数中带有id=数字参数 set $id $1; #set就是设置变量让后给变量赋值,这里就设置了一个id的变量,然后把一堆数字参数赋值给了它(id)。要set一下让下面用$id,如果用$1会有问题。 rewrite ^/test\.php /test/$id.html permanent; #然后如果匹配到了http://域名/test.php?id=一堆数字,就发生跳转变成,http://域名/test/一堆数字.html }
测试链接:http://192.168.1.103/test.php?id=123 #测试结果:http://192.168.1.103/test/123.html?id=123 #会发现php的url已经变成了html,但是后面还跟着?id=123。稍微修改下设置:
if ($query_string ~* "id=(\d+)$"){ set $id $1; rewrite ^/test\.php /test/$id.html? permanent; #就是这里再html后面加了个?后缀。重定向的目标地址结尾处如果加了?号,则不会再转发传递过来原地址的问号?后面的参数那部分。 假如又想保留某个特定的参数,那又该如何呢?可以利用Nginx本身就带有的$arg_PARAMETER参数自行补充来实现。例如: 把http://192.168.1.103/test.php?para=xxx&p=xx 重写向到 http://192.168.1.103/new.php?p=xx 可以写成:rewrite ^/test.php /new.php?p=$arg_p? permanent; }
测试链接:http://192.168.1.103/test.php?id=123 #测试结果:http://192.168.1.103/test/123.html #测试结果后面已经没有?参数了。
示例5、域名间的跳转
先掌握下常用正则:
. : 匹配除换行符以外的任意字符
? :重复0次或1次
+ : 重复1次或更多次
* : 重复0次或更多次
\d : 匹配数字
^ : 匹配字符串的开始
$ :匹配字符串的结束
{n} : 重复n次
{n,} :重复n次或更多次
[a] : 匹配单个字符a
(a|b|c) :匹配单个字符a或者b或者c
[a-z]:匹配范围a-z,也就是匹配小写字母
\ :转义字符
server { listen 80; server_name 51niux.cn; rewrite (.+) $scheme://www.$host$1 permanent; #让访问51niux.com直接跳转到www.51niux.com。这里用到了两个变量,将请求都转发到http://www.51niux.com.com/后面。千万不要是$scheme://www.51niux.com/$1,不然结果是http://www.51niux.com.com//内容。
如:url:http://51niux.cn/ceshi/ceshi/ai 会直接跳转成:http://www.51niux.cn/ceshi/ceshi/ai
2.6 简单防盗链的示例
以前是各种防盗链,现在是各种反爬虫。不过设置了防盗链,对于抵制一般的小站来盗链,降低网站的带宽损失还是有好处的。
先了解nginx referer模块:
当一个请求头的Referer字段中包含一些非正确的字段,这个模块可以禁止这个请求访问站点。
这个头可以随意的伪造,因此,使用这个模块并不能100%的阻止这些请求,绝大多数拒绝的请求来自一些典型的浏览器,可以认为这些典型的浏览器并不能提供一个”Referer”头,甚至是那些正确的请求。
再了解下valid_referers 指令:
语法:valid_referers [none|blocked|server_names] …
默认值:no
使用字段:server, location
这个指令在referer头的基础上为 $invalid_referer 变量赋值,其值为0或1。可以使用这个指令来实现防盗链功能,如果valid_referers列表中没有Referer头的值, $invalid_referer将被设置为1。
参数可以使如下形式:
none :意为不存在的Referer头(表示空的,也就是直接访问,比如直接在浏览器打开一个图片)。通常我们用浏览器打开网站,如果你记录$http_referer"字段。会发现其再日志里是-,也就是一个空值。
blocked :意为根据防火墙伪装Referer头,如:“Referer: XXXXXXX”。里面的值被代理或者防火墙删除了,这些值都不以http://或者https://开头.
server_names :为一个或多个服务器的列表,0.5.33版本以后可以在名称中使用“*”通配符。
现在是在server区域里面定义的一个防盗链的location:
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { #这里就是以我们指定的这些jpg啊等等的结尾的文件 root /web/test1; valid_referers none blocked *.baidu.com$; #用valid_referers指令定义了一个列表,里面有node、blocked 还有baidu.com的所有域名(多个域名列表之间用空格隔开) if ($invalid_referer) { #如果访问这个location的来源地址不是node blocked或者是来源于百度,$invalid_referer就被设置成1了,也就匹配上这里了。 return 403; #然后就可以对这个非法的来源返回一个http状态,或者可以用rewrite重定向一个我们编写好的网页或者什么。 } }