Client-side consent is the default pattern almost every consent management platform (CMP) uses. The page loads, a JavaScript banner runs, and until the visitor clicks "accept," the script blocks tag managers and trackers from firing. Consent state lives in the browser (usually a cookie or localStorage), and scripts are switched on and off at runtime.
Server-side consent means the decision is enforced before the response is even built. The consent state travels with the request (a cookie header), and the server — or an edge worker — simply doesn't emit tracking scripts, consent-requiring cookies, or Set-Cookie headers unless the visitor has consented. Nothing to block, because nothing was ever sent.
Under the GDPR and the ePrivacy Directive, the requirements that matter for architecture are:
Notably, neither law says "you must use JavaScript." Regulators have fined companies not for choosing client-side consent, but for implementing it badly: tags firing before the click, dark-pattern accept buttons, and cookies set despite a rejection. Both patterns can comply. Both can fail. The difference is how easy each one makes it to fail silently.
| Client-side | Server-side | |
|---|---|---|
| Where enforcement happens | Browser, after page load | Server / edge, before response |
| Works without JavaScript | No | Yes |
| Flash of tracking before banner | Common failure mode | Structurally impossible |
| Works with CDN caching | Yes (state is per-browser) | Needs care — cache keys must not vary on consent cookie |
| Typical effort on an existing site | Low — drop in a CMP | Higher — touches rendering/edge layer |
| Third-party tags (analytics, ads) | Controlled via tag manager triggers | Still usually client-side; server only gates first-party scripts |
| Auditability | Depends on CMP logs | Server logs show exactly what was withheld |
The core idea: read a consent cookie on the request, and branch the response on it. On an edge platform like Cloudflare Workers this is a few lines:
export default {
async fetch(req, env) {
const res = await fetch(req);
const consent = getCookie(req, "consent") || "";
if (!consent.includes("analytics")) {
// Strip consent-requiring cookies from the response
const headers = new Headers(res.headers);
headers.delete("set-cookie");
// Optionally rewrite the HTML to omit tracking snippets
return new Headers(headers).has("content-type") &&
headers.get("content-type").includes("text/html")
? rewriteHtml(new Response(res.body, { headers }), env)
: new Response(res.body, { headers });
}
return res;
}
}
On a classic stack the same logic lives in middleware — Nginx can even do a crude version with map on $cookie_consent to include or exclude an analytics snippet file:
map $cookie_consent $analytics_snippet {
default "";
"~analytics" "includes/analytics.html";
}
server {
...
sub_filter '<!--ANALYTICS-->' '';
# or ssi include of $analytics_snippet with SSI enabled
}
If client-side is your pattern (and for most sites it should be — it's what every mainstream CMP does), the failures to avoid are well documented from enforcement practice:
Application → Cookies in devtools. If _ga, _fbp or similar appear anyway, your CMP isn't actually gating anything.Open-source options (Klaro!, Tarteaucitron, Orejime) make all of this achievable without licensing fees — which is exactly what our scanner's fix guidance recommends.
In practice the robust setup is a hybrid: the consent state is a first-party cookie readable by both sides. The client-side banner sets it; the server/edge layer reads it and withholds what it can (first-party Set-Cookie headers, injected snippets); the tag manager handles the third-party scripts client-side. One source of truth, enforced in both places, and a devtools check on either side confirms the other is honest.
| Your situation | Recommended pattern |
|---|---|
| Marketing site or SaaS with third-party tags, small team | Client-side (a proper CMP), verified manually |
| High-traffic site on Cloudflare/Vercel with edge workers | Hybrid — edge-gated first-party cookies + CMP for tags |
| Regulated / audit-heavy environment (finance, health-adjacent) | Hybrid with server-side proof logging |
| Mostly first-party analytics, no ad tech | Server-side gating is realistic and simple |
Whatever pattern you run, verify it the way a regulator would: open the site in a fresh private window, click reject, reload, and inspect which cookies and network calls appear anyway. Our free scanner automates part of this — it detects whether a consent mechanism is present at all and flags missing security headers and legal pages alongside:
For auditor-ready PDF reports across a whole domain portfolio, see EUComply Pro ($79/year).