需求:
之前公司使用的是apache,现在想把apache撤掉需要把之前apache上rewrite转移到nginx
遇到的问题:
Q1 :不确定rewrite写的对不对
A1:打开nginx rewrite log
修改nginx配置文件为debug
error_log logs/error_www2.log debug;
就可以在error_www2.log 中看到rewrite log
apache rewrite log查看方法
在配置文件的rewrite下面log上面添加
#RewriteRule........
LogLevel alert rewrite:trace8
#ErrorLog "|/usr/local/apache/bin/rotatelogs -l /data/logs/httpd/rewrite.5pao.com-error.log 86400"
就可以在rewrite.5pao.com-error.log中查看rewrite日志了
Q2:已经确定rewrite写的没错(日志中可正常看到转发),但是转发到上游tomcat后地址还是无法被找到,此功能用于搜索。
排查步骤:
P1:
在网页搜索编码号时返回正常,但是搜索中文就会变成404
中文搜索时tomcat日志报错:
2020-04-08 15:36:36,493 INFO UserFilter:48 - ***[shopmm]有恶意参数入侵,参数为:sptype=3&xinyu=&xyfrom=&xyto=2&xnyuzk=&shopstype=111&stprice=&enprice=&jiasg=&sarea=&shophour=&sthaopn=&xunibi=&spingf=&skouf=&sdaih=&shuoy=&sid=&zhank=&doubletab=&teshuzz=%e5%8d%8e%e5%8d%97&xinyuorder=&pxfs=&sjpx=&lrpx=&yyepx=&qitype=&qigohu=&brandid=&zizhiid=&page=1
参数不能被识别,查看代码
![](https://img.haomeiwen.com/i13799508/d5c2cb7ed5907fe1.png)
此时查看可能和中文转码有关
P2:
查看nginx转过来的参数
spType=%26sbtype=%26zctype=%26shopsType=%26stPrice=%26enPrice=%26jiasg=%26sarea=%26shopHour=%26shuoy=%26steam=%26sjyan=%26sid=%25E5%258D%258E%25E4%25B8%259C%26zhank=%26doubleTab=%26xinyuOrder=%26pxfs=%26sjpx=%26lrpx=%26yyepx=%26brandId=%26sctype=&nsrzz=&page=1.html
nginx 最后rewrite的参数是被二次转码的,二次转码的参数传递给上游tomcat端不被识别
P3:
查看原apache配置时的rewrite log
spType=&sbtype=&zctype=&shopsType=&stPrice=&enPrice=&jiasg=&sarea=&shopHour=&shuoy=&steam=&sjyan=&sid=%E5%8D%8E%E4%B8%9C&zhank=&doubleTab=&xinyuOrder=&pxfs=&sjpx=&lrpx=&yyepx=&brandId=&sctype=&nsrzz=&page=1.html
apache最后rewrite的参数是一次转码的,传递个tomcat后可被正常使用
P4:
经过多次测试发现,这个问题应该和rewrite和apache中文编码有关,
http://www.jsons.cn/urlencode/
转码发现,nginx rewrite后的指解码后可以得到apache rewrite的值参数值就可以被tomcat接受
现在有两种解决方法:
a 修改代码,添加urldecoder转码
![](https://img.haomeiwen.com/i13799508/0001b6b4535d6017.png)
b 修改nginx,让它只转码一次,这样参数到tomcat中就可以被识别
P5:
仔细查看nginx rewrite日志发现,(我配置的nginx 配置文件中有多次rewrite)
前几次的rewrite都是一次转码,最后一次带有args参数后就被再次转码
rewritten data: "/shopmm/tmp--1.htmlspType=32&sbtype=&zctype=&shopsType=&stPrice=&enPrice=&jiasg=&sarea=&shopHour=&shuoy=&steam=&sjyan=&sid=%E5%B0%8F%E7%BA%A2%E4%B9%A6&zhank=&doubleTab=&xinyuOrder=&pxfs=&sjpx=&lrpx=&yyepx=&brandId=&sctype=", args: ""
rewritten data: "/qitaIndexNew.do", args: "spType=32%26sbtype=%26zctype=%26shopsType=%26stPrice=%26enPrice=%26jiasg=%26sarea=%26shopHour=%26shuoy=%26steam=%26sjyan=%26sid=%25E5%25B0%258F%25E7%25BA%25A2%25E4%25B9%25A6%26zhank=%26doubleTab=%26xinyuOrder=%26pxfs=%26sjpx=%26lrpx=%26yyepx=%26brandId=%26sctype=&nsrzz=&page=1"
所以思路是:将args解码一次并传递给tomcat即可
参考:
P6:
解码需要使用到set_unescape_uri,此命令需要单独安装set-misc-nginx-module模块
安装方法:
set_unescape_uri模块安装
cd /usr/local/src
wget https://github.com/openresty/set-misc-nginx-module/archive/v0.31.tar.gz
wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz
tar zxvf v0.31.tar.gz
tar zxvf v0.3.0.tar.gz
找到源码包
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_sub_module --with-http_realip_module --with-http_addition_module --with-http_gzip_static_module --with-http_stub_status_module --with-pcre --with-http_stub_status_module --with-http_dav_module --add-module=/soft/src/lua-nginx-module-0.10.13 --add-module=/soft/src/ngx_devel_kit-0.3.0 --add-module=/soft/src/set-misc-nginx-module-0.31
注意添加最后两条:
--add-module=/soft/src/ngx_devel_kit-0.3.0 --add-module=/soft/src/set-misc-nginx-module-0.31
且--add-module=ngx_devel_kit一定要早于--add-module=set-misc-nginx-module
make
注意不需要make install
将objs/nginx 替换原有sbin/nginx即可
参考:
使用方法:
set_unescape_uri
语法: set_unescape_uri $dst <src> / set_unescape_uri $dst
默认值: no
配置段:location, location if
当两个参数时,该指令将非转义第二个参数<src>的值作为URI一部分,并分配第一个参数变量$dst分配结果。如:
location /test {
set_unescape_uri $key $arg_key;
echo $key;
}
当请求GET /test?key=hello+world%21时,得到:hello world!。
注意: nginx标准的变量$arg_PARAMETER保存原始的URI参数(转义过的),因此需要set_unescape_uri指令来非转义先。
当单个参数时,该指令将修改参数变量位置,如:
location /test {
set $key $arg_key;
set_unescape_uri $key;
echo $key;
}
当请求GET /test?key=hello+world%21时,得到:hello world!。
参考:
http://www.ttlsa.com/nginx/nginx_set-misc-nginx-module-module-description/
P7
在原来rewrite基础上添加
set_unescape_uri $new_args $args ;
rewrite ^(.*)$ /tmallIndexNew.do?;
rewrite ^(.*)$ /tmallIndexNew.do?$new_args break;
set_unescape_uri args ;
首先将得到的参数解码后赋予给新值
$args值:"spType=32%26sbtype=%26zctype=%26shopsType=%26stPrice=%26enPrice=%26jiasg=%26sarea=%26shopHour=%26shuoy=%26steam=%26sjyan=%26sid=%25E5%25B0%258F%25E7%25BA%25A2%25E4%25B9%25A6%26zhank=%26doubleTab=%26xinyuOrder=%26pxfs=%26sjpx=%26lrpx=%26yyepx=%26brandId=%26sctype=&nsrzz=&page=1"
$new_args值:spType=32&sbtype=&zctype=&shopsType=&stPrice=&enPrice=&jiasg=&sarea=&shopHour=&shuoy=&steam=&sjyan=&sid=%E5%B0%8F%E7%BA%A2%E4%B9%A6&zhank=&doubleTab=&xinyuOrder=&pxfs=&sjpx=&lrpx=&yyepx=&brandId=&sctype=&nsrzz=&page=1
rewrite ^(.*)$ /tmallIndexNew.do?;
rewrite ^(.*)$ /tmallIndexNew.do?$new_args break;
重新rewrite之前的url,为什么要加
rewrite ^(.*)$ /tmallIndexNew.do?;
”因为直接rewrite ^(.*)$ /tmallIndexNew.do?$new_args break;得到的url会出现两个$args值,影响搜索(下一个rewrite会带有上一个rewrite的参数)
所以曲折一下,先获取参数值,再rewrite没有参数页面,再rewrite解码后传递的参数的url
此时域名可以被正常搜索不报404了
知识点
1 中文转码
a. 为什么要进行url编码
b. 怎么编码
c. 有哪些细节需要注意
url编码是一种传输url的机制, 个人认为最主要的作用是区分字符是否有特殊意义. 我们都知道有许多字符在url中是有特殊含义的, 比如?
作为参数开始的标志, 又比如&
作为参数切分标志. 因此如果想要让这些 字符保留字符原本含义就需要对它们进行url编码. 另外一个作用是用来传递非可视字符. url编码的主要内容是: 0-9A-Za-z
以及-
, ~
, .
, ~
无需通过编码即可传输(普通字符), 而以下字符(保留字符):
! | * | ‘ | ( | ) | ; | : | @ | & | = | + | $ | , | / | ? | # | [ | ] |
如果要当作字符常量对待则需要经过编码. 另外其他的字符都需要进过url编码来进行传输. 编码形式是%
后跟对应的十六进制形式.
这里需要注意的有两点:
a. 编码并非是强制性, 即使是普通字符我们也可以进行编码然后进行传输
b. 在参数部分, 空格会被编码成+
而非%25
, 参见这里的The application/x-www-form-urlencoded type一节, 所以会造成经过base64编码的内容直接传输时, 加号在经过解码之后变为空格. 针对这个问题衍生出了其他的base64方式(主要是替换了/
, +
)
参考:
http://blog.aka-cool.net/blog/2013/07/09/url-encode-in-nginx/
url了解及其转义参考
2 nginx last和break
last: 相当于Apache的[L]标记,表示完成rewrite
break: 停止执行当前虚拟主机的后续rewrite指令集
redirect: 返回302临时重定向,地址栏会显示跳转后的地址
permanent: 返回301永久重定向,地址栏会显示跳转后的地址
这里 last 和 break 区别有点难以理解:
last一般写在server和if中,而break一般使用在location中
last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配
break和last都能组织继续执行后面的rewrite指令
3 变量
$request_uri变量
这个$request_uri就是完整url中刨去最前面$host剩下的部分,比如http://www.baidu.com/pan/beta/test1?fid=3这个url,去掉www.baidu.com剩下的就是了,日志里会看到打印出来的$request_uri其实是/pan/beta/test1?fid=3。如果只访问www.baidu.com,$request_uri里也会有个/的
if ($request_uri ~* "^/$") 表示url中只有域名,后面不跟任何东西,比如www.baidu.com。
if ($request_uri ~* "test") 表示域名后面那串儿只要包含test这个关键词,就可匹配成功。比如www.baidu.com/pan/beta/test3
$args变量
举栗子:
http://localhost:8080/test?a=0&b=1&c=2'
args:a=0&b=1&c=2值?后面的变量
注意:多次rewrite后,下一次的rewrite会带着前一个args,导致args中会有多于变量。
默认的情况下,Nginx在进行rewrite后都会自动添加上旧地址中的参数部分,而这对于重定向到的新地址来说可能是多余。虽然这也不会对重定向的结果造成多少影响
参考:
今天在给某网站写rewrite重定向规则时,碰到了这个关于重定向的参数处理问题。默认的情况下,Nginx在进行rewrite后都会自动添加上旧地址中的参数部分,而这对于重定向到的新地址来说可能是多余。虽然这也不会对重定向的结果造成多少影响,但当你注意到新地址中包含有多余的“?xxx=xxx”时,心里总还是会觉得不爽。那么该如何来处理这部分的内容呢?看了下面两个简单的例子你就会明白了。
例如:
把http://example.com/test.php?para=xxx 重定向到 http://example.com/new
若按照默认的写法:rewrite ^/test.php(.*) /new permanent;
重定向后的结果是:http://example.com/new?para=xxx
如果改写成:rewrite ^/test.php(.*) /new? permanent;
那结果就是:http://example.com/new
所以,关键点就在于“?”这个尾缀。假如又想保留某个特定的参数,那又该如何呢?可以利用Nginx本身就带有的$arg_PARAMETER参数来实现。
例如:
把http://example.com/test.php?para=xxx&p=xx 重写向到 http://example.com/new?p=xx
可以写成:rewrite ^/test.php /new?p=$arg_p? permanent;
只求结果的朋友可以直接忽略前面的内容,看这里:
rewrite ^/test.php /new permanent; //重写向带参数的地址
rewrite ^/test.php /new? permanent; //重定向后不带参数
rewrite ^/test.php /new?id=$arg_id? permanent; //重定向后带指定的参数
permanent是永久重定向参数,根据需要去掉也可以,不过最好是带有。
参考301重定向与302重定向的区别
地址:[https://blog.csdn.net/zhsh87/article/details/8191264](https://blog.csdn.net/zhsh87/article/details/8191264)
args相同
其它变量可参考
4 解决Nginx反向代理不会自动对特殊字符进行编码的问题
参考:
网友评论