Django app integration¶
Add pulumi-django-azure to the Django project that runs on App Service.
Minimal settings wiring¶
from pulumi_django_azure.settings import * # noqa: F403
from pulumi_django_azure.settings import patch_django_settings_for_azure
INSTALLED_APPS = [
# ... your apps ...
"django.contrib.staticfiles",
]
MIDDLEWARE = [
# ... your middleware ...
]
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"OPTIONS": {
"context_processors": [
# ...
],
},
},
]
patch_django_settings_for_azure(INSTALLED_APPS, MIDDLEWARE, TEMPLATES)
patch_django_settings_for_azure will:
- Insert
collectfastabeforedjango.contrib.staticfiles - Append
pulumi_django_azure,django_rq, anddjango_tasks_rqif missing - Insert
SuspiciousRequestBlockMiddlewarebeforeSecurityMiddlewareon Azure when probe blocking is enabled (default) - Insert
SuspiciousRequestAccessBlockMiddlewarebeforeSecurityMiddlewareon Azure whenACCESS_BLOCK_ENABLED=trueandACCESS_BLOCK_QUEUE_URLis set (Pulumi default:add_django_website(..., access_block=True)) - Insert
RequestLogContextMiddlewarebeforeSecurityMiddlewareon Azure, outermost of the package middleware (adds client IP andHostto Django log lines, including after probe blocking) - Insert
HealthCheckMiddlewarebeforeSecurityMiddlewareon Azure (append when not on Azure) - Insert
WagtailHostAliasMiddlewarebefore Wagtail’s redirect middleware when present - Append the
add_build_infocontext processor
Scanner probe blocking¶
On Azure, SuspiciousRequestBlockMiddleware rejects common vulnerability-scanner traffic before SSL redirect and URL routing. This reduces log noise and worker load from automated probes (.php shells, WordPress paths, empty User-Agent, and similar).
Blocked by default (hardcoded rules):
- Any path segment starting with
.(e.g./.env,/.git/) — except/.well-known/ - Paths ending in
.php,.asp,.aspx,.jsp,.cgi,.sql,.bak - Paths starting with
/wp-,/cgi-bin/,/phpmyadmin,/administrator/,/vendor/phpunit, and other non-Django CMS/framework prefixes (see Settings and env) - Paths containing
/phpunit/ - Requests with a missing, empty, or whitespace-only
User-Agentheader
Not blocked: /health-check, /.well-known/…, /admin/ (Django/Wagtail), and normal site URLs.
Disable individually via App Settings:
| Variable | Default on Azure | Purpose |
|---|---|---|
BLOCK_SUSPICIOUS_PATHS |
true |
Path-based probe blocking |
BLOCK_EMPTY_USER_AGENT |
true |
Reject requests without a User-Agent |
Set either to false to disable that check. When both are false, the middleware is not registered.
Blocked requests return 404 immediately and are logged at DEBUG on pulumi_django_azure.security (not django.request WARNING).
When automated access block is enabled, the same suspicious requests are also enqueued for App Service IP Deny rules after a rate threshold (no Redis required).
Manual alternative¶
Without the patch helper:
from pulumi_django_azure.settings import * # noqa: F403
INSTALLED_APPS += ["collectfasta", "pulumi_django_azure", "django_rq", "django_tasks_rq"]
MIDDLEWARE += ["pulumi_django_azure.middleware.HealthCheckMiddleware"]
Place collectfasta before django.contrib.staticfiles.
What settings do on Azure¶
When App Service sets IS_AZURE_ENVIRONMENT=true (done by Pulumi), the imported settings configure Postgres with Entra tokens, Azure Storage/CDN, secure cookies, optional Redis and tasks, optional ACS email via MAILERS when AZURE_COMMUNICATION_SERVICE_ENDPOINT is set, and logging. See Settings and env and Local vs production.
Health check¶
Pulumi configures App Service health checks at /health-check. On Azure, HealthCheckMiddleware is registered before SecurityMiddleware so probes are answered without SSL redirect or Wagtail routing.
The middleware:
- Serves
/health-check: probes the database and can recycle Gunicorn workers on failure. (DB tokens are not refreshed here: the DB engine acquires one per connection, see the database guide.) - Serves Azure Always-On pings (
GET /from loopback withUser-Agent: AlwaysOn) with a lightweight200 OK— no DB probe.
Override freely¶
Import Azure defaults first, then override any setting in your module afterward.