Compare Versions
Version 1
December 19, 2025, 7:33 p.m.
arada
Version 2
December 20, 2025, 1:26 a.m.
arada
| Version 1 | Version 2 | ||||
|---|---|---|---|---|---|
| 7 | pip install django-hosts | 7 | pip install django-hosts | ||
| 8 | ``` | 8 | ``` | ||
| 9 | 9 | ||||
| n | 10 | ## Settings | n | 10 | ## 1. Settings |
| 11 | ```python | 11 | ```python | ||
| n | n | 12 | # settings.py | ||
| 12 | INSTALLED_APPS = ["django_hosts", ...] | 13 | INSTALLED_APPS = ["django_hosts", ...] | ||
| 13 | MIDDLEWARE = [ | 14 | MIDDLEWARE = [ | ||
| n | 14 | "django_hosts.middleware.HostsRequestMiddleware", # First | n | 15 | "django_hosts.middleware.HostsRequestMiddleware", # MUST be first |
| 15 | ... | 16 | ... | ||
| n | 16 | "django_hosts.middleware.HostsResponseMiddleware", # Last | n | 17 | "django_hosts.middleware.HostsResponseMiddleware", # MUST be last |
| 17 | ] | 18 | ] | ||
| n | n | 19 | |||
| 20 | # django-hosts configuration | ||||
| 18 | ROOT_HOSTCONF = "config.hosts" | 21 | ROOT_HOSTCONF = "config.hosts" | ||
| n | 19 | DEFAULT_HOST = "api" | n | 22 | DEFAULT_HOST = "api" # Default subdomain |
| 23 | PARENT_HOST = "example.com" # Parent domain (IMPORTANT for URL generation) | ||||
| 24 | |||||
| 25 | # Login URL for admin subdomain | ||||
| 26 | LOGIN_URL = "/login/" # Since admin is at root, login is at /login/ | ||||
| 20 | ``` | 27 | ``` | ||
| 21 | 28 | ||||
| n | 22 | ## Hosts Config | n | 29 | ## 2. Hosts Config |
| 23 | ```python | 30 | ```python | ||
| 24 | # config/hosts.py | 31 | # config/hosts.py | ||
| n | n | 32 | from django.conf import settings | ||
| 25 | from django_hosts import patterns, host | 33 | from django_hosts import patterns, host | ||
| 26 | 34 | ||||
| 27 | host_patterns = patterns("", | 35 | host_patterns = patterns("", | ||
| 31 | ) | 39 | ) | ||
| 32 | ``` | 40 | ``` | ||
| 33 | 41 | ||||
| n | 34 | ## URL Configs | n | 42 | ## 3. Admin URLs |
| 35 | ```python | 43 | ```python | ||
| 36 | # config/admin_urls.py | 44 | # config/admin_urls.py | ||
| n | 37 | urlpatterns = [path("", admin.site.urls)] | n | 45 | from django.contrib import admin |
| 46 | from django.urls import path, include | ||||
| 47 | from django.views.generic import RedirectView | ||||
| 48 | |||||
| 49 | urlpatterns = [ | ||||
| 50 | # Prevent /admin/ loops (redirect to root) | ||||
| 51 | path("admin/", RedirectView.as_view(pattern_name="admin:index", permanent=True)), | ||||
| 52 | |||||
| 53 | # Admin at root | ||||
| 54 | path("", admin.site.urls), | ||||
| 55 | ] | ||||
| 38 | ``` | 56 | ``` | ||
| 39 | 57 | ||||
| n | 40 | ## Nginx | n | 58 | ## 4. Nginx |
| 41 | ```nginx | 59 | ```nginx | ||
| 42 | server { | 60 | server { | ||
| 43 | server_name admin.example.com; | 61 | server_name admin.example.com; | ||
| 44 | location / { | 62 | location / { | ||
| 45 | proxy_pass http://127.0.0.1:8000; | 63 | proxy_pass http://127.0.0.1:8000; | ||
| 46 | proxy_set_header Host $host; # Critical! | 64 | proxy_set_header Host $host; # Critical! | ||
| n | n | 65 | proxy_set_header X-Real-IP $remote_addr; | ||
| 66 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||
| 67 | proxy_set_header X-Forwarded-Proto $scheme; | ||||
| 47 | } | 68 | } | ||
| 48 | } | 69 | } | ||
| 49 | ``` | 70 | ``` | ||
| 50 | 71 | ||||
| 51 | ## How It Works | 72 | ## How It Works | ||
| n | 52 | 1. Request: https://admin.example.com/users/ | n | 73 | 1. Request: https://admin.example.com/ |
| 53 | 2. Nginx forwards with Host: admin.example.com | 74 | 2. Nginx forwards with Host: admin.example.com | ||
| 54 | 3. django-hosts reads Host header | 75 | 3. django-hosts reads Host header | ||
| 55 | 4. Routes to config.admin_urls | 76 | 4. Routes to config.admin_urls | ||
| n | 56 | 5. Serves admin panel | n | 77 | 5. Serves admin panel at root |
| 78 | |||||
| 79 | ## Preventing Redirect Loops | ||||
| 80 | |||||
| 81 | The redirect loop `/admin/admin/login/` happens when: | ||||
| 82 | - Admin is served at root of subdomain | ||||
| 83 | - But Django still tries to redirect to `/admin/` | ||||
| 84 | |||||
| 85 | **Fixes:** | ||||
| 86 | 1. Set `LOGIN_URL = "/login/"` in settings | ||||
| 87 | 2. Set `PARENT_HOST = "example.com"` for proper URL generation | ||||
| 88 | 3. Add redirect from `/admin/` to `/` in admin_urls.py | ||||
| 57 | 89 | ||||
| 58 | ## Key Points | 90 | ## Key Points | ||
| t | 59 | - One Django project, multiple subdomains | t | 91 | - Admin served at admin.example.com/ (root) |
| 60 | - Nginx passes Host header | 92 | - Login at admin.example.com/login/ (NOT /admin/login/) | ||
| 61 | - django-hosts routes by subdomain | 93 | - PARENT_HOST required for django-hosts URL generation | ||
| 62 | - Separate URL configs per subdomain | 94 | - Redirect /admin/ to / prevents old URL loops | ||
| 63 | 95 | ||||
| 64 | ## Allowed Hosts | 96 | ## Allowed Hosts | ||
| 65 | ```python | 97 | ```python | ||
--- Version 1+++ Version 2@@ -7,21 +7,29 @@ pip install django-hosts
```
-## Settings
+## 1. Settings
```python
+# settings.py
INSTALLED_APPS = ["django_hosts", ...]
MIDDLEWARE = [
- "django_hosts.middleware.HostsRequestMiddleware", # First
+ "django_hosts.middleware.HostsRequestMiddleware", # MUST be first
...
- "django_hosts.middleware.HostsResponseMiddleware", # Last
+ "django_hosts.middleware.HostsResponseMiddleware", # MUST be last
]
+
+# django-hosts configuration
ROOT_HOSTCONF = "config.hosts"
-DEFAULT_HOST = "api"
+DEFAULT_HOST = "api" # Default subdomain
+PARENT_HOST = "example.com" # Parent domain (IMPORTANT for URL generation)
+
+# Login URL for admin subdomain
+LOGIN_URL = "/login/" # Since admin is at root, login is at /login/
```
-## Hosts Config
+## 2. Hosts Config
```python
# config/hosts.py
+from django.conf import settings
from django_hosts import patterns, host
host_patterns = patterns("",
@@ -31,35 +39,59 @@ )
```
-## URL Configs
+## 3. Admin URLs
```python
# config/admin_urls.py
-urlpatterns = [path("", admin.site.urls)]
+from django.contrib import admin
+from django.urls import path, include
+from django.views.generic import RedirectView
+
+urlpatterns = [
+ # Prevent /admin/ loops (redirect to root)
+ path("admin/", RedirectView.as_view(pattern_name="admin:index", permanent=True)),
+
+ # Admin at root
+ path("", admin.site.urls),
+]
```
-## Nginx
+## 4. Nginx
```nginx
server {
server_name admin.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host; # Critical!
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
## How It Works
-1. Request: https://admin.example.com/users/
+1. Request: https://admin.example.com/
2. Nginx forwards with Host: admin.example.com
3. django-hosts reads Host header
4. Routes to config.admin_urls
-5. Serves admin panel
+5. Serves admin panel at root
+
+## Preventing Redirect Loops
+
+The redirect loop `/admin/admin/login/` happens when:
+- Admin is served at root of subdomain
+- But Django still tries to redirect to `/admin/`
+
+**Fixes:**
+1. Set `LOGIN_URL = "/login/"` in settings
+2. Set `PARENT_HOST = "example.com"` for proper URL generation
+3. Add redirect from `/admin/` to `/` in admin_urls.py
## Key Points
-- One Django project, multiple subdomains
-- Nginx passes Host header
-- django-hosts routes by subdomain
-- Separate URL configs per subdomain
+- Admin served at admin.example.com/ (root)
+- Login at admin.example.com/login/ (NOT /admin/login/)
+- PARENT_HOST required for django-hosts URL generation
+- Redirect /admin/ to / prevents old URL loops
## Allowed Hosts
```python