IP-geolocatie in Next.js: browser- en serverpatronen
Next.js kan dezelfde pagina op de server of in de browser renderen, en die keuze bepaalt welk Watch-IP-endpoint daadwerkelijk de locatie van een bezoeker herleidt — of in plaats daarvan die van je server.
Voordat je begint
- Een publiceerbare bezoekerssleutel voor het clientpatroon, of een geheime lookup-sleutel voor het serverpatroon — zie authenticatie.
- Een Next.js App Router-project (voorbeelden gaan uit van de Server/Client Component-splitsing van Next.js 16).
- Voor het serverpatroon: een header die je hostingplatform op inkomende verzoeken zet met het echte IP van de bezoeker (bijvoorbeeld CF-Connecting-IP op Cloudflare) — niet een header die een client zelf zou kunnen instellen.
1. Begrijpen wat elk patroon daadwerkelijk aanroept
GET /v1/geo identificeert wie er ook maar verbinding maakt met de API — de browser van een bezoeker die het rechtstreeks aanroept, of je Next.js-server als je eigen code het aanroept tijdens server-rendering. Het aanroepen van GET /v1/geo vanuit een Server Component of Route Handler herleidt de netwerklocatie van je server, niet die van de bezoeker, wat meestal niet is wat je wilt. Twee patronen herleiden daadwerkelijk de bezoeker: het verzoek in de browser uitvoeren (Client Component), of het op je server uitvoeren tegen een opgegeven IP met GET /v1/lookup/{ip}.
2. Client Component: GET /v1/geo aanroepen vanuit de browser
Dit is hetzelfde verzoek dat de JavaScript- en React-handleidingen gebruiken — een Client Component die met een publiceerbare sleutel een fetch doet. De eigen Origin-header van de browser voldoet aan de controle op toegestane origins van de sleutel, dus de publieke URL van je app moet op die lijst staan.
TypeScript (Client Component)
"use client";
import { useEffect, useState } from "react";
export function VisitorLocation() {
const [city, setCity] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
fetch("https://api.watch-ip.com/v1/geo", {
headers: { "X-Api-Key": "YOUR_PUBLISHABLE_KEY" },
signal: controller.signal,
})
.then((res) => (res.ok ? res.json() : null))
.then((geo) => setCity(geo?.city ?? null))
.catch(() => {});
return () => controller.abort();
}, []);
return <span>{city ?? "jouw regio"}</span>;
}3. Server Component of Route Handler: het IP van de bezoeker expliciet opzoeken
Om de locatie van een bezoeker tijdens server-rendering te herleiden, roep je niet GET /v1/geo aan — roep je GET /v1/lookup/{ip} aan met een geheime sleutel en het echte IP van de bezoeker, gelezen uit welke header je hostingplatform ook op het inkomende verzoek zet. Vertrouw nooit op een gewone X-Forwarded-For-waarde die een client zelf zou kunnen instellen, tenzij je platform deze aan de edge verwijdert en opnieuw zet.
TypeScript (Server Component)
import { headers } from "next/headers";
export async function VisitorLocationServer() {
const ip = (await headers()).get("cf-connecting-ip");
if (!ip) return null;
const response = await fetch(`https://api.watch-ip.com/v1/lookup/${ip}`, {
headers: { Authorization: "Bearer YOUR_SECRET_KEY" },
cache: "no-store",
});
if (!response.ok) return null;
const geo = await response.json();
return <span>{geo.city ?? "jouw regio"}</span>;
}4. Cache het resultaat van de ene bezoeker niet voor een andere
cache: 'no-store' hierboven is bewust: een lookup-resultaat is specifiek voor de bezoeker die het heeft aangevraagd. Als deze component rendert binnen een route die Next.js of een CDN kan cachen — een statische pagina, een ISR-pagina, een gedeelde fetch-cache — zou een gecachet antwoord daarna de stad van één bezoeker aan iedereen tonen. Houd de gepersonaliseerde tak dynamisch gerenderd, en voeg een lookup-antwoord nooit samen met een cache op paginaniveau of CDN-niveau.
Foutmeldingen en randgevallen
Een ontbrekende of lege IP-header (sommige lokale of proxy-configuraties zetten er geen) betekent dat er geen bezoekers-IP is om op te zoeken — render de terugvaloptie in plaats van het endpoint met een lege string aan te roepen. Een 403 van GET /v1/lookup/{ip} betekent meestal dat er een publiceerbare sleutel is gebruikt waar de route een geheime sleutel nodig heeft; een 400 betekent dat de header iets bevatte dat geen geldig IPv4/IPv6-adres is. Zie foutmeldingen en rate limits.
Volgende stap
Voor een component die je zowel binnen client-only React-apps als binnen Next.js kunt hergebruiken, zie de React-handleiding — het Client Component-patroon hierboven is een Next.js-specifieke wrapper rond hetzelfde verzoek.
Gerelateerde pagina's
- BezoekersgeolocatieRoep GET /v1/geo aan vanuit de browser met een publiceerbare sleutel.
- GET /v1/geo-referentieVolledige veldreferentie voor het bezoekersgeolocatie-endpoint.
- GET /v1/lookup/:ip-referentieVolledige veldreferentie voor het endpoint voor het opzoeken van een willekeurig IP.
- React-handleidingBezoekerslocatie toevoegen aan een React-component.