---
title: "Django security middleware: quickstart · camada"
description: "Keep credential stuffers, scrapers and exploit scanners out of your Django app: pip install camada, two lines and one env var."
url: https://www.camada.app/docs/quickstart/django
last_updated: 2026-10-30
---

Quickstart

# Django quickstart

Package `camada`, on PyPI.

## 1. Install

```sh
pip install camada
```

## 2. Environment

The key is printed once when you create a project in the app. Set these as environment variables: export them in the shell that starts your app, or add them in your host's settings. Python reads no .env file on its own.

shell

```sh
export CAMADA_KEY=<ingest_token>.<snap_token>
export CAMADA_INGEST_URL=http://localhost:8787   # dev only; defaults to production ingest
```

## 3. Wire it in

```python
# settings.py
MIDDLEWARE = ["camada.django.CamadaMiddleware", *MIDDLEWARE]
```

- Put it first, so blocked requests never reach the rest of the stack.
- Sync and async stacks alike; the matched route pattern rides the event.

## 4. Add the beacon to HTML pages

Optional but recommended. The beacon cuts false positives by showing which sessions are real browsers. Use the helper.

```python
from camada.django import script_tag
return f"<head>{script_tag(request)}</head>…"
```

## 5. Send app outcomes

Login failures, signups and checkout declines are the signals no edge position sees. The identifier is HMAC-hashed in your process. How they catch [credential stuffing](https://www.camada.app/use-cases/credential-stuffing).

```python
from camada.django import track
track(request, "login_failed", user=email)
```

Runtime

## What it does per request

1. Refreshes the snapshot off-path on a daemon thread (30 s poll with ETag, gzip on the wire). A cold start fails open.
2. Resolves the client IP per your trusted-proxy setting. Raw X-Forwarded-For is never trusted without it.
3. Your ordered custom rules first, then allow, block, challenge. A block gets a 403 with x-block-reason before your app runs; a challenge serves the proof-of-work page on a page load, and an API call reaches your app with an x-camada-challenge: 1 response header. The event still ships.
4. Serves /_cam/b.js (the beacon, first-party) and relays /_cam/fp posts onto the event batch with the resolved client IP.
5. Sets x-rid and the _sfp session cookie, and when the response is done ships one batched, redacted event.

Limits

## Caveats at this position

- Enforcement at this position covers IP, path, user-agent and request-header conditions. ASN, country and TLS-fingerprint entries fail open in-app.
- WSGI loses the wire's header order; the analyst knows this tap cannot see it and never scores its absence.
- Every worker process polls its own snapshot (about 5 MB each) — the server's poll_seconds keeps the cadence honest. Under gunicorn --preload the threads restart after the fork.
- CAMADA_SERVERLESS=1 switches to lazy refresh with no poll thread.

Kill switch: `CAMADA_DISABLED=1`, checked per request. All other variables are listed in the [docs index](https://www.camada.app/docs); the package's options and deployment notes are on the [Python page](https://www.camada.app/docs/python).

## Sitemap

[Every page on this site](https://www.camada.app/sitemap.md)
