Geografische routering aan de rand: personalisatie van inhoud en naleving van de AVG
Leer routeringslogica te bouwen op basis van land, regio en taal direct in de Workers, met voorbeelden voor geofencing, gelokaliseerde prijzen en AVG-naleving zonder de hoofdserver te wijzigen.
Waarom geografische routering aan de rand thuishoort
Het personaliseren van inhoud op basis van geografische locatie is een van de meest voorkomende behoeften in wereldwijde webapplicaties: prijzen weergeven in de lokale valuta, voldoen aan landspecifieke regelgeving (GDPR in Europa, CCPA in Californië), inhoud in bepaalde rechtsgebieden blokkeren en omleiden richting regionale domeinen.
Historisch gezien werd dit afgehandeld met geolocatiedatabases op de server (MaxMind GeoLite2) of met handmatig geconfigureerde CDN-regels. Beide benaderingen ze hebben beperkingen: de databaseserver voegt latentie toe, de CDN-regels zijn statisch en moeilijk dynamisch bij te werken.
Met Cloudflare Workers is geolocatie dat wel al beschikbaar
in het voorwerp request.cf zonder enige database om bij te houden.
Cloudflare bepaalt de locatie op basis van de BGP-topologie van het netwerk,
niet bij het opzoeken van IP-adressen, met een nauwkeurigheid op landniveau van meer dan 99,9%.
Wat je gaat leren
- Woningen beschikbaar in
request.cfvoor geolocatie - Geofencing: blokkeren en omleiden per land
- Gelokaliseerde prijzen: valuta en btw op basis van regio
- AVG-naleving: automatische toestemming voor cookies voor EU-gebruikers
- Routering tussen meerdere regio's met aangepaste headers
- Geogebaseerde logica testen zonder implementatie
Het voorwerp request.cf van Cloudflare
Elk verzoek aan een Cloudflare-werknemer bevat het onderwerp cf met
Geografische en netwerkmetadata bepaald door Cloudflare in realtime:
// 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: blokkeren per land
Geo-fencing is het patroon van het blokkeren of omleiden van inhoud naar specifieke landen. De meest voorkomende gebruiksscenario’s zijn: blokkeren voor internationale sancties, inhoud met territoriale licenties (streaming, media) en markten nog niet open voor bepaalde producten:
// 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 {}
Gelokaliseerde prijzen en valuta's
Het weergeven van prijzen in de lokale valuta van de klant is een best practice van e-commerce die de conversie verhoogt. Met de Werknemer kan dat bepaal de juiste valuta voordat het verzoek zelfs maar bereikt is de oorspronkelijke 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: '






