Backend frameworks · 5 min setup
How to add analytics to a Django site
<head> of your base.html template, with the site id coming from settings through a small context processor so it's only output where it's configured. If you use Django 6's built-in Content-Security-Policy, add visitrack.app to script-src and connect-src.Updated
Every template that extends base.html gets the tag, and nothing changes in your views. Django's admin uses its own templates, so staff activity in /admin/ is never counted.
Django at a glance
- Where the tag goes
templates/base.html, inside<head>- Config
VISITRACK_SITE_IDsetting + context processor- CSP
- Django 6
SECURE_CSP, or django-csp on older versions - htmx
hx-boost/hx-push-urlnavigations tracked- Server events
- POST
/api/trackfrom views or signals
How to install VisitTrack on Django
Step 1: Add the settings
Read the site id and a secret API key (VisitTrack Settings → API / MCP) from the environment. Leave
VISITRACK_SITE_IDempty in development and the tag isn't rendered.settings.py# settings.py import os VISITRACK_SITE_ID = os.environ.get("VISITRACK_SITE_ID", "") VISITRACK_API_KEY = os.environ.get("VISITRACK_API_KEY", "") # server-side onlyStep 2: Expose the site id to templates
A context processor makes
VISITRACK_SITE_IDavailable in every template. Register it inTEMPLATES[0]["OPTIONS"]["context_processors"]as"myproject.context_processors.visitrack".myproject/context_processors.py# myproject/context_processors.py from django.conf import settings def visitrack(request): return {"VISITRACK_SITE_ID": settings.VISITRACK_SITE_ID}Step 3: Add the tag to base.html
Inside
<head>. Django auto-escapes the value.templates/base.html<head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{% block title %}My site{% endblock %}</title> {% if VISITRACK_SITE_ID %} <script defer data-site="{{ VISITRACK_SITE_ID }}" src="https://visitrack.app/tracker.js"></script> {% endif %} </head>Step 4: Allow it in your Content-Security-Policy
Django 6.0 added CSP support in core (
ContentSecurityPolicyMiddlewareandSECURE_CSP). On older versions the same sources go into django-csp's settings.settings.py# settings.py (Django 6.0+, with ContentSecurityPolicyMiddleware enabled) from django.utils.csp import CSP SECURE_CSP = { "default-src": [CSP.SELF], "script-src": [CSP.SELF, "https://visitrack.app"], "connect-src": [CSP.SELF, "https://visitrack.app"], }
How to check VisitTrack is working on Django
- Set
VISITRACK_SITE_IDin production, deploy, and view source — the tag is in<head>. - Browse two pages; the VisitTrack live view shows you and the Pages tab lists both URLs.
- If nothing arrives, the browser console shows any CSP violation naming
visitrack.app.
Does VisitTrack track Django page navigation?
Server-rendered Django pages are full page loads — one pageview each. With htmx, boosted links (hx-boost="true") and requests with hx-push-url update the address bar through the History API, which the tracker follows, so those count as pageviews too. Partial swaps that don't push a URL aren't pageviews.
Turbo (via django-turbo or Hotwire) behaves the same way: Turbo Drive visits push a URL and are counted, and the tag in <head> is loaded once.
How to track custom events and goals in Django
Send signups from the view that creates the account. In standard mode the tracker keeps the visitor id in a first-party _vt_vid cookie on your domain, so the request already carries it.
# accounts/visitrack.py
import httpx
from django.conf import settings
def send_event(request, name, props=None):
visitor_id = request.COOKIES.get("_vt_vid")
if not (visitor_id and settings.VISITRACK_API_KEY):
return
try:
httpx.post(
"https://visitrack.app/api/track",
headers={"Authorization": f"Bearer {settings.VISITRACK_API_KEY}"},
json={"type": "event", "visitorId": visitor_id, "name": name, "props": props or {}},
timeout=3,
)
except httpx.HTTPError:
pass # analytics must never break signup
# in your signup view, after user.save():
# send_event(request, "signup", {"method": "email"})Using django-allauth? Its user_signed_up signal passes the request, so you can call send_event from a receiver. To link the visitor to your user id and traits, send a type: "identify" call the same way — see People & identify. In templates, data-vt-goal and data-vt-scroll work on any element.
Revenue attribution on Django
If you create Stripe Checkout Sessions in a view, pass the same cookie value as client_reference_id, then connect Stripe in VisitTrack's Settings → Revenue. Each payment is credited to the visitor's first referrer, UTM campaign and landing page. Details: Stripe revenue attribution.
session = stripe.checkout.Session.create(
mode="subscription",
line_items=[{"price": price_id, "quantity": 1}],
success_url=request.build_absolute_uri("/thanks/") + "?session_id={CHECKOUT_SESSION_ID}",
cancel_url=request.build_absolute_uri("/pricing/"),
client_reference_id=request.COOKIES.get("_vt_vid"),
)Track AI crawlers in Django
AI crawlers don't run JavaScript, so they're invisible to the browser tag. This middleware (from the AI crawler tracking docs) reports every request with a short timeout; add it to MIDDLEWARE. Make sure robots.txt and sitemap.xml are served through Django (or report them at the layer that serves them), since crawlers request those first.
import httpx
from django.conf import settings
def crawler_tracking_middleware(get_response):
def middleware(request):
try:
httpx.post("https://visitrack.app/api/collect-bot", json={
"siteId": settings.VISITRACK_SITE_ID,
"path": request.path,
"userAgent": request.headers.get("user-agent"),
"ip": request.META.get("HTTP_X_FORWARDED_FOR", "").split(",")[0].strip(),
}, timeout=2)
except Exception:
pass
return get_response(request)
return middlewareScript options you might need on Django
All optional — add them to the same tag. Full reference: script configuration.
| Attribute | What it does |
|---|---|
data-cookieless | Store nothing in the browser; the server derives a daily-rotating id. See cookieless mode. |
data-exclude="/app/*" | Never track these paths (comma-separated, * wildcard). Checked on every navigation. |
data-allow-local | Track on localhost and *.local — for testing an install only. |
data-debug | Log every event the script sends, and why it skipped one, to the console. |
data-auto="false" | Turn off automatic events (downloads, contact clicks, form submits, rage clicks, JS errors, 404s). |
Django troubleshooting
The tag doesn't render
The context processor isn't registered, or VISITRACK_SITE_ID is empty in that environment. Check TEMPLATES → OPTIONS → context_processors.
CSP blocks the script or its requests
Both directives need the origin: script-src for the script, connect-src for its POST to /api/collect.
request.COOKIES has no _vt_vid
The site is in cookieless mode (no cookie is ever written), the tracker was blocked, or the request came from a different root domain. In cookieless mode, use your own user id as visitorId.
Admin pages missing from analytics
Intended — the admin doesn't extend your base.html. Leave it that way.
Django analytics FAQ
Where do I put an analytics script in Django?
In the <head> of base.html, the template your pages extend. A context processor that exposes the site id from settings keeps it configurable per environment.
How do I allow VisitTrack in Django's Content-Security-Policy?
Add https://visitrack.app to script-src and connect-src. On Django 6.0+ that's the built-in SECURE_CSP setting with ContentSecurityPolicyMiddleware; on older versions, django-csp's settings.
Does VisitTrack track htmx navigation in Django?
Yes, whenever htmx pushes a new URL (hx-boost or hx-push-url). The tracker follows History API changes; swaps that don't change the URL aren't pageviews.
How do I send Django signups to VisitTrack?
POST a signup event to /api/track from the signup view or an allauth signal, using the _vt_vid cookie as visitorId and a secret API key. See server-side events.
Can VisitTrack attribute Stripe payments in a Django app?
Yes. Set client_reference_id to the visitor id when creating the Checkout Session and connect Stripe in Settings → Revenue.
Keep going
- Laravel analyticsAdd VisitTrack to Laravel in your Blade layout with @production, send signups from a Registered listener, and attribute Cashier Stripe Checkout revenue.
- Ruby on Rails analyticsAdd VisitTrack to Rails in application.html.erb — Turbo Drive visits tracked — with CSP initializer settings, Devise signups and Stripe Checkout revenue.
- Next.js analyticsAdd VisitTrack to a Next.js App Router or Pages Router app with next/script — client-side route changes are tracked automatically.
- Netlify analyticsAdd VisitTrack on Netlify with Snippet injection (no code) or in your framework, keep deploy previews out, and set CSP in _headers.
- Install the tracking scriptThe reference tag, CSP notes and how to check it's working.
- Custom events and signupsvisitrack(), data-vt-goal, scroll events and server-side signups.
- Use casesHow SaaS teams, indie hackers, stores and agencies use VisitTrack.
- VisitTrack vs Google AnalyticsCookie-free analytics with revenue attribution, compared honestly.
Add VisitTrack to your Django site
Cookie-free analytics with revenue attribution, live visitors, funnels and session replays. One script tag, every feature on every plan. 14 days free, no card required.
Not on Django? See all 29 integration guides.