Docker环境下Nginx访问日志无法显示真实远程IP的求助
Docker容器中Nginx访问日志显示内部IP的问题
我在Docker容器中部署了Nginx,但访问日志里的远程地址一直是Docker内部网络IP,试了谷歌前两页的方案都没解决。我需要日志显示真实远程IP,这样fail2ban才能正常拦截恶意请求IP。
以下是我的配置文件:
docker-compose.yml
version: "3" services: web: build: context: ./ dockerfile: Dockerfile-test volumes: - ./api:/var/www/html/app/api - ./frontend:/var/www/html/app/frontend ports: - "80:80" networks: - app mysql: image: 'mysql:8.0' volumes: - mysql_conf:/etc/mysql/conf.d - db_data:/var/lib/mysql - mysql_logs:/var/log/mysql ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: '${MYSQL_ROOT_PASSWORD}' MYSQL_USER: '${DB_USERNAME}' MYSQL_DATABASE: '${DB_DATABASE}' MYSQL_PASSWORD: '${DB_PASSWORD}' networks: - app mailhog: image: 'mailhog/mailhog:latest' ports: - "1025:1025" - "8025:8025" networks: - app volumes: mysql_conf: mysql_logs: db_data: driver: local networks: app: driver: bridge
"web"服务包含Nginx、Laravel应用和前端应用,基于php:8.0-fpm构建镜像
Nginx配置(conf.d文件夹内)
upstream frontend { server localhost:3000; } upstream api { server localhost:9000; } server { listen 80; server_name localhost; error_log stderr; error_log /var/log/nginx/error.log; access_log stdout; access_log /var/log/nginx/access.log; # don't send the nginx version number in error pages and Server header server_tokens off; # config to don't allow the browser to render the page inside an frame or iframe # and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking # if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri # https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options # when serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header, # to disable content-type sniffing on some browsers. # https://www.owasp.org/index.php/List_of_useful_HTTP_headers # currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx # http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx # 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020 add_header X-Content-Type-Options nosniff; add_header X-Frame-Options SAMEORIGIN; location / { proxy_pass http://frontend; 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; proxy_read_timeout 1m; proxy_connect_timeout 1m; proxy_redirect off; } location /api/v1 { root /var/www/html/app/api/public; include fastcgi_params; 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; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; fastcgi_intercept_errors off; fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME $document_root/index.php; fastcgi_param DOCUMENT_ROOT $realpath_root; } # Deny .ht* access location ~ /\.ht { deny all; } }
我还尝试过给web服务添加以下host模式配置,但也没有效果:
ports: - mode: host protocol: tcp published: 80 target: 80 - mode: host protocol: tcp published: 443 target: 80
解决方案
核心问题梳理
Docker默认桥接网络会做端口转发,导致Nginx拿到的$remote_addr是Docker网关IP而非真实客户端IP;同时你的Nginx配置存在冗余参数,且未配置信任代理来源,无法正确解析真实IP头。
具体修复步骤
1. 调整Docker配置,确保IP传递
在docker-compose.yml的web服务中添加信任网段环境变量(或直接改用host模式):
services: web: # 其他配置不变 environment: - TRUSTED_PROXIES=172.17.0.0/16 # 替换为你的Docker桥接实际网段
若改用host模式,删除networks: - app和ports配置,添加network_mode: host即可
2. 修改Nginx全局配置(nginx.conf的http块中)
添加真实IP解析规则,信任Docker内部网段:
# 信任Docker网关网段,从X-Forwarded-For头提取真实IP set_real_ip_from 172.17.0.0/16; real_ip_header X-Forwarded-For; real_ip_recursive on;
3. 优化server块配置
- 前端反向代理(location /):保留现有proxy_set_header配置即可
- API的fastcgi配置(location /api/v1):删除冗余的proxy_set_header,改用fastcgi参数传递真实IP:
location /api/v1 { root /var/www/html/app/api/public; include fastcgi_params; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; fastcgi_intercept_errors off; fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME $document_root/index.php; fastcgi_param DOCUMENT_ROOT $realpath_root; # 传递真实IP给PHP fastcgi_param REMOTE_ADDR $realip_remote_addr; fastcgi_param HTTP_X_FORWARDED_FOR $realip_remote_addr; }
4. 修改访问日志格式,记录真实IP
调整access_log配置,用$realip_remote_addr替代默认的$remote_addr:
access_log /var/log/nginx/access.log '$realip_remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
5. 验证生效
重启web容器:docker-compose restart web,访问服务后查看access.log,确认显示真实客户端IP,再测试fail2ban拦截功能。
额外注意事项
- 若使用CDN或外部反向代理,需将对应IP段加入
set_real_ip_from - 可关闭Docker的userland-proxy避免干扰:在
/etc/docker/daemon.json中添加{"userland-proxy": false},然后重启Docker服务
内容的提问来源于stack exchange,提问作者DGregory
相关产品推荐
相关产品推荐

