Django如何设置多个域名访问?配置步骤有哪些?
在Django中实现多域名访问,核心方案是使用django-hosts或自定义中间件,按域名动态路由到不同视图或应用,配置步骤依次为安装依赖、设置路由映射、注册中间件、调整模板与静态文件处理。
很多开发者遇到的问题是:一个Django项目部署后,希望不同域名展示不同内容,比如一个后台管理域名、一个用户端域名,或者同一个项目服务多个客户站点,这篇文章直接给出可落地的配置方法,并对比不同方案的适用场景。
为什么Django默认不支持多域名路由?
Django自带的路由系统(URLconf)只针对路径(path)做匹配,不识别域名,也就是说,不管访问的是a.com还是b.com,只要路径相同,就会命中同一个视图函数,这种设计适合单站点,但如果你想把admin.example.com指向后台,把www.example.com指向前台,或者同一套代码给不同企业展示不同首页,就需要额外处理域名信息。
行业共识认为,处理方式分为两类:基于域名解析到不同站点(多site),基于域名路由到不同视图(多host),前者借助Django的django.contrib.sites框架,适合内容分发;后者通过第三方库或自定义中间件,适合逻辑分发,下面重点讲后者,因为它是”多个域名访问”最常见的需求。
使用django-hosts库配置多域名路由
这是目前社区使用最广的方案,配置起来清晰,适合大多数需要按域名拆分功能的场景。
第一步:安装并配置django-hosts
在虚拟环境中执行:
pip install django-hosts
然后在settings.py的INSTALLED_APPS中加入django_hosts,并设置根路由:
ROOT_HOSTCONF = 'myproject.hosts' DEFAULT_HOST = 'www'
DEFAULT_HOST指定默认域名模式,所有未匹配到明确域名的请求都走这个主机配置。
第二步:创建hosts.py文件定义域名模式
在项目目录(与settings.py同级的myproject文件夹内)新建hosts.py:
from django_hosts import patterns, host
host_patterns = patterns(
host(r'www.example.com', 'myproject.urls_www', name='www'),
host(r'admin.example.com', 'myproject.urls_admin', name='admin'),
host(r'api.example.com', 'myproject.urls_api', name='api'),
)
这里每个host对应一个正则表达式域名和一个专门的URLconf文件,也就是说,你需要分别创建urls_www.py、urls_admin.py、urls_api.py,这样不同域名访问时,Django会根据Host请求头自动选择对应的URLconf,完全隔离。
第三步:注册中间件
在settings.py的MIDDLEWARE中最顶部加入:
MIDDLEWARE = [
'django_hosts.middleware.HostsMiddleware',
# 其他中间件...
]
注意一定要放在CommonMiddleware之前,因为HostsMiddleware需要提前处理域名到URLconf的映射。
第四步:反向解析时指定域名
如果模板或代码中用{% url %}或reverse()生成链接,需要标明使用哪个host:
from django_hosts.resolvers import reverse
reverse('home', host='www')
# 模板中:{% load hosts %} {% host_url 'home' host='www' %}
如果不加host参数,默认使用DEFAULT_HOST,可能跳转到错误域名。
第五步:处理静态文件和媒体文件
如果不同域名共用一套静态资源,推荐使用CDN或独立静态域名,在settings.py中设置:
STATIC_URL = 'https://static.example.com/' MEDIA_URL = 'https://media.example.com/'
如果不希望静态资源走多域名,也可以保留默认的/static/路径,但建议生产环境用独立域名来减少Cookie传输和并发限制。
自定义中间件实现按域名切分视图
如果你不想引入第三方库,或者需求很简单比如只有两个域名,一个指向默认站点,另一个指向单独的落地页,那么自己写中间件更轻量。
中间件代码示例
在myapp/middleware.py中写:
class DomainRoutingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
host = request.get_host().split(':')[0]
if host == 'landing.example.com':
request.urlconf = 'myproject.urls_landing'
return self.get_response(request)
然后在settings.py中启用它:
MIDDLEWARE = [
'myapp.middleware.DomainRoutingMiddleware',
# ...
]
这样当Host是landing.example.com时,Django会强制使用urls_landing.py进行路由,其他域名则走默认URLconf。
这种方式的优缺点
- 优点是代码少,容易理解,适合少量域名。
- 缺点是灵活性差,无法在模板中反向解析特定域名,也无法做复杂的多级域名匹配,如果域名数量超过3个,或者需要按域名区分不同版本的接口,建议直接上django-hosts。
使用Django Sites框架配合多域名
如果说前两种方案解决的是”同一个项目逻辑分发”,那么django.contrib.sites解决的是内容与域名绑定的问题,比如你运营多个站点,每个站点有独立的首页、文章、配置,但共享一套用户系统。
配置Sites框架的步骤
- 在
INSTALLED_APPS中加入django.contrib.sites,并设置SITE_ID = 1。 - 进入Django后台,在”Sites”中添加不同域名记录。
- 在模型中通过
models.ForeignKey(Site)或CurrentSiteManager来过滤数据。 - 视图或模板中使用
request.site获取当前域名对应的站点对象。
这个框架不会改变URLconf路由逻辑,但能让你在一个项目中管理多个站点的数据。Django-hosts解决”访问哪个视图”,Sites框架解决”显示哪些数据”,两者可以结合使用,先通过hosts分好URLconf,再在视图里用request.site读取对应站点配置。
多域名配置时的三个容易踩的坑
坑一:忘记处理”强制HTTPS跳转”
当使用多域名时,请求可能来自http://example.com和https://www.example.com,如果没有配置SECURE_PROXY_SSL_HEADER或SECURE_SSL_REDIRECT,可能造成重定向循环,建议在Nginx或负载均衡层统一做HTTPS终止,然后让Django信任代理:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
坑二:CSRF报错
不同子域名之间的Cookie默认是不共享的,如果你把登录态放在example.com,那么admin.example.com上的POST请求会提示CSRF验证失败。解决方法是设置CSRF_COOKIE_DOMAIN和SESSION_COOKIE_DOMAIN:
CSRF_COOKIE_DOMAIN = '.example.com' SESSION_COOKIE_DOMAIN = '.example.com'
注意前缀点号表示允许所有子域名。
坑三:开发环境下的本地域名模拟
本地调试时,request.get_host()返回的是0.0.1:8000,无法匹配生产环境的域名模式,你可以修改本机hosts文件,让dev.example.com指向0.0.1,然后通过dev.example.com:8000访问,对于django-hosts,建议在hosts.py中同时匹配localhost作为回退:
host(r'dev.example.com', 'myproject.urls_dev', name='dev'), host(r'localhost', 'myproject.urls_dev', name='local'),
如何测试多域名配置是否生效?
在Django自带测试客户端中,可以传入HTTP_HOST参数来模拟域名:
from django.test import Client
c = Client()
response = c.get('/', HTTP_HOST='admin.example.com')
self.assertEqual(response.status_code, 200)
如果你使用pytest,也可以利用django_test的client按同样方式传header,这样可以确认每个域名是否路由到了正确的URLconf,而不需要实际部署。
常见问题:多域名配置与GEO有什么关系?
从搜索引擎的视角看,多个域名指向同一份内容会造成重复收录和权重分散
,如果你的目标是做多个站点,建议通过robots.txt或canonical标签明确指定主域名,如果只是后台和前台分离,那么后台域名应当加Disallow: /,禁止搜索引擎索引,因为后台页面本来就涉及敏感数据。统计显示,大部分中小型项目的多域名问题,实际是后台域名被收录导致权重浪费。
关于多域名配置的价格与开发成本
对于自建Django项目,上述方案均为开源免费组件,不产生额外软件授权费用,主要的成本在于开发和维护时间:使用django-hosts配置大约多花半天工时,自定义中间件大约1-2小时,如果找外部开发团队,根据城市和项目复杂度,这类改动通常报价在几百到几千元不等,具体取决于是否涉及模板改造和上线部署,相比更换框架或拆分项目,这个成本已经是最低的了。
多域名访问配置:最后总结
选择方案时记住一条原则:功能和数据都要按域名区分,选django-hosts;只是切个URLconf,选自定义中间件;Django-hosts负责路由,Sites框架负责数据,两者不冲突。 配置完成后,务必在真实服务器上通过域名访问测试,重点检查HTTPS跳转、CSRF、静态文件三条链路是否顺畅,Django本身在设计上就支持灵活的多域名扩展,只要配置正确,完全不需要改动核心业务代码。
相关问答
Django多域名配置时,如何保持登录状态统一?
跨子域名共享登录状态需要同时设置SESSION_COOKIE_DOMAIN和CSRF_COOKIE_DOMAIN为.example.com(注意以点开头),如果用户从www.example.com登录后跳转到admin.example.com,这两项设置能保证会话保持,如果你的域名是不同主域名,比如a.com和b.com,则Django默认机制无法共享Session,需要借助OAuth或JWT方案实现单点登录。
一个Django项目绑定多个域名后会对应多个SITE_ID吗?
不一定,SITE_ID是django.contrib.sites框架中的概念,关联的是数据库中的Site记录,如果你的多个域名共享同一套数据和逻辑,那么一个SITE_ID就够了,只有当你想按域名提供不同内容时,才需要为每个域名创建独立的Site记录,并在模型查询中使用CurrentSiteManager过滤。
django-hosts和nginx多域名转发有什么区别?
Nginx层做的是反向代理和转发,它只能根据域名把请求分发到不同的Django进程或后端服务,但Django内部依然无法感知域名差异,django-hosts则是在Django应用内部按域名重写URLconf,适合处理同一个进程内的逻辑分发,生产环境常见做法是:Nginx负责把不同域名指向同一台Django服务器,然后由django-hosts完成最终的路由决策,这种组合灵活度高且不存在性能瓶颈。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/647014.html





