Contents
- 1. The two patterns, in one paragraph each
- 2. What the law actually requires (and what it doesn't)
- 3. Side-by-side comparison
- 4. Server-side consent: how to build it
- What server-side consent does well
- What it can't do alone
- 5. Client-side consent: how to build it properly
- 6. The hybrid pattern most teams end up with
- 7. How to choose
- 8. Test your current setup
Server-side vs client-side cookie consent: which pattern fits your stack
Table of contents
1. The two patterns, in one paragraph each 2. What the law actually requires (and what it doesn't) 3. Side-by-side comparison 4. Server-side consent: how to build it 5. Client-side consent: how to build it properly 6. The hybrid pattern most teams end up with 7. How to choose 8. Test your current setup1. The two patterns, in one paragraph each
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.
2. What the law actually requires (and what it doesn't)
Under the GDPR and the ePrivacy Directive, the requirements that matter for architecture are:
- Prior consent for non-essential cookies and similar storage — before they are set, not after.
- Proof of consent — what was accepted, when, and under which banner version.
- Withdrawal must be as easy as giving it — a working "reject" and a re-openable preference center.
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.
3. Side-by-side comparison
| 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 |
4. Server-side consent: how to build it
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
}
What server-side consent does well
- First-party analytics and session cookies can be genuinely withheld — nothing ships.
- No dependence on the visitor allowing JavaScript, so consent holds even for hardened browsers and extensions.
- Compliance is provable from server logs alone.
What it can't do alone
- Third-party scripts you embed (ad pixels, chat widgets) still load client-side — gating them requires either not embedding them or proxying them server-side (the "server-side tagging" approach used by tools like GTM server containers).
- The banner itself still needs to render in the browser.
5. Client-side consent: how to build it properly
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:
- Block by default. Tag manager triggers must wait for a consent event — never "page view." The most common fine trigger is a tag firing between load and click.
- Equal buttons. "Accept all" and "Reject all" must be equally prominent, same size, same color contrast. Pre-ticked boxes are invalid consent.
- Verify after reject. Click reject, reload, and check
Application → Cookiesin devtools. If_ga,_fbpor similar appear anyway, your CMP isn't actually gating anything. - Store the proof. Timestamp, banner version, and choices — you may need to show consent was obtained.
- Honor withdrawal. A visible link that reopens the banner, and deletion of the tracking cookies on reject.
Open-source options (Klaro!, Tarteaucitron, Orejime) make all of this achievable without licensing fees — which is exactly what our scanner's fix guidance recommends.
6. The hybrid pattern most teams end up with
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.
7. How to choose
| 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 |
8. Test your current setup
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 editable WordPress document starters and an HTML report from the latest scan, see EUComply Pro ($79/year per website). Runtime PDF reports and hosted monitoring are not included today.
Further reading
- EU cookie consent guide 2026
- GDPR cookie banner fines: what triggers them
- The EU website compliance checklist for 2026