How to Set Up report-uri.com on Django

Let us see things we could not see before!

In recent years browsers have gained many powers to report back problems they encounter on your site, such as:

Browsers send these reports to URIs listed in specific security headers, including the exiperimental Report-To header. These are really useful since they can uncover issues that would otherwise go unseen.

A service for collecting, parsing, and making sense of these reports is report-uri.com. It’s run by Scott Helme a security researcher who also made the useful free tool securityheaders.com. It makes a lot of sense to use a separate service for receiving browser reports, since if you have a problem on your own site, it’s likely you’d have problems collecting the reports too!

Yesterday I set up report-uri.com on my new Django project db-buddy.com. Here’s how I did it.

Note: I added the headers from within Django. This makes sense for me since I’m deploying on Heroku and serve all URLs from Django, including static assets via Whitenoise. If your site is a bit more complicated than this, you might want to add the headers via a wrapping web server, such as nginx, in which case follow the report-uri.com docs.

Adding the Headers

First, I signed up. After the usual account creation I landed on the setup screen. This provides the values to plug into the various headers:

report-uri.com setup screen

Second, I added the Content Security Policy report. I’m using django-csp to control my CSP header, so this required just one more setting:

CSP_REPORT_URI = "https://dbbuddy.report-uri.com/r/d/csp/enforce"

Third, I added a middleware to inject two more headers - the generic Report-To and NEL for network error logging:

class ReportUriMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        # Header values from https://report-uri.com/account/setup/
        response["Report-To"] = '{"group":"default", ...}'
        response["NEL"] = '{"report_to":"default", ...}'
        return response

This fit in just after my other security header middleware:

MIDDLEWARE = [
    ...,
    "django.middleware.security.SecurityMiddleware",
    "csp.middleware.CSPMiddleware",
    "django_feature_policy.FeaturePolicyMiddleware",
    "db_buddy.core.middleware.ReportUriMiddleware",
    ...,
]

I also added a test, verifying that the middleware worked and I’d copied the JSON from report-uri.com correctly:

import json

from django.http import HttpResponse
from django.test import RequestFactory, SimpleTestCase

from db_buddy.core.middleware import ReportUriMiddleware


class ReportUriMiddlewareTests(SimpleTestCase):
    request_factory = RequestFactory()

    def test_middleware(self):
        def dummy_view(request):
            return HttpResponse()

        middleware = ReportUriMiddleware(dummy_view)
        request = self.request_factory.get("/")

        response = middleware(request)

        assert json.loads(response["Report-To"])
        assert json.loads(response["NEL"])

Reports Appear

Once the above changes were deployed, reports started coming in. For example here’s a network error report I received from someone testing my /500/ URL, which demoes the “Internal Server Error” screen:

report-uri.com network error reports

Nice!

Fin

It’s awesome to see this development in browsers. report-uri.com is a really easy to set up service and I’m looking forward to using it going forwards.

—Adam


Read my book Boost Your Git DX to Git better.


Subscribe via RSS, Twitter, Mastodon, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: