Platforms

How to add a cookie consent banner to a Django site

In Django, paste the snippet in the head of the base template every page extends (usually templates/base.html), above any block where child templates add scripts. Pass the site key from settings with a small context processor, mark tracking tags in the same template with type="text/plain" and data-cq-category, and list Django's sessionid and csrftoken cookies as strictly necessary.
Published 21 October 2026 · By the Cookiequiet team · Markdown

Steps

Copy the snippetIn the Cookiequiet dashboard, add the website and copy the snippet with your site key, data-consent-mode="1" and data-auto-block="1".
Add the key to settingsAdd COOKIEQUIET_SITE to settings.py, read from an environment variable, and a context processor that passes it to every template.
Paste the snippet in base.htmlOpen the base template that child templates name with extends and paste the snippet in the head, above any block where pages add their own scripts.
Mark your tagsPaste analytics and advertising tags below the snippet with type="text/plain" and data-cq-category set to analytics or marketing.
Add the CSP nonce if you use oneWith Django's built-in Content Security Policy, allow https://cookiequiet.com in script-src and connect-src, and add nonce="{{ csp_nonce }}" to inline scripts, marked ones included.
Add the Cookie preferences linkIn the footer of base.html, add a plain anchor with href="#cq-preferences" and the data-cq-show-preferences attribute.
Test with Reject allOpen the site in a private window, click Reject all and check that only cq_consent, sessionid and csrftoken can be set. Accept all, reload and check the consent log.

Where does the snippet go in Django?

Django's template inheritance builds every page from a base "skeleton" template, which child templates name with {% extends "base.html" %} and fill through {% block %} tags. The snippet belongs in the head of that base template, above any block where pages add scripts:

django
{# templates/base.html #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cookiequiet.com/cq.js" data-site="{{ COOKIEQUIET_SITE }}" data-consent-mode="1" data-auto-block="1" defer></script>
<title>{% block title %}My site{% endblock %}</title>
{% block extra_head %}{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>

Every view that renders a template extending base.html now loads the banner, Wagtail pages included, since Wagtail projects use the same template inheritance.

Pass the key from settings. A context processor adds a variable to every template rendered with a request:

python
# settings.py
COOKIEQUIET_SITE = os.environ.get("COOKIEQUIET_SITE", "")
​
# mysite/context_processors.py
from django.conf import settings
​
def cookiequiet(request):
return {"COOKIEQUIET_SITE": settings.COOKIEQUIET_SITE}

Then add "mysite.context_processors.cookiequiet" to the context_processors list in TEMPLATES. Writing the key directly in the template works too: it is public, it appears in every page.

Paste tracking tags under the snippet and change their opening tag:

html
<script type="text/plain" data-cq-category="analytics" src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script type="text/plain" data-cq-category="analytics">
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXX');
</script>

Cookiequiet runs marked tags when the category is granted, and at once on later visits where the stored choice includes it. Tags that a page adds in {% block extra_head %} work the same way when the block sits below the snippet. The Google Analytics 4 guide explains the Consent Mode alternative, and the guide on blocking scripts covers embeds and other tags. Scripts created at runtime by JavaScript are held by the auto-blocker when they come from one of the about 80 trackers it knows.

Django admin. The admin uses its own templates and does not extend your base.html, so it gets no banner. It is used by your staff, not by visitors, and sets only Django's own cookies.

What about Django's own cookies?

Django sets sessionid when the session is used (for example after a login) and csrftoken for cross-site request forgery protection on pages with forms. Both serve a function the visitor asked for, so they are strictly necessary and need no consent in most readings of the ePrivacy rules. List them in the cookie table under necessary; the banner does not block them. The cookie scanner finds them along with any third-party cookies.

How does it work with Django's Content Security Policy?

Django 6.0 added built-in Content Security Policy support: a middleware sends the header, the SECURE_CSP setting holds the policy, and the django.template.context_processors.csp context processor exposes a csp_nonce variable. Add https://cookiequiet.com to script-src and connect-src (the banner fetches its configuration, the geo lookup and the consent records from that origin), and give inline scripts nonce="{{ csp_nonce }}". Marked inline scripts take it too (<script type="text/plain" data-cq-category="analytics" nonce="{{ csp_nonce }}">): Cookiequiet copies the nonce to the script it runs after consent, so the policy lets it through.

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

How do I test it?

  1. Run the development server, or deploy, and view the page source: cq.js is the first script in the head.
  2. Open the site in a private window and click Reject all. The cookies are cq_consent and, on pages with forms or after a login, csrftoken and sessionid.
  3. Clear the cookies, click Accept all, reload: the marked tags run and _ga appears.
  4. Check the consent log in the dashboard.

Setup is one tag in one template, with no Django package to install. Cookiequiet is free for one website with geo-targeting; Starter is €3 per website per month with no pageview limit.

Questions

No. The banner is one script tag in base.html. A context processor of a few lines can pass the site key from settings, but nothing needs to be installed with pip.
They keep a logged-in session and protect forms against cross-site request forgery, which makes them strictly necessary in most readings of the ePrivacy rules. List them as necessary in the cookie table; the banner does not block them.
Not unless you add it. The admin uses its own templates, which do not extend your base.html. It is a staff tool, so it usually does not need one.
Allow the cookiequiet.com origin in script-src and connect-src in SECURE_CSP, add the csp context processor, and put nonce="{{ csp_nonce }}" on inline scripts, marked ones included. Built-in CSP support arrived in Django 6.0.

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.