Skip to content

Instantly share code, notes, and snippets.

@birkin
Created April 22, 2025 14:50
Show Gist options
  • Save birkin/20225e331c0df2e5037285db94bd4548 to your computer and use it in GitHub Desktop.
Save birkin/20225e331c0df2e5037285db94bd4548 to your computer and use it in GitHub Desktop.
thought about handling exempt-paths for django turnstile middleware.

exempt-paths refactor suggestion

idea

Based on thinking and research, below is a suggestion for handling exempt-paths in turnstile_middleware.py.

Some nice things about this...

  • the regex-compilation happens just once, when the webapp first loads, improving speed.
  • it allows us to change a .env setting easily, without deploying new code.
  • it allows the pattern-match code in turnstile_middleware.py to be simple, becase regex is handling the "starts-with" or "anywhere-within" check.
  • the one downside with regex, for folk like me who don't live and breathe it -- is being confident about what's being matched. For that, we have the unit-tests.

.env file

export DJANGO_TURNSTILE_EXEMPT_PATHS='[
  "^.*?/turnstile-verify/",
  "^/static/",
  "^/media/"
]'

This shows the flexibility of using regex -- the first example will check for the pattern anywhere, the next two will do a "starts-with" check.


settings.py

import json
import os
import re

temp_exempts = os.environ.get('DJANGO_TURNSTILE_EXEMPT_PATHS', '[]')
try:
    temp_list = json.loads(temp_exempts)
except json.JSONDecodeError:
    temp_list = []
TURNSTILE_EXEMPT_PATHS = [re.compile(p) for p in temp_list]

turnstile_middleware.py

from django.conf import settings

EXEMPT_PATHS = settings.TURNSTILE_EXEMPT_PATHS

## if there's a match...
if any(pattern.match(request.path) for pattern in EXEMPT_PATHS):
    ## ...just let the request through
    return self.get_response(request)

tests

import re

from django.conf import settings
from django.test import SimpleTestCase

class TurnstileExemptPathsTests(SimpleTestCase):
    def setUp(self):
        ## grab the compiled regex list from settings
        self.patterns = settings.TURNSTILE_EXEMPT_PATHS

    ## not a test -- the checker
    def is_exempt(self, path: str) -> bool:
        return any(p.match(path) for p in self.patterns)

    def test_should_be_exempt(self):
        ## exact path
        self.assertTrue(self.is_exempt('/turnstile-verify/'))
        ## with querystring
        self.assertTrue(self.is_exempt('/turnstile-verify/?next=/secret/'))

    def test_should_not_be_exempt(self):
        self.assertFalse(self.is_exempt('/dashboard/'))
        self.assertFalse(self.is_exempt('/api/v1/data/'))
        self.assertFalse(self.is_exempt('/turnstile-verify-now/'))  # partial non‑match
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment