Nginx 通过 map 指令定义条件变量实现 access_log 的条件写入,如按状态码、URL、IP 或 User-Agent 过滤日志,需配合 if=$variable 使用,且变量必须由 map 定义,不可直接用内置变量。

Nginx 本身不支持直接在 access_log 指令中写复杂的条件逻辑(比如“只记录状态码为 404 的请求”),但可以通过 map 指令 + 变量控制 实现访问日志的条件写入,这是最常用、最稳定的做法。
用 map 定义日志开关变量
在 http 块中定义一个映射变量,根据请求特征(如状态码、URL、User-Agent、IP 等)决定是否记录日志。Nginx 会为每个请求计算该变量值,再在 access_log 中引用它:
- 值为非空字符串(如
"1")→ 写入日志 - 值为空字符串(
"")→ 不写入该条日志(Nginx 自动跳过)
例如,只记录返回 404 和 500 的请求:
http {
map $status $loggable {
~^(404|500)$ "1";
default "";
}
<pre class="brush:php;toolbar:false;">log_format custom '[$time_local] "$request" $status $body_bytes_sent';
access_log /var/log/nginx/error_only.log custom if=$loggable;}
按 URL 路径或参数过滤
可以结合 $request_uri 或 $args 做匹配。比如忽略所有健康检查接口(/health)和静态资源(.js、.css):
map $request_uri $loggable {
~^/health "";
~\.(js|css|png|jpg|gif)$ "";
default "1";
}
<p>access_log /var/log/nginx/access.log main if=$loggable;</p>
注意:正则匹配是大小写敏感的,需要加 i 标志才忽略大小写,如 ~*\.png$。
按客户端 IP 或 User-Agent 过滤
屏蔽内部监控探针或爬虫的访问日志,减少日志体积:
map $remote_addr $loggable {
192.168.1.100 ""; # 屏蔽某内网监控 IP
10.0.0.0/8 ""; # 屏蔽整个私有网段
default "1";
}
<p>map $http_user_agent $ua_filtered {
~*(curl|wget|monitoring) "";
default "1";
}</p><h1>组合多个条件(需嵌套 map 或用更复杂逻辑)</h1><p>map $ua_filtered $loggable {
"" "";
"1" $loggable_by_ip; # 假设已定义 $loggable_by_ip
}
</p>
实际中建议拆成单维度判断,或用 Lua(配合 ngx_lua 模块)做更灵活组合,但纯原生 Nginx 推荐优先用 map 分层控制。
验证与注意事项
配置完成后必须:
- 运行 nginx -t 检查语法
- 执行 nginx -s reload 重载配置
- 发起测试请求(如故意触发 404),查看日志文件是否按预期写入
关键提醒:
- if= 后的变量必须是 map 定义的,不能直接用 $status 等内置变量(Nginx 限制)
- 日志文件权限要确保 Nginx 工作进程(通常是 www-data 或 nginx 用户)有写入权限
- 条件太复杂时,map 性能影响极小,但频繁正则匹配(尤其在高并发下)建议预编译或简化规则











