Every visitor hits your origin with an IP. Resolve it once at the edge, cache it, and ship the right currency, language, and legal banners before your React bundle finishes parsing.
| Field | Use |
|---|---|
country | Currency, legal banners (GDPR, CCPA), regional pricing |
country_name | Human-readable "shipping to Germany" |
region | US state for sales-tax routing; EU region for VAT |
timezone | "Delivery at 3pm your time" instead of "3pm UTC" |
At the CDN edge, look up the visitor IP once per session and cache the result in a signed cookie. Your origin never pays the RTT.
// Cloudflare Worker — geo-cookie export default { async fetch(req, env) { const cookie = getCookie(req, 'ipa_geo'); if (cookie) return fetch(req, { cf: { cookie } }); const ip = req.headers.get('cf-connecting-ip'); const r = await fetch(`https://api.ip-atlas.io/json/${ip}`, { headers: { 'X-API-Key': env.IPATLAS_KEY } }).then(r => r.json()); const res = await fetch(req); res.headers.append('Set-Cookie', `ipa_geo=${r.country}; Max-Age=86400; Path=/; Secure`); return res; } };
// middleware.ts export async function middleware(req) { const ip = req.ip; const r = await fetch(`https://api.ip-atlas.io/json/${ip}`, { headers: { 'X-API-Key': process.env.IPATLAS_KEY! } }).then(r => r.json()); const res = NextResponse.next(); res.cookies.set('country', r.country, { maxAge: 86400 }); res.cookies.set('tz', r.timezone, { maxAge: 86400 }); return res; }