Astro cookie consent comes down to one tag: paste the consent banner script at the top of <head> in your base layout (usually src/layouts/Layout.astro), marked is:inline so Astro leaves it exactly where you put it. The banner then holds back analytics and marketing scripts until the visitor chooses. A plain Astro build sets no cookies of its own, so the work starts the day you add Google Analytics, a pixel or an embed.
<head>
<script is:inline src="https://cdn.kukie.io/s/YOUR_SITE_KEY/c.js" async></script>
<meta charset="utf-8" />
<!-- the rest of your head -->
</head>
Why Astro sites still need cookie consent
Astro generates static HTML by default and ships zero client-side JavaScript unless you explicitly opt in. That architectural choice earns strong performance scores, yet it does not remove the obligation to obtain valid cookie consent before setting non-essential cookies.
The moment you add Google Analytics, a Meta Pixel, a live chat widget, or any third-party embed, your Astro site starts dropping cookies such as _ga, _fbp, and _hjSessionUser into visitor browsers. Under Article 5(3) of the ePrivacy Directive and the GDPR's consent requirements, each of those cookies requires prior, informed, and freely given consent from the visitor, regardless of which framework generated the page.
A perfect Lighthouse score says nothing about consent: performance audits never check whether _ga was set before the visitor answered the banner.
Astro bundles scripts unless you tell it not to
Astro processes <script> tags in .astro files by bundling and optimising them through Vite. The output is a hashed, deferred JavaScript file loaded once across the site. This is efficient, but it means analytics snippets placed inside a standard <script> block will execute automatically on every page load, before a visitor has a chance to give or refuse consent.
Two directives change that behaviour. The is:inline directive tells Astro to leave a script tag untouched in the final HTML, skipping the Vite pipeline entirely. The define:vars directive passes server-side variables into a script and implicitly applies is:inline. Both are relevant when you need precise control over when and whether a tracking script runs.
Astro's islands architecture hydrates interactive components independently using client directives such as client:load, client:idle, and client:visible. A cookie banner component, for example, can be hydrated with client:load so it appears immediately, while a non-essential analytics component could be loaded only after consent is confirmed.
Cookies you will typically find on an Astro site
A freshly built Astro project with no integrations sets no cookies at all. The cookies appear once you add third-party services. Below is a summary of common cookies grouped by category.
| Category | Example cookies | Source | Consent required |
|---|---|---|---|
| Strictly necessary | __cf_bm | Cloudflare bot management, if your site sits behind Cloudflare | No |
| Analytics | _ga, _ga_*, _hjSession | Google Analytics 4, Hotjar | Yes |
| Marketing | _fbp, _gcl_au, li_fat_id | Meta Pixel, Google Ads, LinkedIn | Yes |
| Functional | Theme or language preference | Your own code, often in localStorage | Usually not, when the visitor set it |
Running a cookie scan on your live Astro site is the fastest way to get a definitive list. Scanners detect cookies set by third-party scripts, iframes, and embedded content that you might not realise are present.
Installing a cookie banner on an Astro site
The recommended approach is to place the banner script in your site's <head> element, inside the base layout file that wraps every page. In most Astro projects, this is src/layouts/Layout.astro or src/layouts/BaseLayout.astro.
Add the script tag with the is:inline directive so Astro leaves it as written instead of bundling it. Astro's documentation notes that any attribute on a script, async included, already implies is:inline, but writing it out makes the intent clear to the next person who edits the layout. Paste the tag exactly as the installation guide gives it, async included, at the top of <head>, so it loads ahead of every analytics and marketing script.
For a detailed walkthrough with code examples, see the Astro installation guide in the Help Centre.
Handling view transitions
Astro's View Transitions API swaps page content without a full reload. Astro 5 renamed the router component from <ViewTransitions /> to <ClientRouter />, as the Astro 5 upgrade guide records. With it switched on, keep the banner tag in the shared layout so every page has it, then click through a few pages and confirm the banner and its revisit button stay in place.
Astro's view transitions guide warns that inline scripts can re-execute when a visitor returns to a page, and that some scripts stop re-running after client-side navigation. Code that must run on every page, such as sending a page view to analytics after consent, belongs in an astro:page-load listener rather than DOMContentLoaded.
Blocking third-party scripts until consent
A cookie banner alone is not enough. You must prevent non-essential cookies from being set before the visitor makes a choice. There are two practical strategies for Astro sites.
Strategy 1: script type swapping
Change the type attribute of analytics and marketing scripts from text/javascript to text/plain and name the category the script belongs to. The browser ignores scripts with an unrecognised MIME type. When the visitor grants consent for that category, the consent management platform releases the script and it executes. With Kukie.io, the category goes in a data-cc-category attribute:
<script is:inline type="text/plain" data-cc-category="analytics"
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX"></script>
This technique works well with is:inline scripts. Because Astro does not process inline scripts through Vite, the type="text/plain" attribute remains intact in the output HTML.
Strategy 2: auto-blocking with a CMP
Some consent management platforms offer automatic script blocking. The CMP's script loads first and intercepts all subsequent script requests, holding back anything classified as analytics or marketing until consent is recorded. This removes the need to manually tag every third-party snippet.
Google Consent Mode in an Astro layout
If you use Google Analytics 4 or Google Ads, Google Consent Mode v2 is worth configuring. Consent Mode sends consent signals to Google's tags so they adjust their behaviour, collecting no identifying data when consent is denied, but still providing modelled conversion data.
In an Astro project, the Consent Mode default command should appear before the Google tag snippet. Both should use is:inline to prevent Astro from reordering or bundling them. The consent management platform then updates the consent state when the visitor interacts with the banner.
A working setup pairs the CMP script, the Consent Mode defaults and the gtag.js snippet, all as inline scripts in the <head> of your base layout. A CMP with a Consent Mode integration sends the updates for you.
Common mistakes when adding consent to Astro
Loading the banner from a bundled <script> block (without is:inline) lets Astro move it into a deferred module, which means tracking scripts may fire before the banner loads. Always use is:inline for consent-critical scripts.
Forgetting to block Google Analytics during the default consent state is another frequent error. Loading gtag.js sets cookies immediately unless Consent Mode defaults are configured to deny analytics and ad storage.
Neglecting iframe embeds is a third pitfall. YouTube videos, Google Maps, and social media embeds all set cookies through iframes. These need placeholder elements that load the real iframe only after consent.
Relying on client:visible to delay analytics components does not constitute consent management. Visibility-based hydration controls performance, not privacy. A visitor scrolling to an analytics component has not consented to tracking merely by seeing it.
Which regulations apply to your Astro site
The legal requirements depend on where your visitors are located, not where your server is hosted.
| Regulation | Region | Consent model | Key requirement |
|---|---|---|---|
| GDPR | EU/EEA | Opt-in | Prior consent before non-essential cookies |
| UK GDPR and PECR | United Kingdom | Opt-in | Clear, affirmative action required |
| CCPA and CPRA | California | Opt-out | "Do Not Sell or Share" link required |
| LGPD | Brazil | Opt-in | Consent must be free, informed, unambiguous |
| PIPEDA | Canada | Implied or express | Meaningful consent with clear purposes |
| POPIA | South Africa | Opt-in | Consent must be voluntary and specific |
Geo-detection features offered by consent management platforms apply the correct consent model based on the visitor's location. A visitor from Germany sees an opt-in banner, while a visitor from California sees a "Do Not Sell" toggle. Configuring region rules avoids showing unnecessary prompts to visitors in jurisdictions with no cookie law.
Checklist for consent on an Astro project
Use this as a quick reference when setting up or auditing your Astro site.
Run a cookie scan to identify every cookie your site sets
Place the CMP script at the top of
<head>in your base layout withis:inlineBlock all non-essential scripts until consent is granted (type-swap or auto-block)
Configure Consent Mode v2 defaults if using Google tags
Add iframe placeholders for embedded content (YouTube, maps, social)
Set up geo-detection to apply the right consent model per region
Generate a cookie policy and link it from the banner
Test with Chrome DevTools to confirm no cookies appear before consent
Verify the banner works with
<ClientRouter />enabled
Astro consent questions
Does a static Astro site need cookie consent?
Yes. If your Astro site loads any third-party script that sets non-essential cookies, such as Google Analytics, a Meta Pixel or Hotjar, you are required to obtain consent under the GDPR, ePrivacy Directive, and similar regulations worldwide.
How do I stop Google Analytics cookies loading before consent in Astro?
Use the is:inline directive on your analytics script, set type="text/plain" and add data-cc-category="analytics" so the browser does not execute it until consent. Alternatively, configure Google Consent Mode v2 defaults to deny analytics storage, or use a CMP with automatic script blocking.
Does Astro's islands architecture affect cookie compliance?
Islands architecture controls when components hydrate on the client, which is a performance feature. It does not prevent cookies from being set by third-party scripts loaded in the head or through iframes. You still need a consent mechanism independent of hydration.
Should I use is:inline for the cookie banner script in Astro?
Yes. Use is:inline so Astro leaves the tag as written, and paste it at the top of the head section exactly as the installation guide gives it, async attribute included. Being first in the head is what lets it run ahead of the scripts it blocks.
Can I use client:load for a cookie consent component in Astro?
You can hydrate a consent UI component with client:load, but the script-blocking logic must still run before any tracking code. A CMP snippet in the head handles blocking, while a hydrated component can manage the visual banner and user interaction.
Do I need separate cookie banners for EU and US visitors on my Astro site?
Not separate banners, but different consent models. The GDPR requires opt-in consent, while the CCPA requires an opt-out mechanism. A CMP with geo-detection applies the correct model per visitor location automatically.
Find out what your Astro build sets before you add the banner
If you are not sure which cookies your Astro site sets, start with a scan of the live build. Kukie.io lists the cookies it finds, sorts them into categories and holds the non-essential ones until visitors choose. The Free plan covers up to 5 websites.
