nginx map指令用于根据现有的变量的值来赋值新变量。如根据
x-forwarded-for
和
remote_addr
的值,创建新变量
clientip
。
用法解析
Syntax: map string $variable { … }
Default: —
Context: http
-
string
可以是一个或多个变量组成的字符串 -
$variable
是新变量名 -
{...}
中的内容为
source
与
resulting
的映射-
source
可以为字符串或正则(
~
区分大小写,
~*
不区分大小写) -
resulting
赋予
$variable
新变量的值
-
-
{...}
中可以使用以下关键字,当
source
与关键字重复时需要使用
\
转义-
default value
设置默认值 -
hostnames
指明
source
的值是主机名,主机名可包含前缀或后缀 -
include file
包含变量文件 -
volatile
指明变量不可缓存
-
实例演示
获取client真实ip
nginx http区块配置如下:
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"';
在客户端访问两次:
# curl -H 'x-forwarded-for:10.0.1.60' 10.2.1.105/f5.html
# curl 10.2.1.105/f5.html
访问日志:
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区块配置如下:
map $http_user_agent $ifmobile {
~*iphone 1;
~*ipod 1;
~*android 1;
default 0;
}
nginx server区块配置如下:
if (-f $request_filename/index.html) {
rewrite ^(.*) $1/index.html;
}
if ( $ifmobile = 1 ) {
rewrite (.*) /m$1;
}
注:这里
rewrite
语句干扰了
index
语句的执行,在访问目录时,无法正确定位到目录下的index.html,故加了
$request_filename/index.html
的判断。
在客户端访问:
# 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区块使用以下配置来实现:
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区块配置如下:
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"';
其中以下两行
*.example.com 1;
example.com 1;
可以简写为
.example.com 1;
前缀可以这样简写,后缀就不行了。
在nginx server区块添加以下配置,就可以防盗链了:
location ~* \.(jpg|jpeg|png|gif|ico)$ {
if ($ifmyreferer = 0) {
return 404;
}
}
客户端请求:
# 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
。