Platforms

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.
Published 20 October 2026 · By the Cookiequiet team · Markdown

Steps

Find where your theme prints the headLook in your theme for the base template (layouts/baseof.html, or layouts/_default/baseof.html on versions before 0.146) and the partial it calls inside the head, often head.html in layouts/_partials/ (layouts/partials/ before 0.146).
Copy the file into your projectCopy that file to the same relative path under your project's layouts/ directory. Hugo uses the project's file when both exist, so the theme can be updated without losing your change.
Add the snippet as the first scriptIn the copied file, add the Cookiequiet snippet right after the charset meta tag, above the theme's analytics or other scripts. You can read the site key from site.Params to keep it out of templates.
Handle the Google tagIf your theme or Hugo's embedded Google Analytics template prints the Google tag, either replace it with a marked copy below the snippet (type="text/plain", data-cq-category="analytics") or add Google's default consent block above it to run it in Consent Mode.
Handle the YouTube shortcodeSet privacy.youTube.privacyEnhanced to true in your site configuration so the shortcode uses youtube-nocookie.com, or override the shortcode in layouts/_shortcodes/youtube.html to print data-cq-src instead of src.
Add the Cookie preferences linkIn the footer partial, add a plain anchor with href="#cq-preferences" and the data-cq-show-preferences attribute.
Build and testRun hugo server, open the site in a private window, click Reject all and check that no _ga or _fbp cookie is set. Accept all, reload, and check the consent log.

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:

  1. Find the theme's head partial (themes/<theme>/layouts/_partials/head.html or themes/<theme>/layouts/partials/head.html) or its base template.
  2. Copy it to the same relative path in your project's layouts/ directory.
  3. 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.

In the footer partial you copied into the project:

html
<a href="#cq-preferences" data-cq-show-preferences>Cookie preferences</a>

How to test

  1. Run hugo server and open the site in a private window.
  2. Click Reject all: the cookies are cq_consent only, no _ga, no _fbp.
  3. Clear the cookies, click Accept all, reload: the marked tags run.
  4. Run hugo and check in the built HTML (public/index.html) that cq.js is the first script in the head.
  5. 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.

Questions

Copy the one your theme actually has to the same path in your project. Hugo 0.146.0 moved _default files to the layouts root and partials to _partials, and maps old paths to new ones, so both layouts work until you migrate the theme.
Either remove the services.googleAnalytics.id setting and paste a marked copy of the Google tag below the snippet, or add Google's default consent block above the theme's tag so it runs in Consent Mode with analytics_storage denied. Cookiequiet sends the update after the visitor's choice.
The default shortcode embeds a youtube.com player that sets cookies. Set privacy.youTube.privacyEnhanced to true for youtube-nocookie.com, or override the shortcode to print data-cq-src so the player waits for the marketing category.
Yes. The banner is one client script served from cookiequiet.com; the built site stays fully static. The consent log and geo-targeting are handled by Cookiequiet's servers.

Put a quiet banner on your website today

Free for one website with every core feature, no card and no trial clock. Upgrade a website to Starter for $3 a month to match your brand with AI and drop the branding, or to Pro for $9 to design it with the AI agent.