Géolocalisation IP en Next.js : modèles navigateur et serveur
Next.js peut rendre la même page côté serveur ou dans le navigateur, et ce choix détermine quel point de terminaison Watch-IP résout réellement la position d'un visiteur — ou résout celle de votre serveur à la place.
Avant de commencer
- Une clé visiteur publiable pour le modèle client, ou une clé secrète de recherche pour le modèle serveur — voir authentification.
- Un projet Next.js App Router (les exemples supposent la répartition Composants Serveur/Client de Next.js 16).
- Pour le modèle serveur, un en-tête défini par votre plateforme d'hébergement sur les requêtes entrantes avec l'IP réelle du visiteur (par exemple CF-Connecting-IP sur Cloudflare) — pas un en-tête qu'un client pourrait définir lui-même.
1. Comprendre ce que chaque modèle appelle réellement
GET /v1/geo identifie quelle que soit la connexion qui atteint l'API — le navigateur d'un visiteur qui l'appelle directement, ou votre serveur Next.js si votre propre code l'appelle pendant le rendu serveur. Appeler GET /v1/geo depuis un composant serveur ou un gestionnaire de route résout la position réseau de votre serveur, pas celle du visiteur, ce qui n'est généralement pas ce que vous voulez. Deux modèles résolvent réellement le visiteur : exécuter la requête dans le navigateur (composant client), ou l'exécuter sur votre serveur avec une IP fournie via GET /v1/lookup/{ip}.
2. Composant client : appeler GET /v1/geo depuis le navigateur
C'est la même requête que celle utilisée par les guides JavaScript et React — un composant client qui effectue un fetch avec une clé publiable. L'en-tête Origin du navigateur lui-même satisfait le contrôle des origines autorisées de la clé, donc l'URL publique de votre application doit figurer sur cette liste.
TypeScript (composant client)
"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 ?? "votre région"}</span>;
}3. Composant serveur ou gestionnaire de route : rechercher explicitement l'IP du visiteur
Pour résoudre la position d'un visiteur pendant le rendu serveur, n'appelez pas GET /v1/geo — appelez GET /v1/lookup/{ip} avec une clé secrète et l'IP réelle du visiteur, lue depuis l'en-tête que votre plateforme d'hébergement définit sur la requête entrante. Ne faites jamais confiance à une simple valeur X-Forwarded-For qu'un client pourrait définir lui-même, sauf si votre plateforme la supprime et la redéfinit en périphérie (edge).
TypeScript (composant serveur)
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 ?? "votre région"}</span>;
}4. Ne mettez pas en cache le résultat d'un visiteur pour un autre
cache: 'no-store' ci-dessus est délibéré : un résultat de recherche est spécifique au visiteur qui l'a demandé. Si ce composant s'affiche dans une route que Next.js ou un CDN peut mettre en cache — une page statique, une page ISR, un cache de fetch partagé — une réponse mise en cache afficherait ensuite la ville d'un visiteur à tout le monde. Gardez la branche personnalisée rendue dynamiquement, et n'intégrez jamais une réponse de recherche dans un cache au niveau de la page ou du CDN.
Erreurs et cas limites
Un en-tête IP manquant ou vide (certaines configurations locales ou de proxy n'en définissent pas) signifie qu'il n'y a pas d'IP visiteur à rechercher — affichez le repli plutôt que d'appeler le point de terminaison avec une chaîne vide. Un 403 depuis GET /v1/lookup/{ip} signifie généralement qu'une clé publiable a été utilisée là où la route nécessite une clé secrète ; un 400 signifie que l'en-tête contenait une valeur qui n'est pas une adresse IPv4/IPv6 valide. Voir erreurs et limites de débit.
Étape suivante
Pour un composant réutilisable aussi bien dans des applications React purement client que dans Next.js, voir le guide React — le modèle de composant client ci-dessus est un wrapper spécifique à Next.js autour de la même requête.
Pages associées
- Géolocalisation du visiteurAppelez GET /v1/geo depuis le navigateur avec une clé publiable.
- Référence de GET /v1/geoRéférence complète des champs pour le point de terminaison de géolocalisation du visiteur.
- Référence de GET /v1/lookup/:ipRéférence complète des champs pour le point de terminaison de recherche d'IP arbitraire.
- Guide ReactAjouter la position du visiteur à un composant React.