# 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.

Source: https://cookiequiet.com/platforms/hugo
Published: 2026-10-20
Publisher: Cookiequiet

## Steps

1. **Find where your theme prints the head** Look 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).
2. **Copy the file into your project** Copy 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.
3. **Add the snippet as the first script** In 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.
4. **Handle the Google tag** If 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.
5. **Handle the YouTube shortcode** Set 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.
6. **Add the Cookie preferences link** In the footer partial, add a plain anchor with href="#cq-preferences" and the data-cq-show-preferences attribute.
7. **Build and test** Run 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](/platforms/google-analytics-4):

- 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](/guides/youtube-embeds-and-consent) 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

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](/blog/cookie-banner-for-a-static-site).
- **Multilingual sites.** One snippet serves every language; the banner's language follows the visitor's browser.

## Questions

### My theme has layouts/_default/baseof.html, not layouts/baseof.html. Which do I edit?

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.

### How do I keep Google Analytics printed by my theme from setting cookies before consent?

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.

### Does the YouTube shortcode need consent?

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.

### Can I use Cookiequiet on GitHub Pages or Netlify?

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.

## Sources

- [Hugo: Base templates and blocks](https://gohugo.io/templates/base/)
- [Hugo: Directory structure](https://gohugo.io/getting-started/directory-structure/)
- [Hugo: New template system (v0.146.0)](https://gohugo.io/templates/new-templatesystem-overview/)
- [Hugo: Configure privacy](https://gohugo.io/configuration/privacy/)
