Dear group,
with the introduction of CSP support in Django 6.0, I’d like to start with a report-only policy to get some experience before even thinking about enforcing it. First of all, huge thanks to everyone working on Django’s security features – the CSP integration is a great addition.
However, the docs say:
To collect and analyze them, you must implement your own reporting endpoint or integrate with a third-party monitoring service.
and pretty much everywhere else I look, I read similar notes.
Googling around, I found django-csp-reports. It looks like a solid project and I appreciate the work that went into it, but it doesn’t seem to support Django 6.0 (or even 5.0, there’s a nearly two-year-old issue about that). Other sources suggest using Sentry.
I could go with either option, but I’m a bit confused: report-only mode is recommended for testing and onboarding, yet there’s almost no guidance out there on how people actually collect and inspect the reports. It feels like I’m missing something obvious, even though I did find the two options above.
So can anyone share what a good, practical “getting started” solution looks like?
Hello,
thanks for your reply!
Well, it would really be a shame if upgrading to Django 6 specifically for using one of the newly introduced, very valued features, resulted in exactly the opposite – and that only because of one unclear detail.
Understood, I found Sentry, too. Maybe my question is too academic (as two viable solutions seem to exist), but it stands. 
Okay, I’ve found a solution, a simple view that works as an endpoint for CSP violation reports:
import json
from django.conf import settings
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
CSP_LOG_FILE = settings.BASE_DIR.parent / "csp-violations.log"
CSP_LOG_MAX_SIZE = 1024 * 1024 # 1 MB
@csrf_exempt
@require_POST
def csp_violation_report(request):
"""
Receives CSP violation reports and writes them into a logfile.
The log file will not exceed 1 MB. When the limit is reached,
no further entries will be written.
"""
if CSP_LOG_FILE.exists() and CSP_LOG_FILE.stat().st_size >= CSP_LOG_MAX_SIZE:
return HttpResponse(status=204)
try:
report = json.loads(request.body.decode("utf-8"))
except (json.JSONDecodeError, UnicodeDecodeError):
return HttpResponse(status=400)
with open(CSP_LOG_FILE, "a", encoding="utf-8") as f:
f.write(json.dumps(report, indent=2) + "\n\n")
return HttpResponse(status=204)
Yes, this solution is minimal, it doesn’t scale, has no proper throttling, has no automatic evaluation, writes into each app server’s file system and probably has other limitations and issues. It’s only purpose is to find places that were overlooked when adding nonces or have other implementation issues – it is not suitable to detect and monitor actual XSS attacks. But, taking these limitations into account, it is a lean solution that turned out to work very well for our project.
@carstenfuchs
Given how very simple your solution is, do you still feel we need to say more in the docs here? (Like, at some point people have to just write the view, but maybe the phrasing makes it seem more than it is?)
Yes, I think that would be helpful. 
In areas where I’m not particularly confident, I tend to lean on Django to guide me in the right direction — and IT security definitely falls into that category for me.
From the CSP docs (both Django’s and e.g. Adam’s blog post, the MDN article, the article at web.dev), I got the impression that it’s quite easy to get CSP wrong on the first attempt and accidentally break parts of the site. Because of that, it seems really important to be able to receive CSP violation reports before enforcing the policy, and only switch it on once things have been quiet for a while.
What’s not very clear is how critical those reports actually are. I’ve covered all <script> tags in the templates, do I still really need reports? Can I reasonably do without a third-party app or service? Or is something like Sentry (even the free tier) effectively the expected solution, with ongoing monitoring? Why did django-csp drop support for reports? And if a minimal, home-grown endpoint is acceptable, what does “good enough” look like? (Without accidentally forfeiting the protection CSP brings?)
This doesn’t need to be a lot of documentation, but a small extra hint or bit of guidance would already be very helpful for someone turning on CSP for the first time.
I think that might be a bit more discursive than we’d really go into in the Django docs, but the section you quoted my go…
Django does not provide built-in functionality to receive, store, or process violation reports. You can implement an endpoint to collect the JSON reports or integrate with a third-party monitoring service for this purpose.
… (or similar) which would at least give the Oh, it’s only JSON feeling, which might be enough to get over the paralysis?