Multiple Host web app (Redirecting/Routing) in Django middleware / view
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
A Django application can serve different behavior based on the request host, but the implementation depends on what “different behavior” actually means. Sometimes the right answer is a redirect to a canonical host. Other times the request should stay on the current host and use different URLs, templates, or tenant data internally. Keeping those cases separate prevents a lot of messy middleware.
Decide Between Redirecting and Routing
There are two common host-based behaviors.
Redirecting means the browser should be sent somewhere else, often from an old domain to a new one. Routing means Django should continue handling the request internally, but with different URL configuration or data selection based on the host.
That distinction matters because the code belongs in different places conceptually:
- redirects are usually global middleware concerns,
- routing can be middleware, URLConf switching, or tenant resolution,
- and a view-level host check is only appropriate when the host rule is extremely local.
Before any of that works, the expected hosts must be in ALLOWED_HOSTS.
Without that, Django rejects the request before your own logic sees it.
Redirect a Host in Middleware
If one host should always redirect to another, middleware is usually the cleanest home.
This keeps host migration logic out of individual views and makes the redirect apply consistently across the whole site.
Route Different Hosts to Different URL Configurations
If multiple hosts should stay active but expose different URL maps, middleware can assign request.urlconf.
Then each host can have its own URL file.
That is usually cleaner than sprinkling if request.get_host() throughout the view layer.
Tenant Routing Is a Different Pattern Again
Sometimes the routes stay the same, but the host identifies a tenant. In that case, middleware often attaches the resolved tenant to the request instead of switching URLConf.
Views then use request.tenant normally. This is a better fit when the host changes data selection rather than route shape.
When View-Level Host Checks Are Acceptable
A host check inside a view is fine when only one or two endpoints truly need host-specific behavior and the rule is tightly local.
As soon as the same host rule appears in several views, move it back into middleware or a reusable abstraction.
Common Pitfalls
- Forgetting to include expected domains in
ALLOWED_HOSTS. - Reading the raw host header instead of using Django’s validated
request.get_host(). - Mixing redirect rules and tenant-routing rules in random views.
- Accidentally comparing the full
host:portstring during local development. - Using view-level host conditionals for behavior that is really a global routing concern.
Summary
- Use redirects when the browser should move to a different canonical host.
- Use middleware routing when the host should change internal URL resolution.
- Use tenant middleware when the host selects data more than routes.
- Keep host logic centralized once more than a small number of views depends on it.
- Always start by configuring
ALLOWED_HOSTScorrectly.

