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

Next.js 13 app目录服务端组件如何获取客户端IP?

在Next.js 13 App目录服务端组件中获取客户端IP的可行方案

核心实现:通过请求头直接提取

在Next.js 13的App Router服务端组件里,你可以用next/headers提供的headers()函数拿到请求头,从中解析客户端IP。不同部署环境的请求头字段有差异,得做兼容处理:

  • 本地开发或无反向代理的场景:可以尝试从remote-address头获取,但生产环境大多会被反向代理覆盖,所以优先用x-forwarded-for。
  • 有反向代理(比如Vercel、Nginx、Cloudflare)的场景:x-forwarded-for头会包含一串IP,第一个就是真实客户端IP;Cloudflare还会提供cf-connecting-ip头,优先级可以更高。

直接上代码示例:

import { headers } from 'next/headers';

export default async function GamePage() {
  const headersList = headers();
  // 按优先级提取IP
  const ip = headersList.get('cf-connecting-ip') || 
             headersList.get('x-forwarded-for')?.split(',')[0].trim() || 
             headersList.get('remote-address') || 
             'unknown';

  // 查询数据库判断是否在封禁期
  const isBlocked = await checkIfBlocked(ip);

  return (
    <div>
      {isBlocked ? (
        <p>请30分钟后再尝试游戏</p>
      ) : (
        <GameComponent />
      )}
    </div>
  );
}

// 封装数据库查询逻辑
async function checkIfBlocked(ip: string) {
  const result = await db.query('SELECT last_failed_at FROM game_bans WHERE ip = ?', [ip]);
  if (!result[0]) return false;
  
  const now = Date.now();
  const lastFailed = new Date(result[0].last_failed_at).getTime();
  // 计算距离上次失败的分钟数
  const minutesPassed = (now - lastFailed) / (1000 * 60);
  
  return minutesPassed < 30;
}

关键注意点

  1. 部署平台适配:不同平台的请求头不一样,比如Vercel用x-forwarded-for,Cloudflare建议用cf-connecting-ip,你得根据自己的部署环境调整字段顺序。
  2. IP局限性:无注册机制下,用户用代理或VPN的话,你拿到的是代理IP,没法精准识别单个用户,这是这种方案的天然缺陷,得接受。
  3. 服务端组件规则:headers()只能在服务端组件的顶层或者async函数里调用,不能放到客户端交互的回调(比如onClick)里,这点别踩坑。

为什么这个方案比客户端API调用好

直接在服务端组件里处理IP和封禁判断,页面渲染时就能直接给出结果,不会出现先加载游戏内容再被拦截的情况,用户体验更顺畅,也少了一次额外的网络请求。

内容的提问来源于stack exchange,提问作者DOMESDAY

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 17:39:24