Geografické směrování na okraji: Personalizace obsahu a soulad s GDPR
Naučte se budovat logiku směrování na základě země, regionu a jazyka přímo v Workers, s příklady pro geo-oplocení, lokalizované ceny a soulad s GDPR bez změny hlavního serveru.
Proč geografické směrování patří na okraj
Personalizace obsahu na základě geografické polohy je jednou z nich nejběžnější potřeby v globálních webových aplikacích: zobrazení cen v v místní měně, dodržujte předpisy specifické pro jednotlivé země (GDPR v Evropě, CCPA v Kalifornii), blokování obsahu v určitých jurisdikcích a přesměrování směrem k regionálním doménám.
Historicky to bylo řešeno geolokačními databázemi na straně serveru (MaxMind GeoLite2) nebo s ručně nakonfigurovanými pravidly CDN. Oba přístupy mají omezení: databázový server přidává latenci, pravidla CDN jsou statická a je obtížné dynamicky aktualizovat.
S Cloudflare Workers je geolokace již k dispozici
v objektu request.cf bez jakékoliv databáze k údržbě.
Cloudflare určuje umístění na základě topologie BGP sítě,
není při vyhledávání IP, s přesností na úrovni země nad 99,9 %.
Co se naučíte
- Vlastnosti dostupné v
request.cfpro geolokaci - Geo-fencing: blokování a přesměrování podle země
- Lokalizované ceny: Měna a DPH podle regionu
- Soulad s GDPR: automatický souhlas se soubory cookie pro uživatele z EU
- Směrování ve více oblastech s vlastními záhlavími
- Testování geograficky založené logiky bez nasazení
Objekt request.cf od Cloudflare
Každý požadavek na Cloudflare Worker obsahuje předmět cf s
Geografická a síťová metadata určená Cloudflare v reálném čase:
// Tutte le proprieta disponibili in request.cf
export default {
async fetch(request: Request): Promise<Response> {
const cf = request.cf as CfProperties;
// Geolocalizzazione
const country = cf.country; // "IT" - ISO 3166-1 alpha-2
const region = cf.region; // "Puglia" - nome della regione
const regionCode = cf.regionCode; // "75" - codice regione
const city = cf.city; // "Bari"
const postalCode = cf.postalCode; // "70121"
const latitude = cf.latitude; // "41.1171"
const longitude = cf.longitude; // "16.8719"
const timezone = cf.timezone; // "Europe/Rome"
const continent = cf.continent; // "EU"
// Rete
const asn = cf.asn; // 1234 - Autonomous System Number
const asOrganization = cf.asOrganization; // "Telecom Italia"
const isEuCountry = cf.isEUCountry; // "1" o "0"
// Performance
const colo = cf.colo; // "FCO" - datacenter Cloudflare piu vicino
const httpProtocol = cf.httpProtocol; // "HTTP/2"
const tlsVersion = cf.tlsVersion; // "TLSv1.3"
return Response.json({
country,
region,
city,
timezone,
continent,
isEuCountry,
colo,
});
},
};
// Tipo per le proprieta cf (parziale)
interface CfProperties {
country?: string;
region?: string;
regionCode?: string;
city?: string;
postalCode?: string;
latitude?: string;
longitude?: string;
timezone?: string;
continent?: string;
asn?: number;
asOrganization?: string;
isEUCountry?: string;
colo?: string;
httpProtocol?: string;
tlsVersion?: string;
}
Geo-Fencing: Blokování podle země
Geo-fencing je vzor blokování nebo přesměrování obsahu na konkrétní zemí. Nejběžnější případy použití jsou: blokování pro mezinárodní sankce, obsah s teritoriálními licencemi (streaming, média) a trhy zatím ne otevřené pro určité produkty:
// src/geo-fence-worker.ts
// Paesi con accesso bloccato (esempio: sanzioni, licenze)
const BLOCKED_COUNTRIES = new Set(['KP', 'IR', 'SY', 'CU']);
// Paesi che richiedono un redirect a una versione localizzata
const REDIRECTS: Record<string, string> = {
DE: 'https://de.example.com',
FR: 'https://fr.example.com',
JP: 'https://jp.example.com',
};
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const cf = request.cf as CfProperties;
const country = cf.country ?? 'US';
const url = new URL(request.url);
// Blocco per paesi non consentiti
if (BLOCKED_COUNTRIES.has(country)) {
return new Response(
JSON.stringify({
error: 'Service not available in your region',
country,
}),
{
status: 451, // 451 Unavailable For Legal Reasons
headers: {
'Content-Type': 'application/json',
'Vary': 'CF-IPCountry',
},
}
);
}
// Redirect verso versione localizzata per certi paesi
const redirectTarget = REDIRECTS[country];
if (redirectTarget && !url.pathname.startsWith('/api/')) {
const targetUrl = new URL(url.pathname + url.search, redirectTarget);
return Response.redirect(targetUrl.toString(), 302);
}
// Aggiunge header con il paese per il downstream (server di origine)
const headers = new Headers(request.headers);
headers.set('CF-Worker-Country', country);
headers.set('CF-Worker-Continent', cf.continent ?? '');
headers.set('CF-Worker-Timezone', cf.timezone ?? '');
// Prosegui verso il server di origine
return fetch(new Request(request.url, {
method: request.method,
headers,
body: ['GET', 'HEAD'].includes(request.method) ? undefined : request.body,
}));
},
};
interface CfProperties {
country?: string;
continent?: string;
timezone?: string;
}
interface Env {}
Lokalizované ceny a měny
Zobrazování cen v místní měně zákazníka je osvědčeným postupem elektronického obchodu, který zvyšuje míru konverze. S Workerem můžete určit správnou měnu dříve, než požadavek vůbec dosáhne původní server:
// src/pricing-worker.ts - prezzi localizzati all'edge
interface CurrencyConfig {
code: string;
symbol: string;
position: 'before' | 'after';
vatRate: number; // IVA in percentuale (0.22 = 22%)
}
const COUNTRY_CURRENCY: Record<string, CurrencyConfig> = {
// Eurozona
IT: { code: 'EUR', symbol: '€', position: 'before', vatRate: 0.22 },
DE: { code: 'EUR', symbol: '€', position: 'before', vatRate: 0.19 },
FR: { code: 'EUR', symbol: '€', position: 'before', vatRate: 0.20 },
ES: { code: 'EUR', symbol: '€', position: 'before', vatRate: 0.21 },
// Altre valute
GB: { code: 'GBP', symbol: '£', position: 'before', vatRate: 0.20 },
US: { code: 'USD', symbol: '






