nginx重定向post请求

场景:
app里访问的接口地址为/service/upload
然而现在项目中实际地址为/servicev3/upload
之所以这样是开发的时候沟通不畅,没有更新为新地址,ios已经上线,无法短时间内上新版。
临时解决办法就想到用nginx的rewrite,结果发现不成功。

这里做了一个测试。
rewrite 方式:

location /testpost {
 rewrite /testpost http://www.test.org/post.php;
}

用postman发起一个post请求 :

192.168.10.101 - [11/Sep/2017:10:24:06 +0800] POST /testpost HTTP/1.1 302 160 - "PostmanRuntime/3.0.11-hotfix.2" -

302跳转

192.168.10.101 - [11/Sep/2017:10:24:06 +0800] GET /post.php HTTP/1.1 200 44 http://www.test.org/testpost "PostmanRuntime/3.0.11-hotfix.2" -

跳转到post.php,但是请求方式变为get,这样post的数据就无法获取到。

后来查资料,需要用proxy_pass方式:

location /testpost {
 proxy_pass http://www.test.org/post.php;
}
192.168.10.101 - [11/Sep/2017:10:28:23 +0800] POST /testpost HTTP/1.1 200 74 - "PostmanRuntime/3.0.11-hotfix.2" -
192.168.10.50 - [11/Sep/2017:10:28:23 +0800] POST /post.php HTTP/1.0 200 63 - "PostmanRuntime/3.0.11-hotfix.2" -

这样才看到post的数据。

个人认为 rewrite(跳转) 可能不支持post重定向,需要通过proxy_pass(反向代理)来实现。

评论关闭

nginx反向代理,后端服务器获取真实ip原理

环境:nginx做反向代理,apache做后端服务器

nginx部分配置代码:
upstream apache{
server 127.0.0.1:8080; # 后端真实服务器地址及端口
}

