怎样给Nginx设置访问IP白名单_nginx 白名单

[复制链接]
发表于 2026-10-4 17:40:00 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

给内网系统、后台管理页、或者某个监测接口做访问控制时,「只允许几个固定 IP 访问」是最直接的需求。Nginx 原生就能做:用 allow/deny 做基础黑白名单,用 geo 模块把客户端 IP 映射成一个变量,再配合 if 做条件判断与跳转,复杂一点还能用 set_by_lua 接入动态名单。本文给出一份可直接抄的完整配置,并说明每段的作用与常见误用。

一、需求与整体思路

目标:主站点正常对所有客户端开放,但若干内部路径(例如管理后台、健康检查专属页、某个需要二次限制的 API)只允许白名单 IP 访问;不在名单里的请求,返回提示页或一个跳转,而不是粗暴 403。

实现分三层:

  • geo 模块:把 $remote_addr 映射成一个自定义变量(如 $ip_whitelist),便于后续复用;
  • allow / deny:在 location 或 server 段做最基础的放行与拒绝;
  • if 判断 + 跳转:命中非白名单时 rewrite 到一个提示页(如 /warn_navigate_page),给出明确反馈。

二、http 块里定义 geo 变量

geo 只能写在 http 上下文里,不能写在 server 或 location 里。写一段最基础的名单:

geo $remote_addr $ip_whitelist {
    default 0;                       # 默认不在白名单
    10.0.0.0/8   1;                  # 内网整段放行
    192.168.1.0/24 1;
    203.0.113.0/24 1;                # 某台办公出口
}

几个要点:

  • default 0 一定要写,不写的话未匹配的地址变量为空,条件判断容易走错分支;
  • geo 支持 CIDR 网段写法,不需要给每个 IP 单独写一行;
  • geo 里也可以放 include,把 IP 段抽到独立文件(见后文 include ip.conf 的用法),便于热更新而不必 reload 整份主配置。

三、用 allow / deny 做兜底

最简版本,写在 server 或 location 里:

location /admin/ {
    allow 10.0.0.0/8;
    allow 203.0.113.0/24;
    deny  all;
    proxy_pass http://127.0.0.1:8080;
}

匹配规则是有序的、遇到第一个命中就停止:deny all 放在 allow 前面就会把所有请求拒掉,这是最常见的配置事故。另外 allow/deny 在 geo(读变量)与方向(写规则)上的语义不同,别混用。

四、把名单抽成独立文件

当白名单很长时,单独维护一个 ip.conf 放在 conf 目录,在 http 块里 include:

http {
    include       mime.types;
    default_type  application/octet-stream;
    include       ip.conf;            # 里面全是 geo 的网段规则
}

ip.conf 内容(注意每一行是「网段 + 值」):

default 0;
10.0.0.0/8       1;
172.16.10.0/24   1;
203.0.113.0/24   1;

这样单独改一个文件就能调整名单,配合 nginx -s reload 生效,主配置不用动,也方便纳管到配置中心。

五、用 if 做条件跳转

geo 变量有了,location 里就能判断。写法是取 $ip_whitelist 的值,命中非白名单(值不等于 1)时重定向到提示页:

server {
    listen 80;
    server_name example.com;

    location = /warn_navigate_page {
        default_type text/html;
        return 200 '<h1>当前网络环境不允许访问</h1>';
    }

    location / {
        if ($ip_whitelist <> "1") {
            rewrite ^ /warn_navigate_page redirect;
        }
        proxy_pass http://backend_upstream;
    }
}

这里有两个容易踩的点:

  • if 在 location 里被戏称为「Nginx 的魔法 if」,能干的事有限,只在 return / rewrite 这类简单动作里用;要写复杂逻辑请交给 real_ip/map/lua;
  • 比较字符串要用 = 或 !=,写成 if ($ip_whitelist = 1) 表示在白名单放行,否则走兜底;
  • rewrite ... redirect 会返回 302,客户端会再请求一次;若不想多一次往返,可改用 return 403 或 return 302 /warn_navigate_page。

六、进阶:用 set_by_lua 接动态名单

当白名单存在数据库、Redis 或者需要按时间段变化时,纯配置文件就力不从心了,可以引入 lua-nginx-module 的 set_by_lua:

http {
    lua_package_path "/usr/local/lualib/?.lua;;";

    server {
        location / {
            set_by_lua $whitelist_flag '
                local ip = ngx.var.remote_addr
                -- 从共享字典或 Redis 里读白名单,这里示例读计数器
                local dict = ngx.shared.whitelist_dict
                return dict and dict:get(ip) or "0"
            ';
            if ($whitelist_flag = "1") {
                proxy_pass http://backend_upstream;
            }
            rewrite ^ /warn_navigate_page redirect;
        }
    }
}

生产使用要注意:lua 代码里不要做阻塞式 I/O(直接连 Redis 如果不用 cosocket 会阻塞 worker),通常配合 lua_shared_dict 缓存名单;另外要限制 lua_code_cache on 的边界,避免热更新后旧代码长期驻留。

七、验证与生效

改完先自检语法,再 reload:

nginx -t
nginx -s reload

从本机、白名单 IP、非白名单 IP 三个来源各 curl 一次:

curl -I -H "X-Forwarded-For: 203.0.113.5" http://127.0.0.1/
curl -I -H "X-Forwarded-For: 198.51.100.9" http://127.0.0.1/

另外提醒一句:如果前面还有 CDN 或七层代理,$remote_addr 拿到的会是代理 IP,此时要先用 set_real_ip_from + real_ip_header X-Forwarded-For 把真实客户端 IP 取出来,再交给 geo 判断,否则白名单永远不命中。配完记得 nginx -t 通过再 reload,reload 失败不会中断线上请求,但本次修改未生效。

八、小结

整件事的配置骨架就三行:http 块用 geo 把 $remote_addr 映射成 $ip_whitelist(记得写 default) → allow/deny 做基础兜底(顺序敏感,deny all 放最后) → location 里 if 判断后 rewrite 到提示页。名单长了抽 include ip.conf,需要动态就上 set_by_lua。最容易被忽略的两点是「deny all 的位置」和「有反向代理时真实 IP 没还原」,这两处错了,表现为「白名单全失效」或「白名单全命中」,非常好认。

qidao123.com企服评测-应用市场 编辑提示

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表