Docker化Next.js+NestJS应用获取真实客户端IP(替代Nginx容器IP)
解决Docker化Next.js 13 + NestJS的真实客户端IP传递问题
环境与问题概述
我已将Next.js 13(前端)和NestJS(API)Docker化,使用nginx-proxy做反向代理,对应的docker-compose.yml配置如下:
api: image: gidgud/my:api container_name: api ports: - '6900:6999' networks: - mynetwork environment: - VIRTUAL_HOST=api.mywebsite.com - VIRTUAL_PORT=6999 web: image: gidgud/my:web container_name: web expose: - '3000' networks: - mynetwork depends_on: - api environment: - VIRTUAL_HOST=www.mywebsite.com,mywebsite.com - VIRTUAL_PORT=3000 nginx-proxy: image: 'nginxproxy/nginx-proxy:latest' container_name: 'nginx-proxy' volumes: - 'html:/usr/share/nginx/html' - 'dhparam:/etc/nginx/dhparam' - 'vhost:/etc/nginx/vhost.d' - 'certs:/etc/nginx/certs' - 'conf:/etc/nginx/conf.d' - '/var/run/docker.sock:/tmp/docker.sock:ro' - './custom_proxy.conf:/etc/nginx/conf.d/custom_proxy.conf' depends_on: - web - api networks: - mynetwork ports: - '80:80' - '443:443' networks: mynetwork: name: my_network external: true
当前前端通过VM IP+API暴露端口请求接口(尝试api.mywebsite.com时Next.js服务器超时,暂用此方式),请求先经Next.js rewrites转发。核心问题:
- API仅能收到
nginx-proxy容器IP,无法获取真实客户端IP; - Next.js服务器组件请求API时,API收到的是Docker网络默认网关IP;
- Vercel部署的非Docker站点请求API时可正常获取真实IP,问题出在Docker/Next.js配置。
解决方案
1. 确保nginx-proxy正确传递客户端IP头
检查挂载的custom_proxy.conf,添加以下配置,确保转发请求时携带真实客户端IP相关头信息:
# custom_proxy.conf proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Real-IP $remote_addr;
nginx-proxy默认会自动处理VIRTUAL_HOST的反向代理配置,但自定义配置需确保不覆盖这些头信息,否则会丢失客户端IP。
2. Next.js配置:传递客户端IP到API
客户端请求(经rewrites转发)
在next.config.js中配置rewrites时,改用Docker内部容器名访问API(同网络下直接用api:6999,解决之前的超时问题),同时保留客户端请求头:
// next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { async rewrites() { return [ { source: '/api/:path*', destination: 'http://api:6999/:path*', headers: { 'X-Forwarded-For': '$request.headers.x-forwarded-for', 'X-Real-IP': '$request.headers.x-real-ip', }, }, ]; }, }; module.exports = nextConfig;
服务器组件请求(Server Components)
在服务器组件中调用API时,手动从Next.js请求对象中提取客户端IP相关头,并传递给API请求:
// app/api/[...path]/route.ts import { NextRequest } from 'next/server'; export async function GET(request: NextRequest) { // 获取客户端IP相关头 const forwardedFor = request.headers.get('x-forwarded-for'); const realIp = request.headers.get('x-real-ip'); // 调用NestJS API时传递头信息 const apiResponse = await fetch('http://api:6999' + request.nextUrl.pathname, { headers: { ...(forwardedFor && { 'X-Forwarded-For': forwardedFor }), ...(realIp && { 'X-Real-IP': realIp }), }, }); return new Response(await apiResponse.text(), { status: apiResponse.status, headers: apiResponse.headers, }); }
3. NestJS配置:信任反向代理并提取真实IP
在NestJS的main.ts中配置信任反向代理来源,让框架从X-Forwarded-For头中提取真实IP:
// main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ExpressAdapter } from '@nestjs/platform-express'; import * as express from 'express'; async function bootstrap() { const app = await NestFactory.create(AppModule, new ExpressAdapter()); // 信任Docker默认网络网段(根据实际mynetwork网段调整) app.set('trust proxy', '172.16.0.0/12'); // 配置CORS,允许前端域名并放行IP相关头 app.enableCors({ origin: ['https://www.mywebsite.com', 'https://mywebsite.com'], credentials: true, allowedHeaders: ['X-Forwarded-For', 'X-Real-IP', 'Content-Type'], }); await app.listen(6999); } bootstrap();
之后在NestJS控制器中,即可通过request.ip获取真实客户端IP:
// app.controller.ts import { Controller, Get, Req } from '@nestjs/common'; import { Request } from 'express'; @Controller() export class AppController { @Get() getHello(@Req() req: Request): string { console.log('真实客户端IP:', req.ip); return 'Hello World!'; } }
4. 优化Docker配置(可选)
改用Docker内部网络访问API后,可移除api服务的宿主机端口映射,避免不必要的外部暴露:
api: image: gidgud/my:api container_name: api # 移除ports映射,仅内部网络访问 networks: - mynetwork environment: - VIRTUAL_HOST=api.mywebsite.com - VIRTUAL_PORT=6999
内容的提问来源于stack exchange,提问作者gidgud
相关产品推荐
相关产品推荐

