Cloudflare WAF Rules for a Django Site

Configure Cloudflare WAF custom rules, rate limiting, and managed rulesets for Django: protect /admin and login views, challenge bad bots, skip rules for webhooks and uploads, avoid blocking legitimate API clients, and verify the real client IP at the origin.

Problem statement

Your Django site runs behind Cloudflare, but bots still brute-force the login, scanners probe /wp-admin/ and .env, and occasionally a burst of requests to search or export endpoints slows everything down. Cloudflare's WAF can stop much of this at the edge with custom rules, rate limiting rules, and managed rulesets. Poorly scoped rules, though, block Stripe webhooks, mobile app API calls, or large uploads.

Cloudflare WAF for DjangoRequestat the edgeCustom rulespaths, countries, botsRate limitslogin, searchManaged rulesOWASP, CVEsEdge rules stop scanners and floods before they reach Django.
  1. Block known-bad paths that Django never serves (/wp-login.php, /.env, /xmlrpc.php) with a custom rule.
  2. Add rate limiting rules for /accounts/login/, /admin/login/, password reset, and expensive endpoints.
  3. Challenge or block traffic with low bot scores on sensitive paths, but skip verified bots and API clients.
  4. Enable the Cloudflare managed ruleset (and OWASP core ruleset if available), starting in log mode.
  5. Add skip rules for webhooks and upload endpoints, and use CF-Connecting-IP at the origin for real client IPs.

Step-by-step solution

1. Block paths Django never serves

A custom rule with action Block:

(http.request.uri.path contains "/wp-" or
 http.request.uri.path eq "/xmlrpc.php" or
 http.request.uri.path contains "/.env" or
 http.request.uri.path contains "/.git")

These requests otherwise reach Django, generate 404s, fill logs, and consume workers.

2. Rate limit authentication endpoints

A rate limiting rule:

  • Expression: http.request.uri.path in {"/accounts/login/" "/admin/login/" "/accounts/password/reset/"} and http.request.method eq "POST"
  • Characteristics: IP (and on higher plans, also a header or cookie)
  • Rate: 10 requests per minute
  • Action: Managed Challenge or Block for 10 minutes

Keep application-level limits (django-ratelimit, django-axes) too; the edge rule stops volume, the app understands usernames.

rule and targetActionScopeBlock scanner pathsblock/wp-*, /.env, /.gitRate limit loginchallenge or blockPOST to auth pathsBot score challengemanaged challenge/admin, signupManaged rulesetlog, then blockall trafficSkip for webhooksskip WAF and bots/webhooks/*Targeted rules stop abuse without blocking real integrationsdjango-deployment.com
Order matters: skip rules must run before the rules they bypass.

3. Handle bots carefully

On plans with Bot Management or Super Bot Fight Mode, challenge low bot scores on /admin/, signup, and checkout. Exclude:

  • Verified bots (search engines) on public pages.
  • Your own mobile app and API clients, which cannot solve challenges; identify them by path (/api/) and authentication.
  • Uptime monitors and payment provider webhooks.

A JavaScript challenge on an API endpoint returns HTML to a client expecting JSON, which looks like an outage to that client.

4. Turn on managed rulesets gradually

Enable the Cloudflare Managed Ruleset in Log mode, review matches for a week in Security Events, and switch to the default actions once you have added exceptions. Django admin edits and rich text fields can trigger XSS and SQLi signatures; scope exceptions to those paths and keep Django's own validation.

5. Skip rules and real IPs

Add a skip rule for webhook endpoints (/webhooks/) that bypasses managed rules and bot checks, relying on signature verification in Django. For large uploads, check Cloudflare's request body size limits for your plan; uploads above them need direct-to-storage uploads.

At the origin, use CF-Connecting-IP for the client address, and only trust it if the origin accepts traffic solely from Cloudflare (tunnel, firewall allowlist, or authenticated origin pulls).

from logging to blocking1Block scanner pathssafe immediately2Rate limit auth POSTschallenge first3Managed rules in log modeone week4Add skips and exceptionswebhooks, admin edits5Enforce and monitorSecurity EventsStart with rules that cannot hurt real users, then tighten with datadjango-deployment.com
Test webhooks from providers after every rule change.

Explanation

Cloudflare evaluates WAF phases in order: custom rules, rate limiting rules, managed rules, and bot rules, each with expressions over request fields. Blocking at the edge saves origin capacity and keeps noise out of Django logs, but edge rules know nothing about your users or business logic. Combining narrow edge rules with application-level checks gives cheap volume filtering without breaking legitimate clients that cannot pass browser challenges.

Edge cases / notes

  • Plan differences. Rule counts, rate limiting characteristics, and bot features vary by plan; check what yours includes.
  • Origin bypass. WAF rules are useless if attackers reach the origin IP directly; lock the origin to Cloudflare.
  • Caching interplay. Cached pages are served before some rules on certain plans; protect dynamic endpoints explicitly.

Next steps

FAQ

Will Cloudflare WAF break my Django webhooks?

It can, especially bot and managed rules. Add a skip rule for webhook paths and verify signatures in Django.

Should I rate limit login at Cloudflare or in Django?

Both. Cloudflare stops floods cheaply; Django understands usernames and accounts.

Why do API clients get HTML challenge pages?

A challenge rule matched API paths. Exclude API endpoints from challenge actions.

How does Django get the real client IP behind Cloudflare?

From the CF-Connecting-IP header, trusted only when the origin accepts traffic exclusively from Cloudflare.

Keep going