Skip to content

让我复盘一下这个 bug 的修复过程:

1. 问题描述:

后端 NestJS 服务无法获取到真实的客户端 IP 所有请求的 IP 都被记录为 "::1"(IPv6 的本地回环地址)

2. 缺失的关键信息:

最初没有看到完整的系统架构(Nginx -> Next.js -> NestJS) 没有看到 Next.js 的中间件代码 没有看到 NestJS 的 IP 获取中间件的具体实现 没有看到各层的日志信息 这些信息的缺失导致我进行了一些无效的修复尝试: 试图在 Next.js rewrites 配置中添加 headers(语法上就是错误的) 过度复杂化 Next.js 中间件的实现 尝试修改环境变量而不是解决根本问题

3. 转折点 - 关键信息的获取:

日志信息的展示:

Next.js 中间件的请求头日志 NestJS 的 IP 获取中间件日志 这让我们看到了 IP 信息在传递过程中的变化

完整的代码上下文:

ip-limit.middleware.ts 的完整实现 Next.js 的 middleware.ts 文件 Nginx 的配置文件

系统架构的理解:

理解了请求经过的完整链路:客户端 -> Nginx -> Next.js -> NestJS 明确了每一层在 IP 传递中的作用

这些信息帮助我们:

确认了 IP 信息确实被 Nginx 正确捕获 发现了 IP 信息在 Next.js 中间件中的处理方式 看到了 NestJS 最终如何处理这些 IP 头部 最终,通过分析日志和完整的代码上下文,我们确认问题不是出在配置上,而是出在请求链路的完整性上。这让我们能够提供一个准确的解决方案,而不是继续进行无效的配置修改

velseine的总结: 先要理解系统架构, 知道实现这个功能时数据处理的流向, 在此基础上才能知道ai需要哪些信息来编写正确的代码, 猜测ai缺失的信息, ai 并不是完全知道项目中的所有代码细节, 而是会用程序员对 ai 提问时的关键字进行查找对应文件进行读取, 这种方式可能有遗漏, 程序员需要手动提示 ai 关键部分所在的位置. 要求 ai 在关键部分添加调试信息

具体的错误

  • 第一个错误:在 Next.js 配置中添加 headers
js
    // ❌ 错误的配置
async rewrites() {
  return [
    {
      source: '/api/:path*',
      destination: '...',
      headers: [  // 错误:rewrites 不支持设置 headers
        {
          key: 'X-Real-IP',
          value: '{client-ip}'
        }
      ]
    }
  ]
}
原因:rewrites 配置不支持直接设置 headers,这是配置语法错误。
  • 第二个错误:环境变量配置
init
# ❌ 错误的配置


<NolebasePageProperties />




NEXT_PUBLIC_API_BASE_URL=http://localhost:3005/api
原因:直接指向后端地址会绕过 Next.js 的代理,导致无法在中间件中处理 IP。
  • 第三个错误:过度复杂的中间件配置
// ❌ 过度复杂的配置
if (forwardedFor) {
  requestHeaders.set('X-Forwarded-For', forwardedFor)
  requestHeaders.set('X-Real-IP', forwardedFor.split(',')[0].trim())
} else if (realIp) {
  requestHeaders.set('X-Real-IP', realIp)
  // ...更多复杂逻辑
}
原因:增加了不必要的复杂性,可能导致 IP 信息在传递过程中丢失。
  • 正确的方案:

a. Next.js 配置(next.config.js):

js
// ✅ 正确的配置
module.exports = {
  async rewrites() {
    return {
      beforeFiles: [
        {
          source: '/api/:path*',
          destination: 'http://localhost:3005/api/:path*',
        },
      ],
    }
  },
}

b. 环境变量(.env.production):

init
# ✅ 正确的配置
NEXT_PUBLIC_API_BASE_URL=/api

c. 中间件(middleware.ts):

js
// ✅ 正确的配置
export function middleware(request: NextRequest) {
  if (!request.nextUrl.pathname.startsWith('/api')) {
    return NextResponse.next()
  }

  const requestHeaders = new Headers(request.headers)
  const clientIp = request.ip || 
                  request.headers.get('x-real-ip') || 
                  request.headers.get('x-forwarded-for')?.split(',')[0].trim()

  if (clientIp) {
    requestHeaders.set('X-Real-IP', clientIp)
    requestHeaders.set('X-Forwarded-For', clientIp)
  }

  return NextResponse.next({
    request: { headers: requestHeaders }
  })
}

关键教训:

  • 保持配置简单,避免过度复杂的逻辑

  • 使用正确的配置语法

  • 确保请求经过正确的代理链路

  • 不要试图在 rewrites 中设置 headers

  • 使用相对路径作为 API 基础 URL

这些错误帮助我们理解了:

  • Next.js 的配置限制

  • 正确的请求代理方式

  • IP 信息传递的最佳实践

最终的解决方案更加简洁和可靠,避免了之前尝试中的各种问题。

贡献者

The avatar of contributor named as sunchengzhi sunchengzhi

文件历史

撰写