Bug tracking for Django — session replay, console + HAR (2026)
Django bug-reporting guide for QA + dev teams: framework-specific gotchas, common bugs, and how to capture them with BugMojo session replay.
What Django teams ship with BugMojo
Django answers an unsafe request with HTTP 403 Forbidden the moment CsrfViewMiddleware rejects it, and on an htmx app that 403 is easy to misread. A server-rendered form carries the token through the {% csrf_token %} tag, which injects a per-response masked value that also defends against the BREACH attack; an AJAX or htmx POST to the same view instead has to send the token in the X-CSRFToken header (configurable via CSRF_HEADER_NAME). The django-htmx docs recommend setting it once so every element inherits it: <body hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'> — and note that is {{ csrf_token }} the variable, not the {% csrf_token %} hidden-input tag. For HTTPS requests with no Origin header Django adds strict Referer checking on top, so a proxy that rewrites those headers can produce a 403 that looks like nothing the user did. The failing request's headers and cookies settle it instantly; a screenshot cannot.
The other Django bug that quietly eats response time is the N+1 query. QuerySets are lazy and follow relations on attribute access, so a list view that renders one related object per row fires one SELECT for the list plus one more per row. The fix depends on the relation direction: select_related() emits a SQL JOIN for forward foreign-key and one-to-one fields, while prefetch_related() runs a second query and joins it in Python for reverse foreign-key and many-to-many fields. Django Debug Toolbar's SQL panel is the canonical way to see it — it groups repeats into Similar (same SQL, different parameters) and Duplicate (byte-identical) badges, so an N+1 shows as a high count of the same SELECT. This guide is for Django teams shipping server-rendered HTML and htmx partials in 2026: htmx sends HX-Request: true on every request it issues (how django-htmx's request.htmx tells a partial swap from a full navigation), and that header behavior is exactly what a captured network HAR makes legible when a partial 403s, 500s, or runs slow.
Django gotchas
Framework-specific failure modes engineers actually ship through. Each is hard to spot in a screenshot and obvious in a session replay.
Common Django bugs
Real Django bug patterns — the symptom you will see in a report and the fix that actually works.
Every htmx POST returns 403 CSRF verification failed
Symptom: Server-rendered forms submit fine, but any htmx-issued POST/PUT/PATCH/DELETE returns 403 and the swap shows Django's CSRF failure page or a blank region.
Fix: htmx requests do not carry the hidden csrfmiddlewaretoken, so Django needs the token in the X-CSRFToken header. Set it once on the body with hx-headers using the {{ csrf_token }} variable, and avoid swapping the element that holds it. The captured request headers show whether X-CSRFToken was present, empty, or stale.
<!-- set the token once so every htmx request inherits it -->
<body hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'>
<!-- ... -->
</body>
<!-- or, in JS, attach it per request -->
<script>
document.body.addEventListener('htmx:configRequest', (e) => {
e.detail.headers['X-CSRFToken'] = getCookie('csrftoken');
});
</script>A list view fires one query per row (N+1)
Symptom: A page that renders a list with a related object per row is slow under real data; Django Debug Toolbar's SQL panel shows a high query count with Similar/Duplicate badges on the same SELECT.
Fix: Add select_related() for forward foreign-key and one-to-one relations (a single JOIN) or prefetch_related() for reverse foreign-key and many-to-many relations (a second query joined in Python). Confirm the drop by reading len(django.db.connection.queries) before and after.
# bug: one extra query per book to fetch book.author
for book in Book.objects.all():
print(book.author.name)
# fix: forward FK -> JOIN in one query
for book in Book.objects.select_related('author'):
print(book.author.name)
# reverse FK / M2M -> prefetch_related
authors = Author.objects.prefetch_related('books')htmx partial 500s in production but works in dev
Symptom: A fragment view renders locally with DEBUG=True but returns a 500 in production; the user sees a blank or stale region instead of an error, because the failing response was swapped into the DOM.
Fix: The usual causes are a template variable that is None only in prod data, a missing template or context key for the partial, or a database error on rows that only exist in production. Because the 500 is swapped in, read the network response for the status and body, then use the DOM replay plus console to see which swap target received it.
# return a fragment for htmx, full page otherwise (django-htmx)
def rows(request):
qs = Item.objects.select_related('owner')
template = 'items/_rows.html' if request.htmx else 'items/list.html'
return render(request, template, {'items': qs})BugMojo vs alternatives
The honest comparison — where BugMojo wins, and where another tool might serve you better.
| Feature | BugMojo | Debug Toolbar / Sentry |
|---|---|---|
| DOM session replay of the user's exact steps | ✅ rrweb-based | ⚠️ Sentry replay (sampled); Debug Toolbar none |
| Network HAR with the 403/500 request + X-CSRFToken/HX-Request headers | ✅ shows the failing request and its headers | ⚠️ server-side view only |
| Captures the bug from a tester's or user's real browser | ✅ browser-layer, no SDK | ❌ Debug Toolbar is local-dev only |
| MCP server: Claude Code / Cursor read the report in the IDE | ✅ bug is a first-class assignee | ❌ none |
| Always-on production error monitoring & aggregation | ❌ on-demand capture | ✅ Sentry is more complete |
| Local SQL / query-count / template timing inspection | ❌ | ✅ Django Debug Toolbar |
| Console + network (HAR) capture | ✓ | Partial |
| Zero-setup Quick Capture | No project, no SDK | Account / SDK required |
BugMojo records the DOM, console, and network — then ships a one-click ticket with the full replay attached. No SDK, no setup.
Try BugMojo freeFrequently asked questions
Frequently asked questions
Sources
- Django 5.2 — Cross Site Request Forgery protection (403 on failure, {% csrf_token %}, strict Referer checking, CSRF_COOKIE_* settings) — Django Software Foundation (official docs) (2025)
- Django 5.2 — How to use Django's CSRF protection (X-CSRFToken header, CSRF_HEADER_NAME, getCookie('csrftoken')) — Django Software Foundation (official docs) (2025)
- Django 5.2 — Database access optimization (select_related/prefetch_related for N+1, connection.queries, lazy QuerySets) — Django Software Foundation (official docs) (2025)
- django-htmx — Tips (recommended <body hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'> pattern; request.htmx middleware) — django-htmx (Adam Johnson, official docs) (2025)
- Django 5.2 released — LTS, April 2, 2025; composite primary keys; security support to April 2028 — Django Software Foundation (weblog) (2025-04-02)
- Django Developer Survey 2024 results (ASGI 37%, async views 14%, DRF 49% favorite package) — JetBrains + Django Software Foundation (2024)

