Executive summary: Cloudflare's edge gives you
request.cf.countrybefore your origin renders. Pair it with an eligibility-aware overlay and you can monetize blocked traffic without origin round-trips for those visitors. Pattern: Worker checks region → injects ReTarget script via HTMLRewriter → auction API enforces license eligibility server-side. Sites at seven-figure blocked pageviews save measurable origin bandwidth.
Cloudflare's edge gives you everything needed to detect and act on geo-blocked traffic before the page fully renders. Combined with an eligibility-aware overlay like ReTarget.gg, you flip a per-region revenue line on without touching your origin application code.
This is the pattern we recommend for high-traffic iGaming and regulated publishers already on Cloudflare.
The 30-second mental model
Visitor
│
▼
Cloudflare Edge (Worker)
│
├─ In licensed region? ──YES──► Pass through to origin (unchanged)
│
└─ NO (blocked)
│
├─ Pattern A: Pass to origin + client-side widget detects
│
└─ Pattern B: HTMLRewriter injects script at edge
│
▼
ReTarget auction API
(eligibility check)
│
▼
Overlay renders (or no-op)
Pattern A is the default install — simplest, works everywhere.
Pattern B saves origin bandwidth on every blocked impression. For sites doing seven-figure blocked pageviews monthly, that is measurable money and latency.
Why edge-side detection matters
You can ship the ReTarget script on every page and let the client-side widget detect blocked visitors. That works fine for most publishers.
Edge-side detection wins when:
| Concern | Client-side only | Edge injection |
|---|---|---|
| Script load on in-region visitors | ~14 KB every session | 0 KB — script only for blocked |
| Adblocker resistance | Third-party script fetch | Inline injection in origin HTML |
| Origin bandwidth for blocked geos | Full page render | Optional: static shell only |
| Auction context | Client-detected | Worker can pass data-region, data-vertical |
| Implementation complexity | Low | Medium — test caching interactions |
Rule of thumb: Under 50k blocked monthly pageviews, client-side is fine. Above 500k, edge injection pays for the engineering time in weeks.
The minimal Worker (Pattern B)
// worker.ts — deploy via Wrangler or Cloudflare dashboard
const RETARGET_SCRIPT = `
<script>
(function () {
var s = document.createElement("script");
s.src = "https://cdn.retarget.gg/widget.js";
s.async = true;
s.setAttribute("data-pub", "YOUR_PUBLIC_KEY");
s.setAttribute("data-website", "YOUR_WEBSITE_KEY");
(document.head || document.documentElement).appendChild(s);
})();
</script>`;
// Your licensed regions — align with Geo rules in dashboard
const ALLOWED = new Set(["GB", "DE", "CA", "IE", "NL", "MT"]);
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const country = request.cf?.country ?? "XX";
// In-region: pass through untouched
if (ALLOWED.has(country)) {
return fetch(request);
}
// Blocked: fetch origin and inject widget
const upstream = await fetch(request);
// Only transform HTML responses
const contentType = upstream.headers.get("content-type") ?? "";
if (!contentType.includes("text/html")) {
return upstream;
}
return new HTMLRewriter()
.on("head", {
element(element) {
element.append(RETARGET_SCRIPT, { html: true });
},
})
.transform(upstream);
},
} satisfies ExportedHandler<Env>;That is the core. Cloudflare gives you request.cf.country for free on every request. You check it against your licensed-region set. Only blocked visitors get the script injected. In-region visitors receive the unmodified origin response.
Keys: Get YOUR_PUBLIC_KEY and YOUR_WEBSITE_KEY from the dashboard. See Quickstart.
Enhanced Worker: pass auction context
Give the auction engine richer signals from the edge:
const RETARGET_SCRIPT = (country: string, colo: string) => `
<script>
(function () {
var s = document.createElement("script");
s.src = "https://cdn.retarget.gg/widget.js";
s.async = true;
s.setAttribute("data-pub", "YOUR_PUBLIC_KEY");
s.setAttribute("data-website", "YOUR_WEBSITE_KEY");
s.setAttribute("data-region", "${country}");
s.setAttribute("data-edge-colo", "${colo}");
s.setAttribute("data-source", "cf-worker");
(document.head || document.documentElement).appendChild(s);
})();
</script>`;
export default {
async fetch(request: Request): Promise<Response> {
const country = request.cf?.country ?? "XX";
const colo = request.cf?.colo ?? "unknown";
if (ALLOWED.has(country)) {
return fetch(request);
}
const upstream = await fetch(request);
const script = RETARGET_SCRIPT(country, colo);
return new HTMLRewriter()
.on("head", {
element(el) {
el.append(script, { html: true });
},
})
.transform(upstream);
},
};This helps reporting distinguish edge-injected impressions from client-side installs in Analytics.
Pattern C: skip origin entirely for blocked visitors
For maximum origin savings, serve a static blocked shell from the Worker without calling origin:
const BLOCKED_HTML = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Service unavailable in your region</title>
<!-- ReTarget script inline here -->
</head>
<body>
<p>This service is not available in your region.</p>
</body>
</html>`;
export default {
async fetch(request: Request): Promise<Response> {
const country = request.cf?.country ?? "XX";
if (ALLOWED.has(country)) {
return fetch(request);
}
return new Response(BLOCKED_HTML, {
headers: { "content-type": "text/html;charset=UTF-8" },
});
},
};Tradeoff: You lose origin personalization for blocked visitors entirely. Good for hard geo-walls; bad if blocked visitors still need account-specific content.
Eligibility still happens at auction time
A common worry: if the Worker injects the script for any blocked visitor, won't unlicensed-region visitors see ineligible offers?
No. The eligibility engine runs server-side at auction time:
- Worker injects the script (dumb, fast).
- Script calls
api.retarget.ggwith visitor geo. - API filters to advertisers licensed in that region.
- Auction runs among eligible offers only.
- If no eligible offer exists → overlay does not render.
Worst case: the API returns no offer and the visitor sees your blocked page without monetization. There is no scenario where an unlicensed advertiser wins in a regulated geo.
Caching gotchas (read before shipping)
HTMLRewriter and Cloudflare cache interact in non-obvious ways:
| Scenario | Risk | Mitigation |
|---|---|---|
| Cache keyed only by URL | In-region visitor gets blocked-injected HTML from cache | Vary cache on CF-IPCountry header |
Cache-Control: public on origin | Blocked response cached for in-region users | Separate cache keys or bypass cache for HTML |
| Workers Cache API | Stale injection state | Include country in cache key |
| CDN purging | Old script version served | Version your Worker deployment |
Test matrix before production:
- VPN to DE (in-region) → no script in HTML source
- VPN to BR (blocked) → script present in HTML source
- Switch VPN DE → BR on same browser → correct behavior after cache TTL
- Verify in-region conversion funnel unchanged
Deeper walkthrough: Cloudflare integration recipe.
Wrangler deployment checklist
# wrangler.toml
name = "retarget-geo-inject"
main = "src/worker.ts"
compatibility_date = "2024-01-01"
[vars]
# Do NOT put API secrets here — public keys only
RETARGET_PUB_KEY = "pk_live_..."
RETARGET_WEBSITE_KEY = "wk_live_..."npx wrangler deploy
npx wrangler tail # watch first 1,000 blocked requests- Route pattern covers all public HTML paths
-
ALLOWEDset matches Geo rules in dashboard - Cache variation configured
- Staging Worker tested with 3+ VPN geos
- Analytics shows
data-source=cf-workerimpressions
When NOT to use this pattern
- You do not already use Cloudflare. Do not migrate CDN for this alone. Client-side install is fine.
- Aggressive edge caching without cache-key discipline. You will serve wrong HTML to wrong geos.
- Heavy per-user dynamic origin content for blocked visitors. Pattern C will not work; Pattern A or B still calls origin.
- Decline Popup triggers. KYC failures happen post-origin. Edge injection handles geo-block only; wire Decline Popup in your application layer.
Combining with Geotargetly or other routers
If Geotargetly runs redirect rules on the same page:
- Geotargetly evaluates redirects first (client-side or their own Worker).
- Cloudflare Worker injects ReTarget only for non-redirected, blocked visitors.
- No double-overlay if sequenced correctly.
See Geotargetly integration recipe.
Performance benchmarks (indicative)
From publishers who shipped Pattern B:
| Metric | Before (client-side everywhere) | After (edge injection) |
|---|---|---|
| Script bytes on in-region sessions | ~14 KB | 0 KB |
| Origin requests from blocked geos | 100% | 0–100% (depends on pattern) |
| Time to first overlay paint | ~800ms | ~400–600ms |
| Blocked session revenue | baseline | ±5% (auction identical) |
Revenue should be neutral — the auction is the same. Savings are bandwidth, latency, and in-region page weight.
FAQ
Does this work with Cloudflare Zero Trust? Yes. Worker runs before access policies in most configurations. Test your specific Access rules.
What about EU visitors and GDPR? ReTarget's script loads only for blocked visitors. Pass no PII from Worker to script. Standard affiliate disclosure applies.
Can I use Workers KV for dynamic allowlists? Yes — store licensed regions in KV and update without redeploying Worker code.
Does HTMLRewriter work with Next.js / React SSR?
Yes, on the HTML output. Test hydration — injected script in <head> should not break React.
What if request.cf.country is wrong?
Rare but possible (VPN, corporate proxies). Auction API uses additional geo signals. Monitor no-offer rate per geo.
Related reading
- How to monetize geo-blocked traffic in iGaming
- Geotargetly alternatives
- Cloudflare integration recipe
- Widget installation
Want help wiring this for your stack? Contact the team with your Cloudflare zone and license map.