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

基于用户位置设置Next.js网站URL路径的实现方案咨询

实现Next.js网站基于地理位置的自动重定向到对应国家代码路径

核心思路

优先通过服务器端获取用户国家代码(避免客户端隐私授权限制),再对根路径请求执行重定向;同时需排除已带国家代码的路径和静态资源,防止循环重定向。


方案一:Next.js App Router(推荐用Middleware)

1. 使用全局中间件(Middleware)

在项目根目录创建middleware.ts,拦截根路径请求并处理重定向:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname
  
  // 跳过静态资源、Next.js内部资源,以及已包含国家代码的路径
  if (
    pathname.startsWith('/_next/') ||
    pathname.startsWith('/static/') ||
    /^\/[A-Za-z]{2}$/.test(pathname)
  ) {
    return NextResponse.next()
  }

  // 获取国家代码:优先用CDN提供的请求头(如Cloudflare的CF-IPCountry)
  const countryCode = request.headers.get('CF-IPCountry')?.toLowerCase()
  // 无有效代码时 fallback 到默认国家(如'us')
  const targetCountry = countryCode?.length === 2 ? countryCode : 'us'

  // 执行重定向
  return NextResponse.redirect(new URL(`/${targetCountry}`, request.url))
}

// 指定中间件生效范围:仅根路径
export const config = {
  matcher: '/',
}

2. 服务器端根布局处理

若无需全局拦截,可在根布局组件中处理:

import { redirect } from 'next/navigation'
import { headers } from 'next/headers'

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const headersList = headers()
  const countryCode = headersList.get('CF-IPCountry')?.toLowerCase() || 'us'
  const currentPath = headersList.get('x-nextjs-pathname') || ''

  // 若当前路径未匹配国家代码,执行重定向
  if (!currentPath.startsWith(`/${countryCode}`)) {
    redirect(`/${countryCode}`)
  }

  return (
    <html lang={countryCode}>
      <body>{children}</body>
    </html>
  )
}

方案二:Next.js Pages Router

1. 通过getServerSideProps处理

在pages/_app.tsx中添加服务器端逻辑:

import type { AppProps } from 'next/app'

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

export async function getServerSideProps(context: any) {
  const { req, res } = context
  const pathname = req.url

  // 跳过静态资源和已带国家代码的路径
  if (
    pathname.startsWith('/_next/') ||
    pathname.startsWith('/static/') ||
    /^\/[A-Za-z]{2}$/.test(pathname)
  ) {
    return { props: {} }
  }

  // 获取国家代码:CDN头或IP解析
  const countryCode = req.headers['cf-ipcountry']?.toLowerCase() || 'us'

  // 发送重定向响应
  res.writeHead(302, { Location: `/${countryCode}` })
  res.end()

  return { props: {} }
}

export default MyApp

关键补充说明

  • 无CDN时的IP解析:如果未使用Cloudflare等带地理位置能力的CDN,可通过本地IP库(如MaxMind GeoIP2)解析用户IP:
    import { Reader } from '@maxmind/geoip2-node'
    // 需提前下载GeoLite2-Country.mmdb数据库文件
    const reader = await Reader.open('GeoLite2-Country.mmdb')
    const userIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress
    const countryCode = reader.country(userIp).country?.isoCode?.toLowerCase() || 'us'
    
  • 客户端方案(不推荐):若必须用客户端获取,需用户授权地理位置,再调用后端接口转换为国家代码:
    useEffect(() => {
      if (!window.location.pathname.match(/^\/[A-Za-z]{2}$/)) {
        navigator.geolocation.getCurrentPosition(async (pos) => {
          const res = await fetch(`/api/get-country?lat=${pos.coords.latitude}&lon=${pos.coords.longitude}`)
          const { countryCode } = await res.json()
          window.location.href = `/${countryCode}`
        }, () => {
          // 用户拒绝授权时跳转默认国家
          window.location.href = '/us'
        })
      }
    }, [])
    
  • 避免循环重定向:必须严格排除已包含国家代码的路径,否则会触发无限重定向逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 08:35:19