Backend frameworks · 5 min setup

How to add analytics to a Django site

To add VisitTrack to Django, render the script tag in the <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_ID setting + context processor
CSP
Django 6 SECURE_CSP, or django-csp on older versions
htmx
hx-boost / hx-push-url navigations tracked
Server events
POST /api/track from views or signals

How to install VisitTrack on Django

  1. Step 1: Add the settings

    Read the site id and a secret API key (VisitTrack Settings → API / MCP) from the environment. Leave VISITRACK_SITE_ID empty 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 only
  2. Step 2: Expose the site id to templates

    A context processor makes VISITRACK_SITE_ID available in every template. Register it in TEMPLATES[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}
  3. 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>
  4. Step 4: Allow it in your Content-Security-Policy

    Django 6.0 added CSP support in core (ContentSecurityPolicyMiddleware and SECURE_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

  1. Set VISITRACK_SITE_ID in production, deploy, and view source — the tag is in <head>.
  2. Browse two pages; the VisitTrack live view shows you and the Pages tab lists both URLs.
  3. If nothing arrives, the browser console shows any CSP violation naming visitrack.app.

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

billing/views.py
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.

myproject/middleware.py
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 middleware

Script options you might need on Django

All optional — add them to the same tag. Full reference: script configuration.

AttributeWhat it does
data-cookielessStore 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-localTrack on localhost and *.local — for testing an install only.
data-debugLog 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

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.