How to make your vibe-coded site GDPR compliant

You asked an AI to build you a site, it shipped fast, and it probably added Google Analytics, a Vercel/Plausible snippet, or an embedded widget somewhere along the way — because that’s what sites do. What it almost certainly didn’t add is real consent for any of that. If you have EU (or UK, or a bunch of other) visitors, that’s a compliance gap, not a nitpick.
NOTE! This isn’t legal advice — your privacy policy and what you actually track are still your call. If you want to be fully compliant, refer to GDPR checklist
This is the mechanical part: how to stop non-essential scripts from running before a visitor agrees to them, using astro-cookie-consent.
Why the banner your AI generated probably doesn’t count
Section titled “Why the banner your AI generated probably doesn’t count”Ask most AI coding assistants for a “cookie consent banner” and you’ll get a <div> with some
text and an “OK” button that just hides itself on click. That’s not consent — it’s a notice. The
analytics script was already sitting in your <head>, already running, before the visitor read a
word of it. Under GDPR/ePrivacy, that’s exactly backwards: non-essential cookies are supposed to
load after affirmative consent, not before.
The other common failure mode is a banner that technically gates something, but stacks the deck — a bold “Accept” button next to a greyed-out “Decline” link, or boxes that are pre-ticked to “yes.” Regulators (CNIL, ICO, EDPB) specifically call these out as dark patterns. A banner that nudges isn’t a compliant banner, it’s a compliant-looking banner.
What actually needs to be true
Section titled “What actually needs to be true”Stripped of legalese, the mechanics regulators check for come down to four things:
- Nothing non-essential runs before consent. Analytics, ads, embeds — blocked until the visitor says yes.
- Accept and decline are equally easy. Same visual weight, same number of clicks.
- Opt-in, not opt-out. Every category defaults to off. A visitor who closes the tab without choosing anything has not consented to anything.
- You can show your work. If asked, you should be able to point to when and what a given visitor consented to.
That’s the whole list. It’s a mechanism problem, not a writing problem — so let’s fix it with code.
Installing the mechanism for Astro sites
Section titled “Installing the mechanism for Astro sites”npm install astro-cookie-consentDrop the banner into your layout, once:
---import ConsentBanner from 'astro-cookie-consent/ConsentBanner.astro';---
<html> <body> <slot /> <ConsentBanner privacyHref="/privacy" /> </body></html>That alone gets you equal-weight Accept/Decline buttons, everything off by default, and a banner that reappears after consent expires (365 days by default) — see why those defaults are the way they are. None of it needed a config file or a line of CSS.
Actually gating the scripts
Section titled “Actually gating the scripts”The banner is only half of it — the other half is making sure trackers genuinely don’t load until
consent is given. That’s what gateScript() is for:
<script> import { gateScript } from 'astro-cookie-consent';
gateScript({ src: 'https://www.googletagmanager.com/gtag/js?id=YOUR-ID' });</script>gateScript() won’t inject that <script src> until the visitor accepts, and it only ever
injects it once. If your AI-generated site has a <script src="..."> sitting directly in
Layout.astro or a page <head> right now, that’s the line to go find and wrap.
If you’re tracking more than one thing
Section titled “If you’re tracking more than one thing”A single accept/decline is enough for a lot of sites, but if you’re running analytics and
marketing pixels, GDPR expects visitors to be able to consent to one without the other. Pass
categories and check them individually before gating:
<ConsentBanner categories={[ { id: 'analytics', label: 'Analytics', description: 'Helps us understand how this site is used.' }, { id: 'marketing', label: 'Marketing', description: 'Used for ad retargeting.' }, ]}/><script> import { getConsent, gateScript } from 'astro-cookie-consent';
if (getConsent('analytics')) { gateScript({ src: 'https://www.googletagmanager.com/gtag/js?id=YOUR-ID' }); }</script>Keeping a record
Section titled “Keeping a record”If you ever need to demonstrate that consent was actually given — Art. 7(1) puts that burden on
you, not the visitor — every choice is stored with a timestamp and retrievable with
getConsentRecord(). No extra setup, no server, no database.
That’s it
Section titled “That’s it”No config file, no cookie-consent-as-a-service subscription, no CSS to fight. If you vibe-coded the rest of the site, this is the one part worth doing on purpose:
- Installation & Quick Start — the full setup, including per-category consent and theming
- Live Demo — the banner with a status readout showing exactly what gets stored
- What GDPR compliance does and doesn’t cover — the mechanics this library handles, and the parts that are still on you