切换主题
让我复盘一下这个 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 信息传递的最佳实践
最终的解决方案更加简洁和可靠,避免了之前尝试中的各种问题。
贡献者
sunchengzhi