You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker环境下Nginx反向代理无法获取客户端真实IP的解决方法

问题根因

拿到Docker内网网段IP由两个问题共同导致:

  • Docker默认bridge网络会对入站流量做SNAT源地址转换,所有外部请求经docker网桥转发到容器时,源IP会被替换为docker网桥网关地址(即你看到的127.20.0.1这类内网地址),容器内Nginx默认读取的$remote_addr就是该网关地址,而非客户端真实IP。
  • Nginx配置存在规则遗漏:正则location优先级高于普通前缀匹配,location ~ ^/api/(system|admin|global)/、location /app_两个块都没有配置X-Forwarded-For、X-Real-IP相关的透传规则,匹配这两个路径的请求不会携带真实IP头到后端。
实现方案

分两步配置,先调整Docker网络模式让容器能识别真实源IP,再补全Nginx透传规则即可。

第一步:调整Docker网络配置

方案A:Linux环境用host网络模式(最简便,开发/生产环境通用)

修改docker-compose.yml中proxy-service的配置,替换原有端口映射规则,直接共享宿主机网络栈:

services:
  proxy-service:
    container_name: budi-nginx-dev
    restart: on-failure
    image: nginx:latest
    network_mode: "host" # 新增配置,直接使用宿主机网络
    volumes:
      - ./.generated-nginx.dev.conf:/etc/nginx/nginx.conf
    # 删除原有ports配置,host模式下容器直接监听宿主机端口,不需要额外映射
    depends_on:
      - minio-service
      - couchdb-service
    extra_hosts:
      - "host.docker.internal:host-gateway"

配置完成后Nginx配置里的listen 10000会直接绑定到宿主机10000端口,不经过docker网桥转发,$remote_addr会直接读取到真实客户端IP。

注意:host网络模式仅在Linux宿主机上可正常获取真实客户端IP,Windows/macOS下的Docker Desktop基于虚拟机架构,host网络无法直接拿到宿主机外部的客户端IP,这类环境建议切换WSL2后端部署,或直接使用Linux宿主机运行服务。

方案B:保留bridge网络,关闭Docker用户态代理

如果不想使用host网络,可修改Docker配置关闭SNAT转换:

  1. 编辑Docker daemon配置文件(默认路径/etc/docker/daemon.json),添加配置:
{
  "userland-proxy": false
}
  1. 执行命令重启Docker服务生效:systemctl restart docker
  2. 保留原有docker-compose的ports映射配置即可,此时入站流量不会经过用户态代理做源地址替换,容器内可直接拿到真实源IP。

第二步:补全Nginx透传规则

不要在单个location块零散配置代理头,把通用透传规则移到http块全局生效,避免遗漏匹配路径,核心配置修改如下:

http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;

  # 全局统一配置代理透传头,所有location块自动继承
  proxy_set_header Host $http_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;

  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
  
  map $http_upgrade $connection_upgrade {
    default     "upgrade";
  }

  server { 
    listen       10000 default_server;
    server_name  _;
    client_max_body_size 1000m;
    ignore_invalid_headers off;
    proxy_buffering off;

    location ~ ^/api/(system|admin|global)/ {
      # 无需重复配置透传头,自动继承全局规则
      proxy_pass      http://host.docker.internal:4002;
    }

    location /api/ {
      proxy_read_timeout 120s;
      proxy_connect_timeout 120s;
      proxy_send_timeout 120s; 

      proxy_http_version  1.1;
      proxy_set_header    Connection          $connection_upgrade;
      proxy_set_header    Upgrade             $http_upgrade;
      # 无需重复配置X-Forwarded-*头,全局已统一配置
      
      proxy_pass      http://host.docker.internal:4001;
    }

    location /app_ {
      # 自动继承全局透传规则
      proxy_pass      http://host.docker.internal:4001;
    }

    location / {
      # 自动继承全局透传规则
      proxy_connect_timeout 300;
      proxy_http_version 1.1;
      proxy_set_header Connection "";
      chunked_transfer_encoding off;

      proxy_pass      http://minio-service:9000;
    }

    client_header_timeout 60;
    client_body_timeout   60;
    keepalive_timeout     60;
    gzip                  off;
    gzip_comp_level       4;
  }
}

后端读取说明

如果Nginx和后端服务(4001/4002端口)之间没有其他代理层,直接读取X-Real-IP请求头即可拿到真实客户端IP;如果存在多层代理,从X-Forwarded-For头的逗号分隔IP列表中取第一个非内网网段的IP即可。

内容的提问来源于stack exchange,提问作者L.Min

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 20:27:21