Next.js API获取客户端IP失败,返回服务器IP问题排查与解决
问题
我正在使用Next.js应用,尝试获取客户端用户的IP地址和地理位置,但得到的却是部署所用Vercel服务器的IP地址。我尝试使用middleware实现:
import { NextFetchEvent, NextRequest, NextResponse } from 'next/server'; export async function middleware(request: NextRequest, _next: NextFetchEvent) { const { nextUrl: url, geo } = request const country = geo?.country // Clone the request headers and set a new header `x-hello-from-middleware1` const requestHeaders = new Headers(request.headers) requestHeaders.set('country', country||'EG') // You can also set request headers in NextResponse.rewrite const response = NextResponse.next({ request: { // New request headers headers: requestHeaders, }, }) return response }
随后在api目录中从请求头获取country:
export default async (req:MyRequest, res:NextApiResponse) => { if (req.method === 'GET') { const country= req.headers.country; } }
但获取到的是服务器所在国家!我还尝试了以下方法:
export default async (req:MyRequest, res:NextApiResponse) => { if (req.method === 'GET') { const forwarded = req.headers['x-forwarded-for']; const ip = typeof forwarded === 'string' ? forwarded.split(/, /)[0] : req.socket.remoteAddress; } }
结果依旧,获取到的是托管服务器的IP。请问这是什么原因?该如何解决?
原因分析
- Vercel边缘网络转发机制:客户端请求会先经过Vercel边缘节点,再转发到应用服务器,直接读取
req.socket.remoteAddress只能拿到边缘节点IP,而非真实客户端IP。 - Middleware运行环境限制:
request.geo仅在边缘运行时(Edge Runtime) 能正确获取客户端地理位置,默认Node.js运行时会返回服务器所在位置。 - 请求头读取错误:Vercel转发请求时用的是专属的
x-vercel-forwarded-for头,而非标准x-forwarded-for,直接读取后者会拿到错误IP。
解决方法
方法1:让Middleware运行在边缘运行时(推荐)
给Middleware添加运行时配置,确保它在Vercel边缘节点执行,这样request.geo就能正确获取客户端地理位置:
import { NextFetchEvent, NextRequest, NextResponse } from 'next/server'; // 指定边缘运行时 export const config = { runtime: 'edge', }; export async function middleware(request: NextRequest, _next: NextFetchEvent) { const { geo } = request const country = geo?.country || 'EG' const requestHeaders = new Headers(request.headers) requestHeaders.set('x-client-country', country) return NextResponse.next({ request: { headers: requestHeaders, }, }) }
API路由中读取自定义头:
export default async (req:MyRequest, res:NextApiResponse) => { if (req.method === 'GET') { const country = req.headers['x-client-country']; res.status(200).json({ country }); } }
方法2:在API路由中读取Vercel专属请求头
Vercel会自动添加专属请求头,直接读取即可获取客户端信息:
x-vercel-forwarded-for:真实客户端IP(多代理场景下第一个值即为客户端IP)x-vercel-ip-country:客户端所在国家代码
修改API路由代码:
export default async (req:MyRequest, res:NextApiResponse) => { if (req.method === 'GET') { // 获取客户端IP const forwarded = req.headers['x-vercel-forwarded-for']; const clientIp = typeof forwarded === 'string' ? forwarded.split(',')[0].trim() : null; // 获取客户端国家 const clientCountry = req.headers['x-vercel-ip-country'] || '未知'; res.status(200).json({ ip: clientIp, country: clientCountry }); } }
方法3:客户端直接获取地理位置(无需服务端验证场景)
如果不需要服务端处理,可直接在浏览器通过原生API获取:
// 客户端组件/页面中使用 const getClientLocation = async () => { return new Promise((resolve, reject) => { if (!navigator.geolocation) { reject('浏览器不支持地理位置API'); return; } navigator.geolocation.getCurrentPosition( (position) => { resolve({ latitude: position.coords.latitude, longitude: position.coords.longitude, }); }, (error) => { reject(error.message); } ); }); }; // 使用示例 getClientLocation().then(location => { console.log('客户端地理位置:', location); }).catch(err => { console.error('获取地理位置失败:', err); });
内容的提问来源于stack exchange,提问作者Amr Eraky
相关产品推荐
相关产品推荐

