Kamero

How to Geo-Redirect Visitors by Country Using IP Geolocation

Geo-redirecting visitors to localized versions of your site is one of the most impactful things you can do for international users. A visitor from Germany landing on your English homepage might bounce โ€” but redirect them to your German page and they're far more likely to engage.

This guide covers three approaches: Next.js middleware (fastest), client-side JavaScript (simplest), and server-side (most flexible).

Approach 1: Next.js Middleware (Recommended)

If you're on Vercel, middleware runs at the edge before the page loads. This means zero flash of wrong content:

// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

const COUNTRY_ROUTES: Record<string, string> = {
  DE: "/de",
  FR: "/fr",
  ES: "/es",
  JP: "/ja",
  BR: "/pt",
};

export function middleware(request: NextRequest) {
  // Skip if already on a localized path
  const { pathname } = request.nextUrl;
  if (pathname.startsWith("/de") || pathname.startsWith("/fr") ||
      pathname.startsWith("/es") || pathname.startsWith("/ja") ||
      pathname.startsWith("/pt")) {
    return NextResponse.next();
  }

  // Skip if user has a locale preference cookie
  if (request.cookies.get("locale-preference")) {
    return NextResponse.next();
  }

  const country = request.headers.get("x-vercel-ip-country") || "";
  const redirectPath = COUNTRY_ROUTES[country];

  if (redirectPath && pathname === "/") {
    const url = request.nextUrl.clone();
    url.pathname = redirectPath;
    return NextResponse.redirect(url);
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/"],
};

Approach 2: Client-Side JavaScript

Works on any hosting provider. The tradeoff is a brief flash before redirect:

// Run this early in your page load
async function geoRedirect() {
  // Don't redirect if user chose a locale manually
  if (localStorage.getItem("locale-preference")) return;

  try {
    const { country } = await fetch(
      "https://geo.kamero.ai/api/geo"
    ).then(r => r.json());

    const routes = {
      DE: "/de",
      FR: "/fr",
      ES: "/es",
      JP: "/ja",
      BR: "/pt",
    };

    const target = routes[country];
    if (target && window.location.pathname === "/") {
      window.location.href = target;
    }
  } catch {
    // Silently fail โ€” user stays on default page
  }
}

geoRedirect();

Approach 3: Server-Side (Node.js / Express)

// Express middleware
app.use(async (req, res, next) => {
  if (req.path !== "/" || req.cookies["locale-preference"]) {
    return next();
  }

  try {
    const response = await fetch("https://geo.kamero.ai/api/geo");
    const { country } = await response.json();

    const routes = { DE: "/de", FR: "/fr", ES: "/es" };
    if (routes[country]) {
      return res.redirect(302, routes[country]);
    }
  } catch {
    // Fall through to default
  }

  next();
});

Best Practices for Geo-Redirects

<link rel="alternate" hreflang="en" href="https://example.com/" />
<link rel="alternate" hreflang="de" href="https://example.com/de" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr" />
<link rel="alternate" hreflang="x-default" href="https://example.com/" />

Geo-Redirect vs Geo-Content

An alternative to redirecting is serving different content on the same URL based on location. This avoids the redirect latency and is simpler to implement:

// Server Component (Next.js on Vercel)
import { headers } from "next/headers";

const messages = {
  DE: { greeting: "Willkommen!", cta: "Jetzt starten" },
  FR: { greeting: "Bienvenue!", cta: "Commencer" },
  JP: { greeting: "ใ‚ˆใ†ใ“ใ!", cta: "ๅง‹ใ‚ใ‚‹" },
  default: { greeting: "Welcome!", cta: "Get Started" },
};

export default async function Home() {
  const h = await headers();
  const country = h.get("x-vercel-ip-country") || "default";
  const msg = messages[country] || messages.default;

  return (
    <main>
      <h1>{msg.greeting}</h1>
      <button>{msg.cta}</button>
    </main>
  );
}

This approach works well for marketing pages where you want localized copy without maintaining separate URL structures.

Get Visitor Country Instantly

Free API, no key required. Works from any framework.

View Documentation โ†’