You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 10:52:40