Nginx & reverse proxies for Django in production
Nginx, Caddy, Traefik, Apache, and CDNs in front of Django: routing, buffering, limits, and the status codes they produce.
Nginx sits in front of Django in most self-hosted deployments. It terminates TLS, serves static files, buffers slow clients, and passes the headers Django relies on to know the host and scheme.
Many Django errors are really proxy errors: 502s, redirect loops, 413s, and wrong client IPs. The guides here cover configuring Nginx well and reading what it tells you.
Set it up
Guides that take this from nothing to running in production.
- Deploy Django with Caddy and Automatic HTTPS
If you want to deploy Django with Caddy in production, the main challenge is not just putting a reverse proxy in front of the app.
- How to Configure Nginx for Django in Production
A production Django stack should not expose Gunicorn or Uvicorn directly to the internet.
- Deploy Django Behind Cloudflare
Put Django behind Cloudflare correctly: Full (strict) TLS, origin lockdown, real client IPs, HTTPS detection, cache rules, and request limits.
- Deploy Django with Traefik and Docker
Put Traefik in front of a Dockerized Django app: automatic Let's Encrypt certificates, HTTP to HTTPS redirects, Docker labels for routing, static files, and upload limits.
- Expose Django with Cloudflare Tunnel
Serve a Django app through Cloudflare Tunnel with no open inbound ports: cloudflared setup, ingress rules, DNS routing, real client IPs, HTTPS settings, and protecting the admin.
- Serve Django Under a URL Prefix (/app) Behind Nginx
Run Django under a subpath such as example.com/app behind Nginx: forward SCRIPT_NAME correctly, set FORCE_SCRIPT_NAME, STATIC_URL, and cookie paths, and avoid broken admin links, redirects, and CSRF errors.
- Load Balance Django With HAProxy
Put HAProxy in front of several Django app servers: active health checks on a /health/ endpoint, TLS termination, X-Forwarded-Proto, connection limits per server, rolling deploys with drain, and stick tables for basic rate limiting.
- Nginx vs Caddy for Django
Compare Nginx and Caddy as the reverse proxy for Django: automatic HTTPS versus Certbot, configuration size, performance, static files, ecosystem and documentation, on-demand TLS for tenants, and which suits your deployment.
- Deploy Django Behind Nginx Proxy Manager
Put a Docker-based Django app behind Nginx Proxy Manager: create a proxy host with Let’s Encrypt, connect containers on a shared network, forward the right headers, serve static files, allow large uploads, and keep the admin UI private.
- Deploy Django with Gunicorn and Nginx on Ubuntu
Moving a Django app from local development to production usually fails on the same points: process management, reverse proxy configuration, static files, secrets, TLS, and safe...
- Deploy Django with uWSGI and Nginx
Run Django with uWSGI and Nginx: a strict uWSGI ini, socket permissions, systemd integration, graceful reloads, harakiri timeouts, and checks.
- Deploy Django with Apache and mod_wsgi
Deploy Django on Apache with mod_wsgi daemon mode, a matching virtualenv, static file aliases, secret loading, HTTPS, and code reloads without restarting Apache.
- Host Multiple Django Sites on One Server
Run several Django projects on one server safely: separate users and virtualenvs, systemd templates, per-site databases, and Nginx blocks.
- Deploy Multi-Tenant Django with django-tenants
Deploy a schema-per-tenant Django SaaS with django-tenants: wildcard DNS and TLS, Nginx for subdomains, migrate_schemas in releases, tenant-aware caching, media, Celery, and pooling.
- Serve Private Django Media Files with Nginx X-Accel-Redirect
Serve permission-checked Django downloads efficiently: Django authorises the request and returns X-Accel-Redirect, and an internal Nginx location streams the file without tying up workers.
- Serve Django API and Admin on Separate Subdomains
Serve one Django project on www, api, and admin subdomains: per-host URLconfs, host-only cookies, CORS and CSRF settings, Nginx server blocks, and restricting admin access by network.
- Run Django Behind an AWS Application Load Balancer
Configure Django correctly behind an AWS Application Load Balancer: HTTPS listeners with ACM, X-Forwarded-Proto and client IPs, ALLOWED_HOSTS for health checks, idle and keepalive timeouts, stickiness, and deregistration delay for deploys.
- Password-Protect a Django Staging Site
Keep a Django staging site private: HTTP basic auth in Nginx or Caddy with health-check and webhook exceptions, Django middleware for PaaS hosts, noindex headers, IP or VPN restrictions, and why basic auth interferes with API tokens.
- Custom Domains With Automatic HTTPS for Django Tenants
Let SaaS customers point their own domains at a multi-tenant Django app with automatic HTTPS: Caddy on-demand TLS with an ask endpoint in Django, domain verification, tenant lookup by Host, and protecting against abuse and rate limits.
- Expose Django on Kubernetes With Gateway API or Ingress
Route HTTPS traffic to Django on Kubernetes with the Gateway API or a maintained Ingress controller: HTTPRoutes or Ingress rules, TLS with cert-manager, forwarded headers, body size and timeouts, and moving off the retired ingress-nginx.
- Protect Django Endpoints With mTLS Client Certificates in Nginx
Require mutual TLS client certificates for Django admin, internal APIs, or partner integrations with Nginx: create a private CA, issue client certificates, configure ssl_verify_client, pass verified identity to Django, and handle revocation and renewal.
- Put Varnish Cache in Front of Django
Use Varnish as an HTTP cache in front of Django: send correct Cache-Control headers from views, write VCL that bypasses logged-in users and POST requests, strip tracking cookies, purge or ban pages on content changes, and run Varnish with Nginx for TLS.
Run it well
Tuning, reliability, and operations once it works.
- Cache Django Responses with Nginx proxy_cache
Use Nginx proxy_cache in front of Django safely: cache zones, bypassing logged-in users, respecting Cache-Control, stale serving, and purging.
- Rate Limit Django Endpoints with Nginx
Protect Django login, API, and expensive endpoints with Nginx limit_req: zones, burst and nodelay, 429 responses, real client IPs, and dry runs.
- Enable HTTP/2 and HTTP/3 for Django with Nginx
Serve a Django site over HTTP/2 and HTTP/3 (QUIC) with Nginx: version-specific directives, Alt-Svc, UDP 443, keeping HTTP/1.1 to Gunicorn, verifying with curl, and what actually gets faster.
- Tune Nginx Keepalive and Worker Connections for Django
Tune Nginx for a Django backend: worker_processes and worker_connections, client keepalive_timeout, upstream keepalive connection pools to Gunicorn or Uvicorn, proxy buffering, and file descriptor limits, with when each setting matters.
- Enable Gzip and Brotli Compression for Django with Nginx
Compress Django responses and static files correctly: Nginx gzip settings, precompressed static assets, Brotli support, BREACH caveats, and checks.
- Django Maintenance Mode During Deploys
Put a Django site into maintenance mode safely: an Nginx flag file returning 503 with Retry-After, a static maintenance page, staff IP bypass, health checks, and when to avoid maintenance windows.
- Set Cache-Control Headers in Django for CDNs and Browsers
Control how browsers and CDNs cache Django responses: cache_control and never_cache decorators, public versus private, max-age versus s-maxage, Vary headers from sessions and cookies, ETags, and safe defaults for logged-in pages.
- Cache Django Pages at Cloudflare's Edge
Cache Django HTML at Cloudflare’s edge safely: Cache Rules for anonymous pages, bypassing cache when session or CSRF cookies exist, respecting origin Cache-Control, purging on deploy or content change, and avoiding cached private pages.
- Custom 404 and 500 Error Pages for Django in Production
Create custom 404, 500, 403, and 400 pages for a Django site in production: templates and handler views, a 500 page that cannot fail, static assets that load, Nginx error pages for when Django is down, and testing them with DEBUG off.
Check it
Review lists to run before launch or after a change.
- Nginx Production Checklist for Django
A production checklist for Nginx in front of Django: server blocks and redirects, TLS, proxy headers, static and media serving, body size and timeouts, buffering, compression, security headers, logging, and safe reloads.
- Django Load Balancer Configuration Checklist
A checklist for putting Django behind a load balancer: health checks that pass host validation, forwarded headers and SECURE_PROXY_SSL_HEADER, correct client IPs, idle and request timeouts, connection draining, sticky sessions avoided, and TLS policy.
Fix it
Errors and symptoms, each traced to a cause and a fix.
- Django 502 Bad Gateway: Causes and Fixes
A 502 Bad Gateway in a Django deployment usually means your reverse proxy is reachable, but it cannot get a valid response from the upstream app server.
- Fix Nginx Not Connecting to Gunicorn (Connection Refused)
Fix Nginx connect() failed (111: Connection refused) to Gunicorn: service status, bind address mismatches, stale sockets, firewalls, and startup ordering.
- Fix Nginx 413 Request Entity Too Large for Django Uploads
If a Django file upload fails with 413 Request Entity Too Large, the rejection usually happens before Django sees the request. In a production stack like:
- Fix Django 504 Gateway Timeout Behind Nginx
Diagnose Django 504 Gateway Timeout errors behind Nginx: find the slow layer, align proxy and Gunicorn timeouts, fix slow views, and offload long work.
- Fix Nginx 403 Forbidden on Django Static Files
Fix 403 Forbidden for Django static and media files served by Nginx: directory traversal permissions, file modes, alias paths, SELinux contexts, and verification.
- Fix Wrong Client IP Addresses in Django Behind a Proxy
Get the real client IP in Django behind Nginx, load balancers, or CDNs: X-Forwarded-For parsing, trusted proxy counts, Nginx realip, and spoofing risks.
- Fix Nginx "upstream sent too big header" with Django
Fix 502 errors caused by Nginx 'upstream sent too big header': measure Django's response headers, raise proxy_buffer_size safely, and shrink oversized cookies, sessions, and headers.
- Fix Nginx Showing the Default Page Instead of Django
Fix the Welcome to nginx page appearing instead of your Django site: enable the site, remove the default server, match server_name, add the HTTPS and IPv6 listeners, and reload after nginx -t.
- Fix Nginx 499 Client Closed Request with Django
Understand and fix Nginx 499 status codes in front of Django: find the slow endpoints and impatient clients, align load balancer and proxy timeouts, and move long work to background tasks.
- Fix Nginx "upstream prematurely closed connection" for Django
Fix the Nginx error upstream prematurely closed connection while reading response header from upstream: find the Gunicorn worker that died or timed out, match timeouts across layers, and fix crashes and memory kills.
- Fix Nginx "no live upstreams" in Front of Django
Fix the Nginx no live upstreams while connecting to upstream error for Django: understand max_fails and fail_timeout, find why every backend was marked down, and configure upstream health and retries safely.
- Fix Nginx "conflicting server name" Warnings
Fix the Nginx warning conflicting server name example.com on 0.0.0.0:80, ignored: find duplicate server blocks in sites-enabled and conf.d, remove leftover Certbot or default configs, and make the right Django site answer.
- Fix Common nginx [emerg] Errors in a Django Config
Fix nginx [emerg] errors that stop Nginx from starting or reloading for a Django site: unknown directive, directive is not allowed here, host not found in upstream, cannot load certificate, and unexpected end of file.
- Fix Nginx bind() to 0.0.0.0:80 Failed (Address Already in Use)
Fix nginx bind() to 0.0.0.0:80 failed (98: Address already in use) on a Django server: find the process holding port 80 or 443, stop Apache or a stray Nginx, and fix duplicate listen directives and Docker port clashes.
- Fix Nginx 404 Not Found for Django URLs
Fix an Nginx 404 Not Found page for Django URLs that work with runserver: route requests to Gunicorn instead of the filesystem, fix try_files and location ordering, root versus alias, and trailing-slash mismatches.
- Fix Django StreamingHttpResponse and SSE Buffered by Nginx
Fix Django StreamingHttpResponse and server-sent events that arrive all at once instead of streaming: disable Nginx proxy buffering with X-Accel-Buffering, avoid GZip middleware on streams, and use async views for long streams.
- Fix Django Redirecting to localhost or the Wrong Host
Fix Django redirects that send users to localhost, 127.0.0.1, or an internal hostname behind Nginx or a load balancer: forward the Host header, use USE_X_FORWARDED_HOST correctly, and fix LOGIN_REDIRECT_URL and absolute URLs.
- Fix Cloudflare 521 and 522 Errors for a Django Origin
Fix Cloudflare Error 521 web server is down and Error 522 connection timed out in front of Django: check that Nginx listens, allow Cloudflare IPs through the firewall, match the SSL mode to the origin, and fix origin DNS records.
- Fix Cloudflare 524 Timeouts on Slow Django Requests
Fix Cloudflare Error 524 a timeout occurred for slow Django views: understand the 100-second proxy limit, move exports and reports to Celery with polling, stream responses, and bypass the proxy for genuinely long admin tasks.
- Fix Nginx Dropping Custom Headers With Underscores Before Django
Fix custom request headers such as X_API_KEY or tenant_id disappearing behind Nginx: understand why Nginx ignores headers with underscores, rename them to use hyphens, or enable underscores_in_headers, and read them correctly in Django’s request.headers.
- Fix Nginx "connect() to unix:/run/gunicorn.sock Failed (2: No Such File or Directory)"
Fix the Nginx 502 error connect() to unix:/run/gunicorn.sock failed (2: No such file or directory): start the Gunicorn socket or service, match the socket path in Nginx and systemd, and stop PrivateTmp and RuntimeDirectory from hiding the file.
- Fix Nginx "Could Not Build server_names_hash" for Django Sites
Fix the Nginx emerg error could not build server_names_hash, you should increase server_names_hash_bucket_size when adding long domain names or many Django sites: set the bucket and max size in the http block, test, and reload safely.
- Fix Nginx "Cannot Load Certificate" for a Django Site
Fix Nginx emerg errors such as cannot load certificate BIO_new_file() failed, no such file, or key values mismatch: check certificate paths, Certbot live symlinks, file permissions, PEM formats, and that the key matches the certificate.
- Fix Nginx "worker_connections Are Not Enough" in Front of Django
Fix the Nginx alert worker_connections are not enough while connecting to upstream for Django: count client and upstream connections, raise worker_connections and worker_rlimit_nofile, use upstream keepalive, and find slow responses that hold connections open.
- Fix Gunicorn Socket Permission Denied (Nginx + Django)
Fix Permission denied on a Gunicorn Unix socket: socket ownership and mode, directory traversal, systemd RuntimeDirectory, umask, and SELinux.
- Fix Django Media Files Not Serving (Uploads Broken in Production)
Fix Django media files returning 404 or 403 in production: MEDIA_URL and MEDIA_ROOT, the static() helper, Nginx alias rules, permissions, and object storage.
- Fix Too Many Redirects with Django HTTPS Behind a Proxy
Fix ERR_TOO_MANY_REDIRECTS on Django behind Nginx, a load balancer, or Cloudflare: SECURE_PROXY_SSL_HEADER, forwarded headers, and duplicate redirects.
- Fix Django WebSocket Connection Failed Behind Nginx
Debug Django Channels WebSocket failures behind Nginx: handshake status codes, Upgrade headers, ASGI routing, origin checks, wss mixed content, and idle timeouts.
- Fix RequestDataTooBig and TooManyFieldsSent in Django
Fix Django RequestDataTooBig, TooManyFieldsSent, and TooManyFilesSent errors: what each upload limit measures, raising limits safely per endpoint, and proxy limits.
- Fix Django Static Files Served with the Wrong MIME Type
Fix Refused to apply style and Expected a JavaScript module script errors for Django static files: HTML 404 pages served as CSS, missing mime.types in Nginx, .mjs and .wasm types, and WhiteNoise.
- Fix HTTPS Not Working for Django Behind Nginx
Fix a Django site where HTTPS does not load after installing a certificate: open port 443 in the firewall and cloud security group, add a listen 443 ssl block, check DNS and the certificate, and make Django aware of HTTPS.
- Fix uWSGI "invalid request block size"
Fix the uWSGI error invalid request block size behind Nginx for Django: use uwsgi_pass instead of proxy_pass for the uwsgi protocol, match socket and http-socket, and raise buffer-size for large cookies and headers.
- Fix Django WebSockets Disconnecting After 60 Seconds
Fix Django Channels WebSocket connections that drop after about 60 seconds of inactivity: raise Nginx proxy_read_timeout, configure load balancer idle timeouts, and send keepalive pings from the client or server.
- Fix AWS Application Load Balancer 502s for Django
Fix intermittent and constant 502 Bad Gateway errors from an AWS Application Load Balancer in front of Django: keepalive timeouts shorter than the ALB idle timeout, unhealthy targets, security groups, and HTTP/HTTPS target mismatches.
- Fix a Django Site That Runs but Cannot Be Reached
Fix a Django site that runs on the server but times out in the browser: check what listens and on which address, open ufw and cloud firewalls, stop binding to 127.0.0.1, and confirm DNS points at the server.
- Fix Django 403 Forbidden Errors in Production
Fix 403 Forbidden errors on a Django site in production: tell Nginx 403s from Django 403s, fix CSRF failures, file permissions on static and media, permission checks in views, and WAF or Cloudflare blocks.
- Fix Django Authorization Header Missing Behind Apache, Nginx, or a Proxy
Fix Django REST Framework returning authentication credentials were not provided because the Authorization header never reaches Django: enable WSGIPassAuthorization in mod_wsgi, check Nginx and load balancer header forwarding, and test with curl at each hop.
Learning paths through this topic
- Your first production deployment on a VPSFrom a fresh Ubuntu server to a Django site on HTTPS, with the settings and checks that keep it safe.
- Docker, from image to productionBuild a production image, run it with Compose, ship it from CI, and check the result.
- Django on KubernetesRun Django on a cluster with config, ingress, probes, jobs, graceful shutdown, and autoscaling, then package it with Helm and GitOps.
- Making Django fastMeasure first, then fix queries, caching, workers, and delivery, and prove it with a load test.