Multi-Site Installation¶
django CMS uses Django’s sites framework to manage multi-site installations.
There are three ways to run multiple sites with django CMS:
The classic (and most common) approach: separate settings files with distinct
SITE_IDvalues.Since django CMS 5.1: shared settings without a
SITE_IDsetting; the active site is resolved by Django’s Sites framework based on the request’s hostname.Since django CMS 5.1: a custom site middleware; if it sets
request.site, django CMS will honor it.
Approach 1: Separate settings with SITE_ID (classic)¶
Operate multiple websites in the same virtualenv by using copies of manage.py and wsgi.py,
plus per-site settings and URL configuration. You can use the same database for all sites
or separate databases for stricter isolation.
A common pattern is to keep shared configuration in a base settings file (e.g. my_project/base_settings.py),
which is imported from each site-specific settings file and overridden as needed. Optionally, import
local, unversioned overrides at the end (e.g. secrets, database credentials).
Copy and edit
wsgi.pyandmanage.py(e.g. towsgi_second_site.pyandmanage_second_site.py) and pointDJANGO_SETTINGS_MODULEto the site’s settings module:os.environ.setdefault( "DJANGO_SETTINGS_MODULE", "my_project.settings_second_site" )
In each site-specific settings module, import the shared base and override site-specific values:
# my_project/settings_second_site.py from .base_settings import * SITE_ID: int = 2 ROOT_URLCONF: str = 'my_project.urls_second_site' # other site-specific settings… from .settings_local import * # optional, not under version control
Configure your web server so the site uses its dedicated
wsgi_*.py(e.g.wsgi_second_site.py).
Note
You do not have to include Django’s admin on all sites. You can run a site without admin by not exposing the admin URLs in that site’s URL configuration. This is useful if you only want to expose the frontend editing and preview endpoints on certain sites, e.g., for security reasons.
The toolbar will only be available on sites which do have admin endpoints.
Approach 3 (since 5.1): Custom site middleware¶
You can provide middleware that sets request.site. django CMS will respect this attribute as the
current site for the request, regardless of whether SITE_ID is configured.
Example middleware:
# my_project/middleware.py
from django.contrib.sites.models import Site
class CurrentSiteFromHostMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
host = request.get_host().split(':')[0]
try:
request.site = Site.objects.get(domain=host)
except Site.DoesNotExist:
request.site = None # optional fallback
return self.get_response(request)
Register the middleware (early in the stack is recommended):
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'my_project.middleware.CurrentSiteFromHostMiddleware',
# …
]
Apphooks and per-request site resolution¶
Apphooks are directly linked into Django’s URL patterns. Since a common settings file results in common
URL patterns, django CMS adds apphooks of all sites into the same URL pattern if the settings contain
no SITE_ID.
To isolate apphooks, the cms_site_filter() decorator is automatically added
to the apphook view functions, which ensures that an apphook is only displayed on its designated site.
However, when multiple sites are configured, this can lead to apphooks shadowing each other if they share the same URL path. It is important to manage URL patterns carefully to avoid conflicts between apphooks across different sites.
Language and URL configuration tips¶
Ensure each domain has a matching
Sitewith the exactdomainvalue.Configure language sets in
CMS_LANGUAGESper site or via the'default'block as needed.With approaches 2 and 3, the active site is determined per request; for batch tasks without requests, pass an explicit site context or temporarily set
SITE_ID.