目录
nginx rewrite
Nginx工作原理
nginx由内核和模块组成
模块由结构分为:
核心模块: HTTP 模块、EVENT 模块和 MAIL 模块
基础模块: HTTP Access 模块、HTTP FastCGI 模块、HTTP Proxy 模块和 HTTP Rewrite 模块
第三方模块: HTTP Upstream Request Hash 模块、Notice 模块和 HTTP Access Key 模块
由功能分为:
Handlers(处理器模块): 此类模块直接处理请求,并进行输出内容和修改 headers 信息等操作。Handlers 处理器模块一般只能有一个
Filters(过滤器模块):此类模块主要对其他处理器模块输出的内容进行修改操作,最后由 Nginx 输出
Proxies(代理类模块): 此类模块是 Nginx 的 HTTP Upstream 之类的模块,这些模块主要与后端一些服务比如FastCGI 等进行交互,实现服务代理和负载均衡等功能。
Rewrite模块
ngx_http_rewrite_module
模块用于使用PCRE正则表达式更改请求URI,返回页面重定向,和按条件选择配置。
ngx_http_rewrite_module
模块指令按以下顺序处理:
- 处理在server级别中定义的模块指令;
- 为请求查找location;
- 处理在选中的location中定义的模块指令。如果指令改变了URI,按新的URI查找location。这个循环至多重复10次,之后nginx返回错误500 (Internal Server Error)。
语法:
语法:
**rewrite** *regex* *replacement* [*flag*];
默认值:—上下文:
server
,location
,if
如果指定的正则表达式能匹配URI,此URI将被*replacement*
参数定义的字符串改写。
rewrite
指令按其在配置文件中出现的顺序执行。flag可以终止后续指令的执行。
如果replacement的字符串以“http://
”或“https://
”开头,nginx将结束执行过程,并返回给客户端一个重定向。
例如:
语法:rewrite regex replacement flag;
,如:
rewrite ^/images/(.*\.jpg)$ /imgs/$1 break;
此处的$1用于引用(.*.jpg)匹配到的内容,又如:
rewrite ^/bbs/(.*)$ http://www.idfsoft.com/index.html redirect;
如上例所示,replacement可以是某个路径,也可以是某个URL
可选的*flag*
参数:
flag | 作用 |
---|---|
last | 基本上都用这个flag,表示当前的匹配结束,继续下一个匹配,最多匹配10个到20个 一旦此rewrite规则重写完成后,就不再被后面其它的rewrite规则进行处理 而是由UserAgent重新对重写后的URL再一次发起请求,并从头开始执行类似的过程 |
break | 中止Rewrite,不再继续匹配 一旦此rewrite规则重写完成后,由UserAgent对新的URL重新发起请求, 且不再会被当前location内的任何rewrite规则所检查 |
redirect | 以临时重定向的HTTP状态302返回新的URL |
permanent | 以永久重定向的HTTP状态301返回新的URL |
rewrite模块的作用是用来执行URL重定向。这个机制有利于去掉恶意访问的url,也有利于搜索引擎优化(SEO)
nginx使用的语法源于Perl兼容正则表达式(PCRE)库,基本语法如下:
标识符 | 意义 |
---|---|
^ | 必须以^后的实体开头 |
$ | 必须以$前的实体结尾 |
. | 匹配任意字符 |
[] | 匹配指定字符集内的任意字符 |
[^] | 匹配任何不包括在指定字符集内的任意字符串 |
| | 匹配 | 之前或之后的实体 |
() | 分组,组成一组用于匹配的实体,通常会有 | 来协助 |
捕获子表达式,可以捕获放在()之间的任何文本,比如:
^(hello|sir)$ //字符串为“hi sir”捕获的结果:$1=hi$2=sir
//这些被捕获的数据,在后面就可以当变量一样使用了
模块应用场景:
地址跳转,例如换新域名后,让旧域名跳转到新域名上
协议跳转,用户通过http协议请求网站时,将其重新跳转至https协议方式
伪静态,很多企业会将动态URL地址伪装成静态地址提供服务,减少过多的参数暴露
搜索引擎,SEO优化依赖于url路径,好记的url便于智齿搜索引擎录入
企业会将动态URL地址伪装成静态地址提供服务
网址换新域名后,让旧的访问跳转到新的域名.上
服务端某些业务调整
实例
#nginx的默认根目录下的images目录没有数据,而/opt/imgs下有名字加1.jpg的图片
[root@www ~]# ls /usr/local/nginx/html/images/
[root@www ~]# ls /opt/imgs/
1.jpg
#当访问images下以.jpg结尾的文件时转到/opt/imgs下的.jpg文件
......
location / {
root html;
index index.html index.htm;
}
location /images {
root /opt;
rewrite ^/images/(.*\.jpg)$ /imgs/$1 break;
}
......
URI是/images/1.jpg,而/images下是没有数据的,说明确实跳转到了/opt/imgs/1.jpg
也可以用last这样写
[root@www ~]# vim /usr/local/nginx/conf/nginx.conf
......
location / {
root html;
index index.html index.htm;
}
location /images {
rewrite ^/images/(.*\.jpg)$ /imgs/$1 last;
}
location /imgs {
root /opt;
}
[root@www ~]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@www ~]# nginx -s reload
......
一样可以访问,这里在处理last之前的指令集时,会停止处理后续rewrite指令集,跳出当前的location作用域,并开始搜索与更改后的URI相匹配的location,同时URL地址是不变的
if
Syntax: if (condition) { ... }
Default: —
Context: server, location
计算指定的condition的值。如果为真,执行定义在大括号中的rewrite模块指令,并将if指令中的配置指定给请求。if指令会从上一层配置中继承配置。
条件可以是下列任意一种:
- 变量名;如果变量值为空或者是以“0”开始的字符串,则条件为假;
- 使用“=”和“!=”运算符比较变量和字符串;
- 使用“~”和“~*” (区分大小写和不区分大小写)运算符匹配变量和正则表达式。正则表达式可以包含匹配组,匹配结果后续可以使用变量$1..$9引用。如果正则表达式中包含字符“}”或者“;”,整个表达式应该被包含在单引号或双引号的引用中。
- 使用“-f”和“!-f”运算符检查文件是否存在;
- 使用“-d”和“!-d”运算符检查目录是否存在;
- 使用“-e”和“!-e”运算符检查文件、目录或符号链接是否存在;
- 使用“-x”和“!-x”运算符检查可执行文件;
nginx一些内置变量
变量名 | 说明 |
---|---|
$arg_name | 指URL请求中的参数,name是参数的名字 |
$args | 代表URL中所有请求的参数 |
$binary_remote_addr | 客户端地址以二进制数据的形式出现,通常会和限速模块一起使用 |
$body_bytes_sent | 发送给客户端的字节数,不包含响应头 |
$bytes_set | 发送给客户端的字节总数 |
$document_uri | 设置$uri的别名 |
$hostname | 运行Nginx的服务器名 |
$http_referer | 表示请求是从哪个页面链接过来的 |
$http_user_agent | 客户端浏览器的相关信息 |
$remote_addt | 客户端IP地址 |
$remote_port | 客户端端口号 |
$remote_user | 客户端用户名,通常在Auth Basic模块中使用 |
$request_filename | 请求的文件路径,基于root alias指令和URI请求生成 |
$request_time | 请求被nginx接收后,一直到相应数据返回给客户端所用时间 |
$request_uri | 请求的URI,带参数 |
$request | 记录请求的URL和HTTP |
$request_length | 请求的长度,包括请求行、请求头和请求正文 |
$server_name | 虚拟主机的server_name的值,通常是域名 |
$server_port | 服务器端口号 |
$server_addr | 服务器的IP地址 |
$request_method | 请求的方式,如GET、POST |
$scheme | 请求的协议,如HTTP、HTTPS |
$sent_http_name | 任意响应头,name为响应头的名字,注意name要小写 |
$realip_remote_addr | 保留原来的客户地址,在real_ip模块中使用 |
$server_protocol | 请求采用的协议名称和版本号,常为HTTP/1.0或HTTP1.1 |
$uri | 当前请求的URI,在请求过程中URI可能会改变,例如在内部重定向或使用索引文件时 |
$nginx_version | Nginx版本号 |
$pid | worker进程的PID |
$pipe | 如果请求是HTTP流水线发送的,pipe值为“p”,否则为“.” |
$connection_request | 当前通过一个连接获得的请求数量 |
$cookie_name | name即Cookie名字,可得到Cookie信息 |
$status | HTTP请求状态 |
$msec | 日志写入时间。单位为秒,经度为毫秒 |
$time_local | 在通用日志格式下的本地时间 |
$upstream_addr | 请求反向代理到后端服务器的IP地址 |
$upstream_port | 请求反向代理到后端服务器的端口号 |
$upstream_response_time | 请求在后端服务器消耗的时间 |
$upstream_status | 请求在后端你服务器的HTTP响应状态 |
$geoip_city | 城市名称,在geoip模块中使用 |
基于浏览器实现分离案例
[root@www ~]# mkdir /usr/local/nginx/html/{QQBrowse,Edg,Chrome}
[root@www ~]# echo 'QQBrowse test page..........' > /usr/local/nginx/html/QQBrowser/index.html
[root@www ~]# echo 'EdgBrowser test page..........' > /usr/local/nginx/html/Edg/index.html
[root@www ~]# echo 'ChromeBrowser test page..........' > /usr/local/nginx/html/Chrome/index.html
[root@www ~]# vim /usr/local/nginx/conf/nginx.conf
......
location / {
if ($http_user_agent ~ Edg) {
rewrite ^(.*)$ /Edg/$1 break;
}
if ($http_user_agent ~ Chrome) {
rewrite ^(.*)$ /Chrome/$1 break;
}
root html;
index index.html index.htm;
}
location /Edg {
root html;
index index.html;
}
location /Chrome {
root html;
index index.html;
}
......
[root@www ~]# nginx -s reload
谷歌浏览器访问
微软浏览器访问
防盗链案例
如果来源不是www.test.com的就返回403
location ~* \.(jpg|gif|jpeg|png)$ {
valid_referers none blocked server_names www.test.com;
if ($invalid_referer) {
return 403;
#rewrite ^/ http://www.test.com/403.html;
}
}
1、none
"Referer" 来源头部为空的情况
2、blocked
"Referer"来源头部不为空,但是里面的值被代理或者防火墙删除了,这些值都不以http://或者https://开头.
3、server_names
"Referer"来源头部包含当前的server_names(当前域名)