django-hostmap
Host-based URL routing and host-aware reversing for Django, with zero call-site changes.
django-hostmap routes requests to different URLconfs by host (subdomain or
full domain) and makes URL reversing host-aware without changing a single
call site: the stock reverse(), reverse_lazy() and {% url %} keep
working everywhere, including inside third-party apps. Links within the
current host stay path-relative, exactly as Django produces them; links to
views on another host come back as absolute URLs. Configuration is one
declarative host map in settings plus one middleware.
It is the ecosystem's replacement for django-hosts, whose parallel reverse
API forces invasive changes across templates and Python code and cannot fix
third-party apps that call django.urls.reverse().
Installation
pip install django-hostmap
INSTALLED_APPS = [
# ...
"hostmap",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"hostmap.middleware.HostmapMiddleware", # before CommonMiddleware
"django.middleware.common.CommonMiddleware",
# ...
]
ROOT_URLCONF remains required (Django needs it at startup) and should point
at the default entry's URLconf (hostmap.W003 warns on mismatch).
The host map
HOSTMAP = {
"www": {"subdomain": "www", "urlconf": "config.urls.www"},
"api": {"subdomain": "api", "urlconf": "config.urls.api"},
"apex": {"host": "example.com", "redirect_to": "www"},
}
HOSTMAP_PARENT_DOMAIN = "example.com"
HOSTMAP_DEFAULT = "www"
With www active:
| Call | Returns |
|---|---|
reverse("blog:index") |
/blog/ (byte-identical to stock Django) |
reverse("api:user-detail", args=[7]) |
https://api.example.com/users/7/ |
{% url "api:user-detail" 7 %} |
the absolute URL, no template changes |
Same-host links stay relative; cross-host links come back absolute. Nothing at the call site changes.
Settings
| Setting | Default | Description |
|---|---|---|
HOSTMAP |
{} |
The host map. Empty map disables all behaviour |
HOSTMAP_PARENT_DOMAIN |
"" |
Domain joined to subdomain entries |
HOSTMAP_DEFAULT |
"" |
Entry for unmatched hosts and out-of-request reversing |
HOSTMAP_PATCH_REVERSE |
True |
Make stock reverse() / {% url %} host-aware |
HOSTMAP_SCHEME |
"https" |
Scheme for cross-host absolute URLs |
HOSTMAP_PORT |
"" |
Port appended to all generated hosts |
HOSTMAP_UNMATCHED |
"default" |
"default" routes unmatched hosts to the default entry; "reject" returns 404 |
HOSTMAP_REDIRECT_PERMANENT |
True |
redirect_to entries use 301, else 302 |
HOSTMAP_ALLOW_UNTESTED_DJANGO |
False |
Explicit opt-out that downgrades the untested-Django reverse-patch refusal from hostmap.E009 to hostmap.W005 |
Explicit API
For code that needs an absolute URL regardless of the active host (emails, Celery tasks, API payloads, webhooks):
from hostmap.urls import reverse, build_absolute_uri, use_host
build_absolute_uri("api:user-detail", args=[7]) # always absolute
with use_host("api"):
reverse("user-detail", args=[7]) # api active
with use_host(host="acme.example.com"): # a wildcard host
reverse("dashboard")
Development story
Modern browsers resolve *.localhost to loopback (RFC 6761), so the whole
map works locally with no /etc/hosts edits:
HOSTMAP_PARENT_DOMAIN = "localhost"
HOSTMAP_SCHEME = "http"
HOSTMAP_PORT = "8000"
ALLOWED_HOSTS = [".localhost"]
Behind a proxy
Host routing is only as good as the Host header that reaches Django. Behind a
reverse proxy, either the proxy passes Host through unchanged or the
deployment sets USE_X_FORWARDED_HOST = True with the proxy sending
X-Forwarded-Host. Misrouted hosts behind nginx are otherwise the first
support issue.
Escape hatch
HOSTMAP_PATCH_REVERSE = False degrades cleanly to routing-only: the stock
reverse() is left untouched (cross-host links regress to paths), routing
keeps working, and the explicit API still works. Set it if a Django upgrade
lands before a compatible django-hostmap release; the startup self-test and
hostmap.E009/hostmap.W004 catch a Django upgrade running ahead of the
package's tested ceiling before anything breaks. With the patch active
(the default), an unverified Django version is hostmap.E009, an Error
that fails startup, since the patch hooks a private resolver seam that has
not been verified there; with the patch off, the same condition is only
hostmap.W004, a Warning, since routing-only operation never touches that
seam.
Diagnostics
manage.py hostmap # print the resolved map for the current settings
Licence
MIT. See LICENSE.
Metadata
Release files for django-hostmap 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_hostmap-1.1.1.tar.gz | 33.9 kB | With provenance | Details for django_hostmap-1.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_hostmap-1.1.1-py3-none-any.whl | Python 3 | none | any | Details for django_hostmap-1.1.1-py3-none-any.whl |
Total release size: 60.4 kB
Release files / django_hostmap-1.1.1.tar.gz
| Download URL | django_hostmap-1.1.1.tar.gz |
|---|---|
| Size | 33.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8e014e0b4a3de9f39d64d21dac63e9a957de3d9fabe44cb465452d22a985a6f6
|
|
BLAKE2b-256 checksum How to use checksums |
0f0dac2fbafa874f53a2e27c079b926652f3e618102f9682f91320b5fe9f8f72
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logRelease files / django_hostmap-1.1.1-py3-none-any.whl
| Download URL | django_hostmap-1.1.1-py3-none-any.whl |
|---|---|
| Size | 26.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a7b163b8eab84fe911bad3ceee4a7f7af4777174040a6ee7d26b934aa1f6f7ff
|
|
BLAKE2b-256 checksum How to use checksums |
796450383331575a39a278c7956f71c36b7a0f876f9449abcba0fa3f36c3409b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logSecurity for django-hostmap 1.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for django_hostmap-1.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.