Django生产环境不使用django-hosts配置多应用子域名方法
Django多应用子域名配置方案(DigitalOcean + Gunicorn + Nginx生产环境)
此前django-hosts方案静态文件失效的根因
这个问题和django-hosts本身的功能无关,是生产环境配置遗漏导致的,本地开发环境Django会自动全局托管静态资源所以不会触发:
settings.py中STATIC_URL、MEDIA_URL使用了相对路径(如/static/),子域名发起请求时会默认从当前子域名路径下查找静态资源,导致路径匹配失败,需要改成带主域名的绝对路径,如https://your-main-domain.com/static/- Nginx配置仅为主域名添加了静态资源映射规则,所有子域名的server块没有匹配静态资源路径的location配置,请求直接被转发到Gunicorn,而生产环境Django默认不处理静态资源请求,直接返回404
- 子域名路由规则优先级过高,拦截了
/static/、/media/开头的请求,转发到了子应用路由表
除django-hosts外的生产环境可行方案
以下方案均在DigitalOcean Droplet + Gunicorn + Nginx架构下实测可用,无第三方包依赖冲突问题:
方案1:Nginx层路由转发(最稳定,零Django侧侵入)
所有路由判断逻辑放在Nginx层处理,静态资源直接由Nginx返回,完全不经过Django,不会出现资源定位问题:
- 为每个子应用单独编写独立的urlconf文件,例如
blog/urls_sub.py、shop/urls_sub.py,仅存放对应子域名下的路由规则 - 为每个子域名单独编写Nginx server块,
server_name字段绑定对应子域名,所有server块统一配置静态资源映射规则,指向collectstatic执行后生成的静态文件目录 - 在每个子域名的反向代理配置中,添加自定义请求头传递当前子域名对应的urlconf路径,配置示例:
location / { proxy_set_header Host $host; 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; # 传递对应子应用的urlconf proxy_set_header X-Urlconf blog.urls_sub; proxy_pass http://127.0.0.1:8000; # 指向Gunicorn监听的本地端口 }
- 在Django项目中编写轻量中间件,读取Nginx传递的请求头动态切换urlconf,不需要安装任何第三方包:
class SubdomainRouterMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): urlconf = request.META.get("HTTP_X_URLCONF") if urlconf: request.urlconf = urlconf return self.get_response(request)
- 将该中间件添加到
settings.py的MIDDLEWARE列表顶部,优先级高于框架自带的CommonMiddleware即可。
方案2:多Gunicorn实例物理隔离
如果各子应用耦合度极低,可以为每个子应用启动独立的Gunicorn进程,监听不同本地端口,实现服务级隔离:
- 主站Gunicorn监听127.0.0.1:8000,blog子应用监听127.0.0.1:8001,shop子应用监听127.0.0.1:8002
- Nginx层每个子域名的server块直接反向代理到对应端口的Gunicorn实例,不需要额外传递路由头,启动子应用Gunicorn时直接指定对应配置即可,示例启动命令:
gunicorn --chdir /path/to/your/project myproject.wsgi:application \ --bind 127.0.0.1:8001 \ --env DJANGO_SETTINGS_MODULE=myproject.settings
- 可以通过systemd为每个Gunicorn实例编写独立的服务文件,方便单独重启、监控资源占用,静态资源规则和单站点配置完全一致,不会出现路径匹配问题。
方案3:原生中间件实现轻量子域名路由
如果不想调整Nginx配置,可以自行实现十几行代码的子域名中间件,替代django-hosts,逻辑更可控:
- 编写中间件直接读取请求Host头判断所属子域名,动态切换urlconf,示例代码:
class NativeSubdomainMiddleware: def __init__(self, get_response): self.get_response = get_response self.subdomain_map = { "blog.yourdomain.com": "blog.urls", "shop.yourdomain.com": "shop.urls" } def __call__(self, request): request_host = request.get_host().split(":")[0] # 剔除端口号 if request_host in self.subdomain_map: request.urlconf = self.subdomain_map[request_host] return self.get_response(request)
- 将该中间件放到MIDDLEWARE列表顶部,同时在
ALLOWED_HOSTS中添加所有子域名,STATIC_URL配置为带主域名的绝对路径,Nginx层为所有子域名添加静态资源映射即可正常运行。
部署前记得在DigitalOcean域名解析面板为所有子域名添加A记录,指向Droplet公网IP;HTTPS证书可以申请通配符证书,一次性覆盖主域名和所有子域名,不需要单独为每个子域名签发。
内容的提问来源于stack exchange,提问作者Ojo Philip Odeniyi
相关产品推荐
相关产品推荐

