文章目录
- 1. Rewrite功能配置
- 1.1 地址重写和地址重定向的区别
- 1.1.1 地址重写(Rewrite)
- 1.1.2 地址重定向(Redirection)
- 1.2 set指令
- 1.3 Rewrite功能常用的全局变量
- 1.4 if指令
- 1.4.1 变量名
- 1.4.2 比较变量和字符串是否相等
- 1.4.3 取反
- 1.4.4 使用正则表达式对变量进行匹配
- 1.4.5 判断请求的文件是否存在
- 1.4.6 判断请求的目录是否存在
- 1.4.7 判断请求的目录或者文件是否存在
- 1.4.8 判断请求的文件是否可执行
- 1.5 break指令
- 1.6 return指令
- 1.7 rewrite指令
- 1.8 rewrite_log指令
- 2. Rewrite案例实战
- 2.1 域名跳转
- 2.1.1 问题分析
- 2.1.2 准备工作
- 2.1.3 具体实现
- 2.1.4 如何在域名跳转的过程中保留请求原始的URL路径
- 2.2 域名镜像
- 2.2.1 概念解释
- 2.2.2 具体实现
- 2.3 独立域名
- 2.3.1 问题分析
- 2.3.2 准备工作
- 2.3.3 具体实现
- 2.4 目录自动添加"/"
- 2.5 合并目录
- 2.5.1 问题分析
- 2.5.2 具体实现
- 2.6 防盗链
如果想了解更多与Nginx相关的内容,可以查看Nginx专栏: Nginx
视频教程:01-Nginx的rewrite内容介绍
1. Rewrite功能配置
Nginx使用
ngx_http_rewrite_module
模块来解析和处理Rewrite功能的相关配置
Rewrite是Nginx服务器提供的一个重要基本功能,是Web服务器产品中几乎必备的功能,主要的作用是用来实现URL的重写
1.1 地址重写和地址重定向的区别
地址重写(Rewrite)和地址重定向(Redirection)是Web服务器中常用的两种技术,它们在处理HTTP请求时具有不同的功能和用途,两者在实现方式和用途上有所不同,但都可以用于管理Web应用的URL结构
- 地址重写:在服务器内部修改URL,不通知客户端,用于美化URL、映射等
- 地址重定向:通知客户端URL已更改,并要求客户端重新发送请求,用于网站迁移、URL更改等
1.1.1 地址重写(Rewrite)
地址重写(URL Rewriting)是指在服务器内部修改请求的URL,而不通知客户端。当请求到达服务器时,服务器根据预先定义的规则将原始URL修改为目标URL,然后使用修改后的URL来处理请求。客户端并不知道URL已经被修改,因为客户端只接收到最终处理结果
特性 | 描述 |
---|---|
浏览器地址变化 | 不会涉及客户端的重定向,因此浏览器的地址栏不会显示新的URL |
请求次数 | 通常只产生一次请求:原始请求被服务器内部重写,不会产生额外的客户端请求 |
路径要求 | 重写到的页面不一定需要是一个完整的路径,可以在服务器内部进行路径的修改 |
属性传递 | 由于地址重写是服务器内部的行为,request 范围内的属性可以直接传递给新页面 |
速度 | 由于不涉及客户端的重定向,速度通常较快 |
1.1.2 地址重定向(Redirection)
地址重定向(Redirection)是指服务器接收到请求后,返回一个状态码(通常是 3xx 系列状态码),指示客户端使用新的 URL 重新发送请求。客户端接收到重定向响应后,会使用新的 URL 发送新的请求。这个过程对客户端是可见的,因为客户端的浏览器地址栏会显示新的 URL
特性 | 描述 |
---|---|
浏览器地址变化 | 通常涉及客户端的重定向,因此浏览器的地址栏会显示新的URL |
请求次数 | 通常会产生两次请求:第一次是原始请求,第二次是重定向后的请求 |
路径要求 | 重定向到的页面通常需要是一个完整的路径,因为客户端需要使用新的URL重新发送请求 |
属性传递 | 由于重定向涉及两次请求,request 范围内的属性不能直接传递给新页面 |
速度 | 由于涉及客户端的重定向,速度通常较慢 |
1.2 set指令
set指令用于设置一个新的变量
语法 | 默认值 | 位置 |
---|---|---|
set $variable value; | server、location、if |
参数 | 描述 |
---|---|
variable | 变量的名称,该变量名称要用"$"作为变量的第一个字符,且不能与Nginx服务器预设的全局变量同名 |
value | 变量的值,可以是字符串、其他变量或者变量的组合等 |
1.3 Rewrite功能常用的全局变量
全局变量 | 说明 |
---|---|
$args | 存储了请求URL中的查询字符串。例如,在http://192.168.200.133:8080?arg1=value1&arg2=value2 中,$args 的值为"arg1=value1&arg2=value2"。与$query_string 功能相同 |
$http_user_agent | 存储了用户访问服务时的用户代理信息。如果是通过浏览器访问,通常会记录浏览器的类型和版本信息 |
$host | 存储了访问服务器的server_name 值,即主机名 |
$document_uri | 存储了当前访问地址的URL部分。例如,在http://192.168.200.133/server?id=10&name=zhangsan 中,$document_uri 的值为"/server"。它与$uri 功能相同 |
$document_root | 存储了当前请求对应location的root值,即文档根目录。如果未设置,默认指向Nginx自带的html目录所在位置 |
$content_length | 存储了请求头中的Content-Length 的值,表示请求体的长度 |
$content_type | 存储了请求头中的Content-Type 的值,表示请求体的媒体类型 |
$http_cookie | 存储了客户端发送的cookie信息。可以通过add_header Set-Cookie 'cookieName=cookieValue' 来添加或修改cookie数据 |
$limit_rate | 存储了Nginx服务器对网络连接速率的限制值,即limit_rate 指令设置的值。默认为0,表示不限制 |
$remote_addr | 存储了客户端的IP地址 |
$remote_port | 存储了客户端与服务端建立连接时使用的端口号 |
$remote_user | 存储了客户端的用户名,通常在启用认证模块时获取 |
$scheme | 存储了访问协议,如"http"或"https" |
$server_addr | 存储了服务端的IP地址 |
$server_name | 存储了客户端请求到达的服务器的名称 |
$server_port | 存储了客户端请求到达的服务器的端口号 |
$server_protocol | 存储了客户端请求使用的协议版本,例如"HTTP/1.1" |
$request_body_file | 存储了发给后端服务器的本地文件资源的名称。如果请求体被保存到文件中,这个变量将包含该文件的路径 |
$request_method | 存储了客户端的请求方法,如"GET"、"POST"等 |
$request_filename | 存储了当前请求的资源文件在服务器上的路径名 |
$request_uri | 存储了当前请求的完整URL,包括查询字符串。例如,在http://192.168.200.133/server?id=10&name=zhangsan 中,$request_uri 的值为"/server?id=10&name=zhangsan" |
1.4 if指令
if指令用来支持条件判断,并根据条件判断结果选择不同的Nginx配置
语法 | 默认值 | 位置 |
---|---|---|
if (condition) {…} | server、location |
condition为判定条件,可以支持以下写法
1.4.1 变量名
如果变量名对应的值为空或者是0,if指令都判断为false,其他条件为true
if ($param){}
1.4.2 比较变量和字符串是否相等
注意:字符串不需要添加引号
使用=
和!=
比较变量和字符串是否相等,满足条件为true,不满足为false
if ($request_method = POST){return 405;
}
1.4.3 取反
!
表示对if指令中的表达式的结果取相反值
1.4.4 使用正则表达式对变量进行匹配
注意:正则表达式字符串一般不需要加引号,但是如果字符串中包含
}
或者;
等字符时,就需要把引号加上
使用正则表达式对变量进行匹配,匹配成功返回true,否则返回false
- ~代表匹配正则表达式过程中区分大小写
~*
代表匹配正则表达式过程中不区分大小写
if ($http_user_agent ~ MSIE) {# $http_user_agent的值中是否包含MSIE字符串,如果包含则返回true
}
1.4.5 判断请求的文件是否存在
判断请求的文件是否存在使用-f
和!-f
当使用-f
时,如果请求的文件存在返回true
,不存在返回false
if (-f $request_filename) {# 判断请求的文件是否存在
}if (!-f $request_filename) {# 判断请求的文件是否不存在
}
1.4.6 判断请求的目录是否存在
判断请求的目录是否存在使用-d
和!-d
当使用-d
时,如果请求的目录存在,返回true
,如果目录不存在,返回false
1.4.7 判断请求的目录或者文件是否存在
判断请求的目录或者文件是否存在使用-e
和!-e
当使用-e
时,如果请求的目录或者文件存在时,返回true
,否则返回false
1.4.8 判断请求的文件是否可执行
判断请求的文件是否可执行使用-x
和!-x
当使用-x
,如果请求的文件可执行,返回true
,否则返回false
1.5 break指令
break指令指令用于中断当前相同作用域中的其他Nginx配置
语法 | 默认值 | 位置 |
---|---|---|
break; | server、location、if |
在 Nginx 配置文件中,break
指令用于终止当前上下文中的处理,并直接跳出该上下文
- 在
if
块中,break
指令可以用来在条件满足时立即终止if
块的执行,并跳到if
块之后的代码继续执行 - 在
location
块中,break
指令可以用来终止当前location
块的执行,并跳出该location
块。控制权将返回到外层的server
块,并继续匹配其他location
块 - 在
server
块中,break
指令可以用来终止当前server
块的执行,并跳出该server
块。控制权将返回到外层的http
或stream
块,并继续匹配其他server
块
server {listen 80;server_name 127.0.0.1;location /admin {# 只有管理员才能访问 /admin 路径auth_basic "Admin Area";auth_basic_user_file /etc/nginx/htpasswd;# 如果认证失败,则终止处理并返回 401 错误if ($auth_status = 401) {break;}# 其他处理指令}location / {# 其他路径的处理指令}}
1.6 return指令
在return指令之后的所有Nginx配置都是无效的
return指令指令用于完成对请求的处理,直接向客户端返回响应状态代码
语法 | 默认值 | 位置 |
---|---|---|
return code [text]; return code URL; return URL; | server、location、if |
参数 | 说明 |
---|---|
code | 返回给客户端的 HTTP 状态代码 |
text | 返回给客户端的响应体内容,支持变量的使用 |
URL | 返回给客户端的 URL 地址 |
1.7 rewrite指令
rewrite指令可以通过使用正则表达式来改变URL
语法 | 默认值 | 位置 |
---|---|---|
rewrite regex replacement [flag]; | server、location、if |
-
regex:用来匹配URL的正则表达式
-
replacement:匹配成功后,用于替换URL中被截取内容的字符串。如果该字符串是以"http://"或者"https://"开头的,则不会继续向下对URL进行其他处理,而是直接返回重写后的URL给客户端
-
flag:用来设置rewrite指令对URL的处理行为,可选值如下
- last:将控制权转移给下一个 location 块,通常用于将请求转发到不同的 location 处理
- break:终止当前 location 块中的处理,并返回重写后的 URL。如果没有任何 location 块匹配,则返回 404 错误
- redirect:将重写后的 URL 返回给客户端,并使用 302 临时重定向状态码
- permanent:将重写后的 URL 返回给客户端,并使用 301 永久重定向状态码
使用Nginx的rewrite
指令时,如果重写的replacement
(替换后的URL)以http://
或https://
开头,意味着你希望客户端重定向到一个新的完整URL,而不是在服务器内部处理这个请求。在这种情况下,Nginx会直接返回一个HTTP重定向响应给客户端
1.8 rewrite_log指令
rewrite_log指令用于开启URL重写日志的输出功能
语法 | 默认值 | 位置 |
---|---|---|
rewrite_log on|off; | rewrite_log off; | http、server、location、if |
开启rewrite_log指令后,与URL重写相关的日志将以notice级别输出到error_log指令配置的日志文件中
2. Rewrite案例实战
视频教程:09-Nginx的rewrite案例之域名跳转
视频教程中用的是Linux操作系统,本文演示的是Windows操作系统,整体步骤相差不多
2.1 域名跳转
2.1.1 问题分析
大家都知道,如果我们想访问京东网站,可以输入www.jd.com
,其实我们输入www.360buy.com
其实也都能访问到京东网站,这个其实是因为京东刚开始的时候域名就是www.360buy.com
,后面由于各种原因把自己的域名换成了www.jd.com
虽然说域名变了,但是对于以前只记住了www.360buy.com的用户来说,我们如何把这部分用户也迁移到我们的新域名上呢?针对这个问题,我们就可以使用Nginx中Rewrite的域名跳转功能来解决
2.1.2 准备工作
准备三个域名
Linux操作环境
vim /etc/hosts
Windows操作系统(修改hosts文件需要管理员权限)
C:\Windows\System32\drivers\etc\hosts
将以下内容粘贴到hosts文件后保存hosts文件(因为域名要收取一定的费用,我们通过修改hosts文件来制作一些虚拟域名)
127.0.0.1 www.itcast.cn
127.0.0.1 www.itheima.cn
127.0.0.1 www.itheima.com
2.1.3 具体实现
在nginx.conf文件中添加以下内容
server {listen 80;server_name www.itcast.cn;location / {default_type text/html;return 200 '<h1>Welcome to itcast!</h1>';}
}server {listen 80;# 通过Rewrite将www.ithema.com和www.itheima.cn的请求跳转到www.itcast.cnserver_name www.itheima.com www.itheima.cn;rewrite ^/ http://www.itcast.cn;
}
保存nginx.conf文件后运行以下指令重载配置文件
nginx -s reload
接着在浏览器访问www.itcast.cn网址,修改了哪台机器的hosts的文件,就用哪台电脑的浏览器访问
www.itcast.cn
如果在浏览器看到以下内容,就说明hosts文件修改成功了,Nginx的配置文件也成功重载了
如果在浏览器访问www.itcast.cn网址后跳转到了传智播客的官网,可能是浏览器最近访问过www.itcast.cn网址,浏览器有对应的DNS缓存,按下
CTRL + SHIFT + DELETE
快捷键清除浏览器缓存后再次访问就可以了
接着分别访问www.itheima.cn网址和www.itheima.com网址,如果浏览器的地址栏都能够成功跳转到www.itcast.cn,而且都能够看到以上的Welcome to itcast!
界面,就说明域名跳转功能成功实现了
如果在浏览器访问www.itheima.com网址后跳转到了传智播客的官网,可能是浏览器最近访问过www.itheima.com网址,浏览器有对应的DNS缓存,按下
CTRL + SHIFT + DELETE
快捷键清除浏览器缓存后再次访问就可以了
www.itheima.cn
www.itheima.com
2.1.4 如何在域名跳转的过程中保留请求原始的URL路径
如果直接通过rewrite ^/ http://www.itcast.cn;
指令实现域名跳转,原始的URL路径是不会保留的(请求携带的参数会保留)
例如,如果访问www.itheima.cn/user/findById?id=1网址,域名跳转后浏览器的地址栏会变成www.itcast.cn?id=1,我们需要借助正则表达式来解决这个问题
修改nginx.conf文件中与域名跳转相关的配置
server {listen 80;# 通过Rewrite将www.ithema.com和www.itheima.cn的请求跳转到www.itcast.cnserver_name www.itheima.com www.itheima.cn;# 将请求重定向到www.itcast.cn的同时保留原始的URL路径rewrite ^(.*) http://www.itcast.cn$1;
}
保存nginx.conf文件后运行以下指令重载配置文件
nginx -s reload
再次访问www.itheima.cn网址,并且在路径后添加URL
www.itheima.cn/user/findById?id=1
可以看到,浏览器的地址栏能够成功跳转到www.itcast.cn,而且请求原始的URL路径也保留下来了
2.2 域名镜像
2.2.1 概念解释
域名镜像,也称为网站镜像,是一种将相同内容的网站部署到多个服务器上的技术。这些服务器通常位于不同的地理位置,并使用不同的URL进行访问。其中,一个服务器上的网站被称为主站,而其他服务器上的网站则被称为镜像网站
镜像网站可以看作是主站的一个备份节点,它们在内容上与主站完全相同,但可能具有不同的访问速度和性能
域名镜像的主要优点包括:
- 加快响应速度:通过将网站内容分发到多个地理位置,用户可以从最近的服务器加载页面,从而减少加载时间,提高访问速度
- 平衡流量负载:当主站面临高流量负载时,镜像网站可以分担部分流量,防止服务器过载,提高网站的可用性和稳定性
- 解决网络限制:在某些地区或网络环境下,可能存在对特定域名的封锁或限制。通过使用镜像网站,用户可以从未被封锁的URL访问相同的内容
- 容错和备份:镜像网站可以作为主站的备份,当主站出现故障或维护时,用户仍然可以通过镜像网站访问内容,确保服务的连续性
2.2.2 具体实现
在域名跳转案例中,访问www.itheima.com和 www.itheima.cn都能跳转到www.itcast.cn,那么我们可以认为www.itcast.cn主域名,而www.itheima.com和 www.itheima.cn就是我们所说的镜像域名
如果我们不想把整个网站做镜像,只想为其中某一个子目录下的资源做镜像,我们可以在location块中配置rewrite功能
server {listen 80;server_name www.itheima.cn www.itheima.com;location /user {rewrite ^/user(.*)$ http://www.itcast.cn$1;}location /emp {default_type text/html;return 200 '<h1>Emp</h1>';}
}
重载nginx.conf配置文件后使用浏览器测试是否生效
www.itheima.cn/user/findById?id=1
www.itheima.cn/emp/findById?id=1
2.3 独立域名
2.3.1 问题分析
一个完整的项目包含多个模块,比如购物网站有商品搜索模块、商品详情模块和购物车模块等,我们如何为每一个模块设置独立的域名呢
需求如下
模块 | 访问地址 |
---|---|
商品搜索模块 | search.itcast.com:81 |
商品详情模块 | item.itcast.com:82 |
商品购物车模块 | cart.itcast.com:83 |
2.3.2 准备工作
准备三个域名
Linux操作环境
vim /etc/hosts
Windows操作系统(修改hosts文件需要管理员权限)
C:\Windows\System32\drivers\etc\hosts
将以下内容粘贴到hosts文件后保存hosts文件(因为域名要收取一定的费用,我们通过修改hosts文件来制作一些虚拟域名)
127.0.0.1 www.search.itcast.com
127.0.0.1 www.item.itcast.com
127.0.0.1 www.cart.itcast.com
2.3.3 具体实现
在nginx.conf文件中添加以下内容
server {listen 81;server_name search.itcast.com;rewrite ^(.*) http://www.itcast.cn/search$1;
}server {listen 82;server_name item.itcast.com;rewrite ^(.*) http://www.itcast.cn/item$1;
}server {listen 83;server_name cart.itcast.com;rewrite ^(.*) http://www.itcast.cn/cart$1;
}
接着在浏览器中分别访问以下三个域名
www.search.itcast.com:81
www.item.itcast.com:82
www.cart.itcast.com:83
2.4 目录自动添加"/"
我们通过以下例子来演示问题
server {listen 8082;server_name localhost;location /heima {root html;index index.html;}
}
通过http://192.168.200.133:8082/heima
访问和通过http://192.168.200.133:8082/heima/
访问有什么区别呢
如果不加斜杠,Nginx服务器内部会自动做一个301的重定向,根据有一个名叫server_name_in_redirect on|off;
的指令来决定重定向的地址
如果server_name_in_redirect
指令设置为on
- 重定向的地址为: http://server_name:8082/目录名/;
- http://localhost:8082/heima/
如果server_name_in_redirect
指令设置为off
- 重定向的地址为: http://原URL中的域名:8082/目录名/;
- http://192.168.200.133:8082/heima/
如果访问http://192.168.200.133:8082/heima时不加斜杠,那么按照上述规则,如果指令server_name_in_redirect设置为on,则301重定向后地址变为http://localhost:8082/heima/
如果指令server_name_in_redirect设置为off,则301重定向后地址变为http://192.168.200.133:8082/heima/
指令server_name_in_redirect设置为off时是正常的,设置为on时有问题
注意server_name_in_redirect指令在Nginx的0.8.48版本之前默认都是on,后来改成了off,所以使用较新版本的Nginx时不需要考虑这个问题,但是如果是0.8.48以前的版本并且server_name_in_redirect指令设置为on,我们需要通过rewrite来解决这个问题
我们可以使用rewrite功能为末尾没有斜杠的URL自动添加一个斜杠
server {# 监听80端口listen 80;# 设置服务器名称为localhostserver_name localhost;# 启用服务器名称用于重定向server_name_in_redirect on;# 匹配以/heima开头的请求location /heima {# 检查请求的文件名是否存在if (-d $request_filename){# 如果存在,重写URL,确保路径以斜杠结尾rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;}}
}
2.5 合并目录
2.5.1 问题分析
搜索引擎优化(SEO,Search Engine Optimization)是一种利用搜索引擎的搜索规则来提高网站在有关搜索引擎内排名的方式
我们在创建自己的站点时,可以通过很多种方式来提高网站在有关搜索引擎内的排名,其中有一项就是URL的目录层级一般不要超过三层
如果URL的目录层级超过三层,不仅利于搜索引擎的搜索,也给客户端的输入带来了负担,但是如果将所有的文件放在一个目录下又会导致文件资源管理混乱,并且访问文件的速度也会随着文件增多而慢下来,这两个问题是相互矛盾的,那么使用rewrite如何解决上述问题
举例,网站中有一个资源文件的访问路径是 /server/11/22/33/20.html,也就是说20.html存在于第4级目录下,如果想要访问该资源文件,客户端的URL地址就要写成 http://192.168.200.133/server/11/22/33/20.html
,但是这个URL是非常不利于SEO搜索引擎优化的,同时客户端也不好记
2.5.2 具体实现
我们可以进行如下配置
server {# 监听8083端口listen 8083;# 设置服务器名称为localhostserver_name localhost;# 定义一个location块,用于处理以/server开头的请求location /server {# 重写规则,将/server-XXXX-XXXX-XXXX-XXXX.html格式的URL重写为/server/1/2/3/4.html# 其中,([0-9]+)表示匹配一个或多个数字,后面的([0-9]+)同理# 重写后的URL中的1/2/3/4分别对应原始URL中的四个数字组rewrite ^/server-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /server/$1/$2/$3/$4.html last;root /path/to/your/document/root;index index.html index.htm;}
}
这样一来,客户只需要访问http://www.web.name/server-11-22-33-20.html就可以访问到20.html页面了,这也充分利用了rewrite指令支持正则表达式的特性
2.6 防盗链
之前我们已经介绍过防盗链相关的知识(参考我的另一篇博文:),在rewrite中的防盗链和之前讲的原理其实都是一样的,只不过通过rewrite可以将防盗链的功能进行完善下
当出现防盗链的情况时,我们可以使用rewrite将请求转发到自定义的一张图片和页面,给用户比较好的提示信息
# 定义一个location块,用于处理以/images开头的请求
location /images {# 验证请求的来源referrer,允许none、blocked以及来自www.baidu.com的请求valid_referers none blocked www.baidu.com;# 如果referrer验证失败if ($invalid_referer) {# 返回403禁止访问状态码# return 403;# 将请求重定向到/images/forbidden.png,并停止处理后续的请求rewrite ^/ /images/forbidden.png break;}# 设置根目录为html目录root html;
}