Watch-IP

Next.js'te IP Konum Belirleme: Tarayıcı ve Sunucu Desenleri

Next.js aynı sayfayı sunucuda veya tarayıcıda render edebilir ve bu seçim, hangi Watch-IP endpoint'inin gerçekte bir ziyaretçinin konumunu — ya da onun yerine sunucunuzun konumunu — çözümlediğini değiştirir.

Başlamadan önce

  • İstemci deseni için yayınlanabilir bir ziyaretçi anahtarı, ya da sunucu deseni için gizli bir sorgulama anahtarı — kimlik doğrulama sayfasına bakın.
  • Bir Next.js App Router projesi (örnekler, Next.js 16'nın Server/Client Component ayrımını varsayar).
  • Sunucu deseni için, barındırma platformunuzun gelen isteklere ziyaretçinin gerçek IP'siyle birlikte ayarladığı bir başlık (örneğin Cloudflare'de CF-Connecting-IP) — bir istemcinin kendisinin ayarlayabileceği bir başlık değil.

1. Her desenin gerçekte neyi çağırdığını anlayın

GET /v1/geo, API'ye kimin bağlantısının ulaştığını belirler — bunu doğrudan çağıran bir ziyaretçinin tarayıcısı, ya da kendi kodunuz sunucu tarafında render sırasında çağırıyorsa Next.js sunucunuz. GET /v1/geo'yu bir Server Component veya Route Handler'dan çağırmak, genellikle istemediğiniz bir şekilde, ziyaretçinin değil sunucunuzun ağ konumunu çözümler. Ziyaretçiyi gerçekten çözümleyen iki desen vardır: isteği tarayıcıda çalıştırmak (Client Component), ya da sunucunuzda, sağlanan bir IP'ye karşı GET /v1/lookup/{ip} ile çalıştırmak.

2. Client Component: GET /v1/geo'yu tarayıcıdan çağırın

Bu, JavaScript ve React kılavuzlarının kullandığı istekle aynıdır — yayınlanabilir bir anahtarla fetch yapan bir Client Component. Tarayıcının kendi Origin başlığı, anahtarın izin verilen origin kontrolünü karşılar; bu yüzden uygulamanızın herkese açık URL'sinin bu listede olması gerekir.

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 ?? "bölgeniz"}</span>;
}

3. Server Component veya Route Handler: ziyaretçinin IP'sini açıkça sorgulayın

Sunucu tarafında render sırasında bir ziyaretçinin konumunu çözümlemek için GET /v1/geo'yu çağırmayın — bir gizli anahtarla ve barındırma platformunuzun gelen istek üzerinde ayarladığı başlıktan okunan ziyaretçinin gerçek IP'siyle GET /v1/lookup/{ip}'yi çağırın. Platformunuz bunu edge'de temizleyip yeniden ayarlamıyorsa, bir istemcinin kendisinin ayarlayabileceği düz bir X-Forwarded-For değerine asla güvenmeyin.

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 ?? "bölgeniz"}</span>;
}

4. Bir ziyaretçinin sonucunu bir başkası için önbelleğe almayın

Yukarıdaki cache: 'no-store' bilinçli bir tercihtir: bir sorgulama sonucu, onu isteyen ziyaretçiye özeldir. Bu bileşen, Next.js'in veya bir CDN'nin önbelleğe alabileceği bir rota içinde render edilirse — statik bir sayfa, bir ISR sayfası, paylaşılan bir fetch önbelleği — önbelleğe alınmış bir yanıt, bir ziyaretçinin city'sini ondan sonra herkese gösterir. Kişiselleştirilmiş dalı dinamik olarak render edilmiş tutun ve bir sorgulama yanıtını asla sayfa düzeyinde veya CDN düzeyinde bir önbelleğe katmayın.

Hatalar ve uç durumlar

Eksik veya boş bir IP başlığı (bazı yerel veya proxy kurulumları bunu ayarlamaz), sorgulanacak bir ziyaretçi IP'si olmadığı anlamına gelir — endpoint'i boş bir dizeyle çağırmak yerine yedek çözümü render edin. GET /v1/lookup/{ip}'den gelen bir 403, genellikle rotanın gizli bir anahtar gerektirdiği yerde yayınlanabilir bir anahtar kullanıldığı anlamına gelir; bir 400 ise başlığın geçerli bir IPv4/IPv6 adresi olmayan bir şey tuttuğu anlamına gelir. Hatalar ve hız sınırları sayfasına bakın.

Sonraki adım

Yalnızca istemci tarafında çalışan React uygulamalarında olduğu kadar Next.js'te de yeniden kullanabileceğiniz bir bileşen için React kılavuzuna bakın — yukarıdaki Client Component deseni, aynı isteğin Next.js'e özgü bir sarmalayıcısıdır.

İlgili sayfalar