👋 欢迎来到吴老二技术博客,专注运维 / 数据库 / 云原生
首页/运维服务/正文

Nginx 根据客户端 IP 转发到不同 Web 服务器

吴
吴老二
2026-10-14运维服务7 次浏览0 条评论

最近在整理 Nginx 的配置时,遇到一种比较常见的需求:同一个访问入口,根据客户端的 IP 地址,把请求转发到不同的 Web 服务器。

比如,指定的办公网 IP 访问测试环境,其他 IP 访问正式环境。前端只需要访问同一个 Nginx 地址,后端由 Nginx 决定请求交给哪台服务器处理。

这种需求不需要部署多套 Nginx,利用 Nginx 自带的 geo 模块就可以实现。

一、先把环境说清楚

假设现在有三台服务器:

| 服务器 | IP 地址 | 用途 | | ------ | ------------ | ------------ | | Nginx | 192.168.1.10 | 统一访问入口 | | Web01 | 192.168.1.20 | 测试环境 | | Web02 | 192.168.1.30 | 正式环境 |

分流规则很简单:

  • 客户端 IP 是 192.168.1.100,转发到 Web01。
  • 其他 IP 访问,转发到 Web02。

请求经过 Nginx 后,根据客户端 IP 选择后端服务器,客户端不需要知道后端部署在哪台机器上。

二、Nginx 怎么配置

这里直接使用 geo 配合 upstream 实现,不用为每个 IP 单独写一个 location。

先编辑配置文件,例如 /etc/nginx/conf.d/ip-route.conf:

upstream web_test {
    server 192.168.1.20:80;
}

upstream web_prod {
    server 192.168.1.30:80;
}

geo $backend {
    default          web_prod;
    192.168.1.100    web_test;
}

server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://$backend;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

这份配置里有三个关键点。

1. upstream 定义后端服务器

web_test 对应测试环境,web_prod 对应正式环境。后端 IP 或端口发生变化时,修改对应的 upstream 即可。

2. geo 负责匹配客户端 IP

geo $backend {
    default          web_prod;
    192.168.1.100    web_test;
}

这里的 default 表示默认转发到正式环境,只有匹配 192.168.1.100 的请求才会转发到测试环境。

如果需要增加其他 IP,可以继续添加:

geo $backend {
    default          web_prod;
    192.168.1.100    web_test;
    192.168.1.101    web_test;
    192.168.1.102    web_prod;
}

如果是整个网段需要转发到测试环境,也可以写成:

geo $backend {
    default          web_prod;
    192.168.1.0/24   web_test;
}

需要注意,geo 必须放在 http 配置上下文中。如果 conf.d 是由主配置文件的 http 块引入的,上面的写法就可以直接使用。

3. proxy_pass 根据变量转发请求

proxy_pass http://$backend;

$backend 的值由 geo 决定,可能是 web_test,也可能是 web_prod。Nginx 根据这个值选择对应的后端。

这种写法比较适合需要维护多条 IP 规则的情况,不用在每个请求路径里重复判断。

三、配置完成后怎么验证

先检查配置:

nginx -t

确认没有报错后,再重新加载:

nginx -s reload

如果服务由 systemd 管理,也可以执行:

systemctl reload nginx

接下来测试请求是否进入了正确的后端。

最简单的办法是让两台 Web 服务器返回不同的页面内容。例如,Web01 返回 web_test,Web02 返回 web_prod。访问 Nginx 后,就能直接看出请求被转发到了哪里。

这里有一个容易忽略的问题:在自己的电脑上执行 curl,并不能随意指定真实的客户端 IP。

例如:

curl http://192.168.1.10/

这条命令只能从当前机器发起请求。Nginx 实际匹配的是它接收到的客户端源 IP,而不是你希望它匹配的 IP。

如果要验证 192.168.1.100 这条规则,需要从该 IP 对应的客户端发起请求,或者在具备相应网络条件的测试环境中进行验证。

四、配置没问题,为什么还是转发错了?

实际排查时,除了检查规则,还需要确认 Nginx 获取到的客户端 IP 是否正确。

1. Nginx 前面还有一层代理

假设访问链路是:

客户端
   |
   v
负载均衡器
   |
   v
Nginx
   |
   +---- Web01
   |
   +---- Web02

这种情况下,Nginx 接收到的连接可能来自负载均衡器,而不是客户端本身。

如果 $remote_addr 显示的是负载均衡器的 IP,那么 geo 匹配的也是这个地址。客户端明明是 192.168.1.100,规则却没有生效,原因可能就在这里。

如果前面确实有代理,需要根据实际链路配置真实客户端 IP。例如,前置负载均衡器地址为 192.168.1.5,并且它会正确传递 X-Forwarded-For,可以配置:

set_real_ip_from 192.168.1.5;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

配置前需要确认 Nginx 已加载 Real IP 模块,并且只信任可信的代理地址。不能直接信任任意客户端传入的 X-Forwarded-For,否则可能被伪造。

2. 怎么确认请求实际去了哪台服务器

可以给 Nginx 增加一条日志格式:

log_format ip_route
    '$remote_addr [$time_local] "$request" '
    'status=$status upstream=$upstream_addr';

access_log /var/log/nginx/access.log ip_route;

log_format 放在 http 块中,access_log 放在对应的 server 块中。

然后查看日志:

tail -f /var/log/nginx/access.log

重点看两个字段:

  • $remote_addr:Nginx 当前识别到的客户端 IP。
  • $upstream_addr:实际转发到的后端地址。

如果客户端 IP 不对,先排查代理链路;如果客户端 IP 正确,但后端地址不对,再检查 geo 规则和变量配置。

3. 后端服务是否允许直接访问

如果要求所有请求都必须经过 Nginx,建议在后端服务器的防火墙或云安全组中限制访问来源,只允许 Nginx 服务器访问 Web 服务端口。

否则,即使 Nginx 已经实现了 IP 分流,客户端仍可能绕过 Nginx,直接访问后端服务器。

五、最后说两句

通过 geo、upstream 和 proxy_pass,就可以完成基于客户端 IP 的请求分流。IP 规则少的时候配置并不复杂,真正容易出问题的地方,反而是前置代理导致客户端 IP 识别不准确,以及测试时误以为自己已经验证了指定 IP。

如果只是少量固定 IP,直接维护几条规则即可;如果后续 IP 和后端环境越来越多,可以进一步把规则独立管理,减少修改 Nginx 主配置的频率。

配置本身不难,关键是先确认 Nginx 看到的 IP 到底是谁。

评论 0

评论需 登录 后发表(注册并验证邮箱)。
还没有评论,来抢沙发~