Run Multiple Independent Sites from One MODX 3 Installation Using Contexts
Configure MODX 3.x to run multiple independent websites from one installation using Contexts, with isolated resource trees, settings and ACLs per domain.
21 Aug 2025, 09:38 UTC

Running example.com and shop.example.com from the same MODX 3 codebase is the practical problem contexts are built for. The useful takeaway is that a single MODX core can serve multiple brands or sub-sites with isolated resource trees, settings and access policies, provided HTTP_HOST is mapped to a context key and each context owns its own site_start, site_url and cache.
Desired outcome
One MODX 3.1+ installation serving two or more distinct websites. Each site has its own resource tree, templates, system settings and Manager access. The core, plugins and Extras are shared, but front-end rendering and editor permissions are separated by context.
A context in MODX is a named execution environment. It stores a set of system settings, a default resource and access policies. Resources belong to a context via the context field, so the same database can hold multiple trees without collision.
Prerequisites
- MODX 3.1+ installed and reachable via Manager. Admin user with sufficient privileges to create contexts and system settings.
- Web server configured with virtual hosts or domain aliases pointing to the same MODX document root. Example hosts: example.com and shop.example.com.
- Database user able to read/write modx_context, modx_context_setting and modx_site_content.
- Staging copy for testing plugins. A fatal error in a shared plugin affects all contexts.
Focused procedure
Create the context and its settings
In Manager go to System > Contexts > New Context. Use a short key without spaces, e.g. key = shop, name = Shop Site. Keep the key stable; it is used for cache paths and routing.
Add context-specific settings under the context. Critical settings are:
- site_url = https://shop.example.com/
- site_start = resource id of the shop home page
- error_page = resource id for 404
- site_name = Shop
These override global settings only when the request runs in the shop context.
Build a resource tree for the context
Create a root resource with Context = shop. Do not reuse resources from the web context. Assign a separate template and Template Variables as needed. TVs are global definitions but values are per resource, so isolation is achieved by keeping resources in different contexts.
Route requests to the correct context
MODX determines the active context early in the request. The simplest reliable method is HTTP_HOST matching via a system event plugin. Create a plugin on OnHandleRequest with system event priority early.
<?php
$host = $_SERVER['HTTP_HOST'] ?? '';
if ($host === 'shop.example.com') {
$modx->setPlaceholders(['modx.context' => 'shop']);
}
// Context switching is also possible via ?context=shop, avoid exposing this publicly.
?>Place the file in core/components/ and load it via the Plugin manager. Requires Manager access to create the plugin and clear cache.
Alternative server-level routing. For Apache, in the virtual host for shop.example.com ensure DocumentRoot points to the same MODX root and no extra rewrite overrides context detection. For Nginx, ensure fastcgi_param HTTP_HOST is passed unchanged.
Risks: misconfigured server variables cause wrong context loading. Session cookies can leak across contexts if session_cookie_domain is too broad. Set session_cookie_domain and session_cookie_path per context if editors work on both sites.
Separate Manager access
Create a user group for shop editors. In Access Control Lists, grant Resource Group access only to resources where context = shop. Use the Manager Switch Context dropdown to verify the tree changes.
Expected checks
- Front-end check: request https://shop.example.com/ loads the resource defined as site_start for context shop, and https://example.com/ loads the web context home.
- Settings override check: output [[++site_name]] and [[++site_url]] on each front-end. Values should reflect the active context.
- Resource isolation check: create a test resource in each context with a unique Template Variable. Confirm values are stored on the correct resource and do not appear in the other context tree.
- Cache separation check: after front-end requests, core/cache/ contains distinct subdirectories named after context keys, e.g. core/cache/shop/ and core/cache/web/. Deleting one does not clear the other.
- Permission check: log in as a shop editor and confirm Manager shows only shop resources.
Recovery options and limitations
If a context becomes inaccessible, disable the virtual host for that domain to fall back to the default web context. Reset context settings via Manager > System > Contexts > shop > Settings, or directly in the modx_context_setting table where context_key = 'shop'.
Rebuild context cache by removing core/cache/shop/ and refreshing the front-end. This forces MODX to regenerate context-specific cache files.
Shared core means a plugin or Extra installed globally affects all contexts. Package installation is global in MODX 3.x. Test Extras in staging per context before production.
MODX 3.x changed context initialization order compared to 2.x. Legacy plugins using OnInitCulture may behave differently. Review plugin events for compatibility.
Database backup is the final recovery path for context corruption. Restore modx_context, modx_context_setting and modx_site_content rows from a known good backup.
Limitations to plan for: no per-context database separation, no per-context core version, and session handling requires explicit configuration to avoid cross-context login leakage.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.