Nginx分布式框架详解72-79静态资源访问-02
rewrite之rewrite指令
该指令通过正则表达式的使用来改变 URI。可以同时存在一个或者多个指令,按照顺序依次对 URL 进行匹配和处理。
URL 和 URI 的区别:
- URI:统一资源标识符
- URL:统一资源定位符
语法 | 默认值 | 位置 |
---|---|---|
rewrite regex replacement [flag]; | — | server、location、if |
regex:用来匹配 URI 的正则表达式
replacement:匹配成功后,用于替换 URI 中被截取内容的字符串。如果该字符串是以 『 http:// 』或者『 https:// 』开头的,则不会继续向下对URI 进行其他处理,而是直接返回重写后的 URI 给客户端。
例如:(括号的值会作为 $1 的值)^ 代表匹配输入字符串的起始位置
server {
listen 80;
charset utf-8;
default_type text/plain;
location /rewrite {
rewrite ^/rewrite/url\w*$ https://www.baidu.com;
rewrite ^/rewrite/(test)\w*$ /$1; # 如果访问/rewrite/testxxx,则从写url为test
rewrite ^/rewrite/(demo)\w*$ /$1; # 如果访问/rewrite/demoxxx,则重写url为demo
}
location /test {
return 200 test_success;
}
location /demo {
return 200 demo_success;
}
}
访问 http://192.168.200.113/8081/rewrite/urlxxx,跳转到 https://www.baidu.com。
访问 http://192.168.200.113/8081/rewrite/testxxx,返回 test_sucess。
访问 http://192.168.200.113/8081/rewrite/demoxxx,返回 demo_sucess。
flag:用来设置 Rewrite 对 URI 的处理行为,可选值有如下:
- last:终止继续在本 location 块中处理接收到的后续 URI,并将此处重写的 URl 作为一个新的 URI,使用各 location 块进行处理。该标志将重写后的 URI 重写在 server 块中执行,为重写后的 URI 提供了转入到其他 location 块的机会。重写地址后访问其他的 location 块,浏览器地址栏 URL 地址不变
# ......
listen 8081;
location /rewrite {
rewrite ^/rewrite/(test)\w*$ /$1 last; # 如果是 /rewrite/testxxx,则重写 url 为 test
rewrite ^/rewrite/(demo)\w*$ $1 last; # 如果是 /rewrite/demoxxx,则重写 url 为 demo
}
location /test { # 重写后的 url 如果为 test,触发 location
default_type text/plain;
return 200 test_sucess;
}
location /demo { # 重写后的 url 如果为 demo,触发 location
default_type text/plain;
return 200 demo_sucess;
}
访问 http://192.168.200.113/8081/rewrite/testxxx,返回 test_sucess。
访问 http://192.168.200.113/8081/rewrite/demoxxx,返回 demo_sucess。
单次访问不明显,多次访问,last 只处理第一个。
- break:将此处重写的 URl 作为一个新的 URI,在本块中继续进行处理。该标志将重写后的地址在当前的 location 块中执行,不会将新的 URI 转向其他的 location 块。仅仅重写地址,不会触发其他 location 块,浏览器地址栏 URL 地址不变
# ......
listen 8081;
location /rewrite {
rewrite ^/rewrite/(test)\w*$ /$1 break; # 如果是 /rewrite/testxxx,则重写 url 为 test
rewrite ^/rewrite/(demo)\w*$ $1 break; # 如果是 /rewrite/demoxxx,则重写 url 为 demo
# /test 和 /demo 就在当前块进行处理,所以会在当前的 location 块找到如下 html 页面:
# /usr/local/nginx/html/test/index.html
# /usr/local/nginx/html/demo/index.html
}
location /test { # 重写后的 url 如果为 test,触发 location
default_type text/plain;
return 200 test_sucess;
}
location /demo { # 重写后的 url 如果为 demo,触发 location
default_type text/plain;
return 200 demo_sucess;
}
和 break 指令类似。假设访问的是 /test,则将 /test 放在当前的 location 块进行处理,哪怕第二个 location 块就是处理 /test 的,它也不会去找第二个 location 块,只在当前块进行处理。所以他会请求 /usr/local/nginx/html/test/index.html
- redirect:将重写后的 URI 返回给客户端,状态码为 302,指明是临时重定向 URL,主要用在 replacement 变量不是以『 http:// 』或者『 https:// 』开头的情况
# ......
listen 8081;
location /rewrite {
rewrite ^/rewrite/(test)\w*$ /$1 redirect; # 如果是 /rewrite/testxxx,则重写 url 为 test
rewrite ^/rewrite/(demo)\w*$ $1 redirect; # 如果是 /rewrite/demoxxx,则重写 url 为 demo
}
location /test { # 重写后的 url 如果为 test,触发 location
default_type text/plain;
return 200 test_sucess;
}
location /demo { # 重写后的 url 如果为 demo,触发 location
default_type text/plain;
return 200 demo_sucess;
}
特点是重定向,就是浏览的地址栏会发送改变。如发送请求 /testxxx,它会重定向到 /test,触发第二个 location 块,浏览的地址栏也会由 /testxxx 变成 /test。
- permanent:将重写后的 URI 返回给客户端,状态码为 301,指明是永久重定向 URL,主要用在 replacement 变量不是以『 http:// 』或者『 https:// 』开头的情况
# ......
listen 8081;
location /rewrite {
rewrite ^/rewrite/(test)\w*$ /$1 permanent; # 如果是 /rewrite/testxxx,则重写 url 为 test
rewrite ^/rewrite/(demo)\w*$ $1 permanent; # 如果是 /rewrite/demoxxx,则重写 url 为 demo
}
location /test { # 重写后的 url 如果为 test,触发 location
default_type text/plain;
return 200 test_sucess;
}
location /demo { # 重写后的 url 如果为 demo,触发 location
default_type text/plain;
return 200 demo_sucess;
}
和 redirect 的区别就是状态码为 301,并且是永久重定向。
flag总结
标记符号 | 说明 |
---|---|
last | 本条规则匹配完成后继续向下匹配新的 location URI 规则 |
break | 本条规则匹配完成后终止,不在匹配任何规则 |
redirect | 返回 302 临时重定向 |
permanent | 返回 301 永久重定向 |
- break 与 last 都停止处理后续重写规则,只不过 last 会重新发起新的请求并使用新的请求路由匹配location,但 break 不会。所以当请求 break 时,如匹配成功,则请求成功,返回 200;如果匹配失败,则返回 404
- 服务器配置好 redirect 和 permanent 之后,打开浏览器分别访问这两个请求地址,然后停止 Nginx 服务。这时再访问 redirect 请求会直接报出无法连接的错误。但是 permanent 请求是永久重定向,浏览器会忽略原始地址直接访问永久重定向之后的地址,所以请求仍然成功。(这个验证不能禁用浏览器的缓存,否则即使是 permanent 重定向,浏览器仍然会向原始地址发出请求验证之前的永久重定向是否有效)
- 对于搜索引擎来说,搜索引擎在抓取到 301 永久重定向请求响应内容的同时也会将原始的网址替换为重定向之后的网址,而对于 302 临时重定向请求则仍然会使用原始的网址并且可能会被搜索引擎认为有作弊的嫌疑。所以对于线上正式环境来讲,尽量避免使用 302 跳转
- 如果 replacement 以 「 http:// 」或「 https:// 」或「 $scheme 」开始,处理过程将终止,并将这个重定向直接返回给客户端
rewrite之rewrite_log指令
该指令配置是否开启 URL 重写日志的输出功能,默认关闭。
语法 | 默认值 | 位置 |
---|---|---|
rewrite_log <on | off>; | rewrite_log off; |
开启后,URL 重写的相关日志将以 notice 级别输出到 error_log 指令配置的日志文件汇总。
server {
listen 80;
charset utf-8;
default_type text/plain;
location /rewrite {
rewrite_log on;
error_log logs/error.log notice;
rewrite ^/rewrite/(test)\w*$ /$1 permanent; # 如果访问/rewrite/testxxx,则从写url为test
rewrite ^/rewrite/(demo)\w*$ /$1 permanent; # 如果访问/rewrite/demoxxx,则重写url为demo
}
}
rewrite案例之-域名跳转
先来看一个效果,如果我们想访问京东网站,大家都知道我们可以输入 www.jd.com,但是同样的我们也可以输入 www.360buy.com 同样也都能访问到京东网站。这个其实是因为京东刚开始的时候域名就是 www.360buy.com,后面由于各种原因把自己的域名换成了 www.jd.com,虽然说域名改变了,但是对于以前只记住了 www.360buy.com 的用户来说,我们如何把这部分用户也迁移到我们新域名的访问上来,针对于这个问题,我们就可以使用 Nginx 中 Rewrite 的域名跳转来解决。
环境准备
准备两个域名 flask-test.mayanan.cn | mayanan.cn
server {
listen 80;
server_name mayanan.cn;
}
server {
listen 80;
server_name flask-test.mayanan.cn;
rewrite ^/ http://mayanan.cn permanent; # 永久重定向
}
问题描述:如何在域名跳转的过程中携带请求的 URI?
比如 flask-test.mayanan.cn?part=显示器 变成 mayanan.cn?part=显示器
- 修改配置信息
server {
listen 80;
server_name flask-test.mayanan.cn flask-demo.mayanan.cn;
rewrite ^(.*) http://mayanan.cn$1 permanent;
}
rewrite案例之域名镜像
上述案例中,将 www.360buy.com 和 www.jingdong.com 都能跳转到 www.jd.com,那么 www.jd.com 我们就可以把它起名叫主域名,其他两个就是我们所说的镜像域名,当然如果我们不想把整个网站做镜像,只想为其中某一个子目录下的资源做镜像,比如用户可以跳到首页 Web下,而管理员跳转到后台 Web,我们可以在 location 块中配置 Rewrite 功能。
比如:
server {
listen 80;
server_name mayanan.cn;
location / {
default_type text/html;
return 200 '<html><head><meta charset="UTF-8"></head><h1>这是index首页</h1></html>';
}
}
server {
listen 80;
server_name flask-test.mayanan.cn flask-demo.mayanan.cn;
location /user {
rewrite ^/user(.*)$ http://mayanan.cn$1;
}
location /emp {
default_type text/html;
return 200 OK;
}
}
rewrite案例之独立域名
一个完整的项目包含多个模块,比如购物网站有商品商品搜索模块、商品详情模块已经购物车模块等,那么我们如何为每一个模块设置独立的域名。
需求:
http://search.product.com:访问商品搜索模块
http://item.product.com:访问商品详情模块
http://cart.product.com:访问商品购物车模块
server{
listen 80;
server_name search.product.com;
rewrite ^(.*) http://www.shop.com/search$1 last;
}
server{
listen 81;
server_name item.product.com;
rewrite ^(.*) http://www.shop.com/item$1 last;
}
server{
listen 82;
server_name cart.product.com;
rewrite ^(.*) http://www.shop.com/cart$1 last;
}
rewrite案例之目录自动添加斜杠 /
有时候访问的地址要求后面以 / 结尾,那么我们需要解决如果用户忘记输入 /,Nginx 就会自动加上 /。
通过一个例子来演示问题:
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html;
}
}
要想访问上述资源,很简单,只需要通过 http://192.168.200.133 直接就能访问,地址后面不需要加 /,但是如果将上述的配置修改为如下内容:
server {
listen 80;
server_name localhost;
location /frx {
root html;
index index.html;
}
}
这个时候,要想访问上述资源,按照上述的访问方式,我们可以通过 http://192.168.200.133/frx/ 来访问,但是如果地址后面不加斜杠,如 http://192.168.200.133/frx,页面就会出问题。如果不加斜杠,Nginx 服务器内部会自动做一个 301 的重定向,重定向的地址会有一个指令叫 server_name_in_redirect 来决定重定向的地址:
如果该指令为 on
重定向的地址为:http://server_name/目录名/
如果该指令为 off
重定向的地址为:http://原URL中的域名/目录名/
所以就拿刚才的地址来说,访问 http://192.168.200.133/frx 如果不加斜杠,那么按照上述规则:
如果指令 server_name_in_redirect 为 on,则 301 重定向地址变为 http://localhost/frx/,IP 发生改变,地址出现了问题
如果指令 server_name_in_redirect 为 off,则 301 重定向地址变为 http://192.168.200.133/frx/。这个符合我们的期望
注意 server_name_in_redirect 指令在 Nginx 的 0.8.48 版本之前默认都是 on,之后改成了 off,所以现在我们这个版本不需要考虑这个问题,但是如果是 0.8.48 以前的版本并且 server_name_in_redirect 设置为 on,我们如何通过 Rewrite 来解决这个问题?
解决方案
我们可以使用 Rewrite 功能为末尾没有斜杠的 URL 自动添加一个斜杠
server {
listen 80;
server_name localhost;
server_name_in_redirect on;
location /frx {
if (-d $request_filename){ # 如果请求的资源目录存在
rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent; # $2 获取第二个括号的值:/
}
}
}
$1 是第一个括号的值,$2 是第二个括号的值。
rewrite案例之目录合并
搜索引擎优化(SEO)是一种利用搜索引擎的搜索规则来提供目的网站的有关搜索引擎内排名的方式。我们在创建自己的站点时,可以通过很多中方式来有效的提供搜索引擎优化的程度。其中有一项就包含 URL 的目录层级,一般不要超过三层,否则的话不利于搜索引擎的搜索,也给客户端的输入带来了负担,但是将所有的文件放在一个目录下,又会导致文件资源管理混乱,并且访问文件的速度也会随着文件增多而慢下来,这两个问题是相互矛盾的,那么使用 Rewrite 如何解决这些问题呢?
举例,网站中有一个资源文件的访问路径 /server/11/22/33/44/20.html,也就是说 20.html 存在于第 5 级目录下,如果想要访问该资源文件,客户端的 URL 地址就要写成 http://www.web.com/server/11/22/33/44/20.html,并且在配置文件进行如下配置:
server {
listen 80;
server_name mayanan.cn;
location /server {
root html;
index index.html;
}
}
请求路径:http://mayanan.cn/server/11/22/33/44/20.html
但是这个是非常不利于 SEO 搜索引擎优化的,同时客户端也不好记。使用 Rewrite 的正则表达式,我们可以进行如下配置:
server {
listen 80;
server_name mayanan.cn;
location /server {
# rewrite ^/server-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /server/$1/$2/$3/$4/$5.html last;
rewrite ^/server-(\d+)-(\d+)-(\d+)-(\d+)-(\d+)\.html$ /server/$1/$2/$3/$4/$5.html last;
}
}
浏览器请求:http://mayanan.cn/server-11-22-33-44-20.html
这里也充分利用了 Rewrite 指令支持正则表达式的特性。
rewrite案例之防盗链
防盗链之前我们已经介绍过了相关的知识,在 Rewrite 中的防盗链和之前将的原理其实都是一样的,只不过通过 Rewrite 可以将防盗链的功能进行完善下,当出现防盗链的情况,我们可以使用 Rewrite 将请求转发到自定义的一张图片和页面,给用户比较好的提示信息。
下面有两个配置实例:
根据文件类型实现防盗链配置:
server {
listen 80;
server_name mayanan.cn;
location ~* ^.+\.(gif|jpg|png|swf|flv|rar|zip)$ {
valid_referers none blocked server_names flask-test.mayanan.cn; # server_names 后指定具体的域名或者 IP
if ($invalid_referer){
rewrite ^/ http://flask-demo.mayanan.cn/images/forbidden.png; # 跳转到默认地址
}
}
}
server {
listen 80;
server_name flask-demo.mayanan.cn;
}
根据目录实现防盗链配置:
server{
listen 80;
server_name www.web.com;
location /file {
root /server/file; # 资源在 server 目录下的 file 目录里
valid_referers none blocked server_names *.web.com; # server_names 后指定具体的域名或者 IP
if ($invalid_referer){
rewrite ^/ http://www.web.com/images/forbidden.png; # 跳转到 file 目录下的图片
}
}
}
访问限流
我们构建网站是为了让用户访问它们,我们希望用于合法访问。所以不得不采取一些措施限制滥用访问的用户。这种滥用指的是从同一 IP 每秒到服务器请求的连接数。因为这可能是在同一时间内,世界各地的多台机器上的爬虫机器人多次尝试爬取网站的内容。
# 限制用户连接数来预防 DOS 攻击
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
# 限制同一客户端 ip 最大并发连接数
limit_conn perip 2;
# 限制同一server最大并发连接数
limit_conn perserver 20;
# 限制下载速度,根据自身服务器带宽配置
limit_rate 300k;
链接超时
长时间占着连接资源不释放,最终会导致请求的堆积,Nginx 处理请求效率大大降低。所以我们对连接的控制都要注意设置超时时间,通过超时机制自动回收资源、避免资源浪费。
# 客户端、服务端设置
server_names_hash_bucket_size 128;
server_names_hash_max_size 512;
# 长连接超时配置
keepalive_timeout 65;
client_header_timeout 15s;
client_body_timeout 15s;
send_timeout 60s;
# 代理设置
# 与后端服务器建立连接的超时时间。注意这个一般不能大于 75 秒
proxy_connect_timeout 30s;
proxy_send_timeout 120s;
# 从后端服务器读取响应的超时
proxy_read_timeout 120s;