How to add a cookie consent banner to a Hugo site
In Hugo, add the snippet to the head of your base template, layouts/baseof.html (layouts/_default/baseof.html before Hugo 0.146), or to the head partial your theme uses, copied into your project's layouts/_partials/ directory (layouts/partials/ on older versions) so it overrides the theme's file. Keep it above the theme's own analytics template.Steps
Where to paste the snippet in Hugo
Hugo builds every page from a base template that prints the <html>, <head> and <body> shell. Since Hugo 0.146.0 the base template lives at layouts/baseof.html and partials at layouts/_partials/; older projects and themes still use layouts/_default/baseof.html and layouts/partials/, which Hugo maps to the new layout. Most themes keep the head in a partial called from the base template, for example {{ partial "head.html" . }}.
The rule that makes this safe: when the project and the theme have a file at the same path, the project's file wins. So:
- Find the theme's head partial (
themes/<theme>/layouts/_partials/head.htmlorthemes/<theme>/layouts/partials/head.html) or its base template. - Copy it to the same relative path in your project's
layouts/directory. - Add the snippet as the first script, right after the charset meta tag:
html<head><meta charset="utf-8">{{ with site.Params.cookiequiet.siteKey }}<script src="https://cookiequiet.com/cq.js" data-site="{{ . }}" data-consent-mode="1" data-auto-block="1" defer></script>{{ end }}...</head>
toml# hugo.toml[params.cookiequiet]siteKey = "YOUR_SITE_KEY"
The with block lets you leave the key empty in a local configuration so development builds carry no banner. Some themes provide an empty partial meant for custom head code (PaperMod's extend_head.html, for instance): using it avoids copying the whole head partial, as long as the theme includes it before its own analytics.
How to handle the tags your theme prints
Google Analytics. Many themes print the Google tag when services.googleAnalytics.id is set, often through Hugo's embedded Google Analytics template. That template does not set Consent Mode defaults. Two options, both described in the Google Analytics 4 guide:
- remove the ID from the configuration and paste a marked copy of the Google tag below the snippet (
type="text/plain" data-cq-category="analytics"on both script tags), so nothing loads before consent; - or keep the theme's tag and add Google's default consent block as an inline script between the snippet and the tag, so it runs in Consent Mode with analytics_storage denied until consent.
Meta Pixel, chat widgets, comments. Paste them in the same partial, marked with the right category (marketing for the pixel, functional for a chat widget). Disqus and similar comment systems load their script with JavaScript; the auto-blocker holds the known ones until consent, and a marked wrapper makes it explicit.
YouTube, Vimeo, X embeds. Hugo's embedded shortcodes print iframes in the HTML. Hugo's privacy configuration helps: privacy.youTube.privacyEnhanced = true switches the YouTube shortcode to youtube-nocookie.com, and privacy.vimeo.enableDNT and privacy.x.simple reduce what the others send. To make a player wait for consent instead, override the shortcode in your project (layouts/_shortcodes/youtube.html, or layouts/shortcodes/ on older versions) and print data-cq-src instead of src; Cookiequiet loads the iframe once marketing is granted. The YouTube embeds guide shows the markup.
How to add the Cookie preferences link
In the footer partial you copied into the project:
html<a href="#cq-preferences" data-cq-show-preferences>Cookie preferences</a>
How to test
- Run
hugo serverand open the site in a private window. - Click Reject all: the cookies are
cq_consentonly, no_ga, no_fbp. - Clear the cookies, click Accept all, reload: the marked tags run.
- Run
hugoand check in the built HTML (public/index.html) thatcq.jsis the first script in the head. - Check the consent log in the dashboard.
What is specific to Hugo
- Theme updates. Because your copy of the partial lives in the project, updating the theme (git submodule or Hugo module) does not remove the snippet; compare the theme's new partial with yours from time to time.
- Hugo Modules follow the same override rule as themes.
- Static hosting. The built site works on Netlify, GitHub Pages, Cloudflare Pages or any web server; the banner needs no server. See cookie banners for static sites.
- Multilingual sites. One snippet serves every language; the banner's language follows the visitor's browser.