server {
listen 80;

server_name www.a.com;
root /usr/share/nginx/html;

location / {
proxy_pass http://apache;
proxy_set_header ClientIpGetFromNginx $remote_addr;
}

首先先去看一下nginx内置的变量: http://blog.csdn.net/iinel/article/details/4321383
变量 $remote_addr 代表客户端ip地址

通常来说nginx反向代理会添加一个请求头
proxy_set_header X-Forwarded-For $remote_addr;
以此传递客户端ip到后端服务器。

为了方便理解,我这里改一下头的名称:
proxy_set_header ClientIpGetFromNginx $remote_addr;

此时用浏览器访问一下,去查看后端服务器的访问日志如下
127.0.0.1 – - [01/Sep/2017:10:31:10 +0800] 后面内容省略···
可以看到客户端ip为127.0.0.1也就是nginx的ip,(我nginx和后端服务器在一起)

这样一来获取的ip是错误的,那怎样获取正确ip呢?

先来看一下apache日志格式。

<IfModule log_config_module>
LogFormat “%h %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\”" combined
LogFormat “%h %l %u %t \”%r\” %>s %b” common

<IfModule logio_module>
LogFormat “%h %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\” %I %O” combinedio
</IfModule>
CustomLog “logs/access_log” combined
</IfModule>

combined、common、combinedio是apache的3中日志格式,默认用 combined 方式

就需要修改
LogFormat “%h %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\”" combined

LogFormat “%{ClientIpGetFromNginx}i %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\”" combined

这样一来,就等于从nginx的请求头中取 ClientIpGetFromNginx 变量做为日志开头的ip,即客户端ip
上面i的意思就是从请求头中取ClientIpGetFromNginx,i就代表请求头
可以参考apache日志格式: http://blog.sina.com.cn/s/blog_672c5a470100xj7z.html

保存配置,再访问一次,再查看apache的访问日志:
192.168.10.105 – - [01/Sep/2017:11:50:41 +0800] 后面内容省略···

可以看到ip是 192.168.10.105 ,这才是客户端的真实ip。

总结:
nginx获取客户端ip是用$remote_addr变量,这个ip是真实的。
后端服务器如果用$remote_addr获取,那么这个ip其实是nginx的ip。
如果nginx设置了传递变量X-Forwarded-For $remote_addr,那么后端用X-Forwarded-For取真实ip

在没有反向代理或CDN的情况下,是不能用X-Forwarded-For获取客户端ip的。因为浏览器是不会发送这个字段的,如果用程序模拟一个访问,这个值是可以被伪造的。

可能会有个想法,如果设置ip传递为
proxy_set_header remote_addr $remote_addr;

是不是就可以不用修改日志格式或者修改代码了?
结果是不行的,会出现一条这样的日志:
127.0.0.1 – - [01/Sep/2017:13:53:42 +0800] 后面内容省略···
ip是127.0.0.1,是nginx的ip地址。表明$remote_addr是不能伪造的。

在上面的设置,通过php中一个数组 $_SERVER 可以获取到:
$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。

通过以下php代码可以看到:
<?php

foreach ($_SERVER as $k => $v)
{
echo $k . “============” . $v . “</br>”;
}

保存为1.php,然后去访问这个页面(当然必须有php运行环境)。结果如下

在阿里云官方网站文档中 https://help.aliyun.com/knowledge_detail/40535.html 也可以看到关于ecs获取客户端ip的方法。是一样的。

知识点:
为啥叫 X-Forwarded-For 而不是别的呢?
这是因为当时的squid之类的缓存软件用的比较广,软件官方文档里就用这个名作为标准了。时间一久,大家都遵守这个习惯了。
在标准请求头中是没有这个变量名的。

评论关闭

goaccess 分析日志详解

yum安装的nginx默认日志格式

log_format 日志格式:

 log_format main '$remote_addr - $remote_user [$time_local] "$request" '
 '$status $body_bytes_sent "$http_referer" '
 '"$http_user_agent" "$http_x_forwarded_for"';

server {
 listen 80 default_server;
 listen [::]:80 default_server;
# 启用认证访问
auth_basic "nginx auth";
auth_basic_user_file /etc/nginx/auth;
#/etc/nginx/auth用htpasswd生成,用户名密码都为root

nginx日志格式,为了对比,我把它换行了,去掉了单引号

$remote_addr - # 访问来源ip 后面的"-" 是个普通字符
$remote_user # 用户,只有在启用 auth_basic 认证访问的时候才会出现,否则是一个 "-" 字符
[$time_local] # 日期,时间,时区
"$request" # 请求地址 注意,这里有双引号
$status # 状态码
$body_bytes_sent # 包发送大小
"$http_referer" # 引用页 注意,这里有双引号
"$http_user_agent" # 客户端useragent信息(系统版本、浏览器等) 注意,这里有双引号
"$http_x_forwarded_for" # nginx做反向代理时的客户端地址 注意,这里有双引号

nginx访问日志(已换行)每一行与上面一一对应:

192.168.10.105 -
root
[31/Aug/2017:13:34:45 +0800]
"GET / HTTP/1.1"
304
0
"-"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36"
"-"

特别说一下$http_referer的含义。
一个页面中会引用css、js、图片等资源,去请求这些资源的时候$http_referer字段就是对应的当前页面地址。

比如现在1.html代码如下,引入了一张图片。

<!DOCTYPE HTML>
<html>

<body>

<p>
一幅图像:
<img src="./1.jpg" width="128" height="128" />
</p>

</body>
</html>

nginx的访问日志如下:

# 下面这条日志,$http_referer 没有内容,默认用了 "-" 代替
192.168.10.105 - root [31/Aug/2017:14:09:23 +0800] "GET /1.html HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36" "-"
# 下面这条日志 $http_referer 为 http://192.168.10.26/1.html,说明1.jpg是这个页面中引用的图片资源
192.168.10.105 - root [31/Aug/2017:14:09:23 +0800] "GET /1.jpg HTTP/1.1" 304 0 "http://192.168.10.26/1.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36" "-"

nginx中支持的日志格式可以从 http://www.rootop.org/pages/3070.html 这里查看。

明白了上面日志格式含义,再去看goaccess的日志格式
可以从 https://goaccess.io/man#custom-log 查看 “CUSTOM LOG/DATE FORMAT” 部分对于日志格式的定义

第一个知识点:goaccess 默认以空格做为分隔符

定义一个对应nginx日志格式的goaccess配置文件

time-format %H:%M:%S
date-format %d/%b/%Y
log-format %h %^ %^ [%d:%t %^] "%r" %s %b "%R" "%u" %^

time-format(时间格式)和date-format(日期格式)可以理解为定义了两个变量,并以此为变量名。后面是变量值。
可以在shell下执行 echo $(date +%d/%b/%Y:%H:%M:%S) ,输出的时间格式和日志里的时间格式是一样的(除了没有时区)


log-format 开始定义日志格式,%d和%t其实就是去调用变量,d代表调用日期变量,t代表调用时间变量

第二个知识点:不要忘记日志格式中的双引号,及其他字符,如[],否则会解析失败。

goaccess 格式参数解释:

%x 与时间格式和日期格式变量匹配的日期和时间字段。当使用时间戳而不是日期和时间在两个单独的变量中时使用这个。
%t time字段匹配时间格式变量。
%d date字段匹配日期格式变量。
%v 根据规范名称设置(服务器块或虚拟主机)的服务器名称。
%e 这是通过HTTP身份验证请求文档的用户ID。
%h host(客户端IP地址,IPv4或IPv6)
%r 客户端的请求行。这需要围绕请求的特定分隔符(单引号,双引号等)可解析。否则,请使用特殊格式说明符(如%m,%U,%q和%H)的组合来解析各个字段。
 注意:使用%r获取完整请求或%m,%U,%q和%H以形成请求,两者不要同时使用。
%m 请求方式。
%U 请求的URL路径。
 注意:如果查询字符串在%U中,则不需要使用%q。但是,如果URL路径不包含任何查询字符串,则可以使用%q,并将查询字符串追加到请求中。
%q 查询字符串。
%H 请求协议。
%s 服务器发送回客户端的状态码。
%b 返回给客户端的对象的大小。
%R “Referer”HTTP请求标头。
%u 用户代理HTTP请求标头。
%D 服务请求所用的时间,以微秒为单位。
%T 以毫秒级分辨率提供服务所需的时间(秒)。
%L 服务请求所需的时间,以毫秒为单位,为十进制数。
%^ 忽略此字段。
%~ 向前移动日志字符串,直到找到一个非空格(!isspace)字符。
~h X-Forwarded-For(XFF)字段中的主机(客户端IP地址,IPv4或IPv6)。

现在生成html。
[root@localhost ~]# goaccess -f /var/log/nginx/access.log -a -d -p b -o ./1.html
-p是指定配置文件,文件名叫b
-o是输出到文件,支持html json csv
更多参数可以去看 goaccess -h 帮助

关于goaccess实时显示:
在生成html时候加一个参数

--real-time-html

这时goaccess会在当前窗口一直运行,可以加–daemonize参数放到后台。
其次,它开启一个端口 7890 用于浏览器和goaccess的WebSocket连接获取最新数据。

评论关闭