0%

nginx_map指令

nginx map指令用于根据现有的变量的值来赋值新变量。如根据x-forwarded-forremote_addr的值,创建新变量clientip

用法解析

Syntax: map string $variable { … }
Default: —
Context: http

  • string可以是一个或多个变量组成的字符串
  • $variable是新变量名
  • {...}中的内容为sourceresulting的映射
    • source可以为字符串或正则(~区分大小写,~*不区分大小写)
    • resulting赋予$variable新变量的值
  • {...}中可以使用以下关键字,当source与关键字重复时需要使用\转义
    • default value 设置默认值
    • hostnames 指明source的值是主机名,主机名可包含前缀或后缀
    • include file 包含变量文件
    • volatile 指明变量不可缓存

实例演示

获取client真实ip

nginx http区块配置如下:

1
2
3
4
5
6
7
map $http_x_forwarded_for $clientip {
"" $remote_addr;
default $http_x_forwarded_for;
}
log_format main '$clientip - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$request_time"';

在客户端访问两次:

1
2
# curl -H 'x-forwarded-for:10.0.1.60' 10.2.1.105/f5.html
# curl 10.2.1.105/f5.html

访问日志:

1
2
10.0.1.60 - - [14/Aug/2019:18:37:44 +0800] "GET /f5.html HTTP/1.1" 200 2 "-" "curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5" "0.000"
10.0.1.60 - - [14/Aug/2019:18:37:58 +0800] "GET /f5.html HTTP/1.1" 200 2 "-" "curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5" "0.000"

这种办法有一个bug,如果客户端伪造了x-forwarded-for头部,后端主机的日志就没有参考价值了。

推荐在前端代理服务器上将x-forwarded-for重置为空之后再赋值。

转发到手机端页面

nginx http区块配置如下:

1
2
3
4
5
6
map $http_user_agent $ifmobile {
~*iphone 1;
~*ipod 1;
~*android 1;
default 0;
}

nginx server区块配置如下:

1
2
3
4
5
6
7
if (-f $request_filename/index.html) {
rewrite ^(.*) $1/index.html;
}

if ( $ifmobile = 1 ) {
rewrite (.*) /m$1;
}

注:这里rewrite语句干扰了index语句的执行,在访问目录时,无法正确定位到目录下的index.html,故加了$request_filename/index.html的判断。

在客户端访问:

1
2
3
4
5
6
# curl -H 'user-agent:huaweimate20 android 9.0' 10.2.1.105/index.html
m/index.html
# curl -H 'user-agent:no' 10.2.1.105/index.html
index.html
# curl 10.2.1.105
index.html

上述需求也可以在nginx server区块使用以下配置来实现:

1
2
3
4
5
6
7
8
9
10
11
if ( $http_user_agent ~* (iphone|ipod|android) ) {
set $ifmobile 1;
}

if (-f $request_filename/index.html) {
rewrite ^(.*) $1/index.html;
}

if ( $ifmobile = 1 ) {
rewrite ^/(.*) /m/$1;
}

匹配hostname

nginx http区块配置如下:

1
2
3
4
5
6
7
8
9
10
11
map $http_referer $ifmyreferer {
hostnames;
*.example.com 1;
example.com 1;
.example.cn 1;
default 0;
}

log_format main '$clientip - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$request_time" "$ifmyreferer"';

其中以下两行

1
2
*.example.com 1;
example.com 1;

可以简写为

1
.example.com 1;

前缀可以这样简写,后缀就不行了。

在nginx server区块添加以下配置,就可以防盗链了:

1
2
3
4
5
location ~* \.(jpg|jpeg|png|gif|ico)$ {
if ($ifmyreferer = 0) {
return 404;
}
}

客户端请求:

1
2
3
4
5
6
7
8
9
10
# curl -H 'referer:www.example.com' 10.2.1.105/luozhao.jpg
GIF89aÿÿÿ!?L;
# curl -H 'referer:www.baidu.com' 10.2.1.105/luozhao.jpg
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

注:防盗链有专用指令valid_referers