Login
Neu hier? Registrieren.
Multiple domains / sub sites with MODx
In this tutorial, I would like to show how multiple domains can be managed with one MODx installation (one database and one manager). You need only a few plugins, snippets, chunks, a template variable (TV) an some entries to the .htaccess. For advanced users or for lazy people I also will show how to use only one template for handling individual designs for each domain.
Requirements and Limitations
There are basically no restrictions, one only needs to be a bit careful in the appointment of its documents or template folders. The following conditions must also be met:
- mod_rewrite loaded. Without mod_rewrite you don`t have to read on.
- Wayfinder 2.5: If you want to display the homepage in the same level as the other level one menu items, you will need Wayfinder 2.5 (currently beta, but stable imho). Reason is that with this version weblinks can be marked active when pointing to an internal ressource.
Approach
Inspired by a thread in the modxcms.com forum, some already existing ideas and of course a current project I created a set of .htaccess entries, plugins, snippets and chunks to allow the following:
- Different domains with one MODx installation
- No hacks of the core (thus 0.9x-compatible)
- One folder per domain in the manager tree
- Individual 404 error page per domain with correct 404-header
- Individual links /aliases including natural directory structure (www.domain1.com/products/cars, www.domain2.com/products/cars/ etc.)
- Option to use content domain-wide with the getField snippet
- No need for multiple snippet calls (Wayfinder,Ditto etc.) using the UltimateParent snippet
- Purpose: Multilingual websites (eg .de and .com), several customers in one installation, editorial content on multiple domains, SEO fun etc...
1. Scenario and initial preparations
First, we set up our SEF URL settings:
Tools-> Configuration-> User-Friendly URLs:
- User-friendly URLs, use: yes
- Suffix:. Html
- Use user-friendly alias path information: yes
- Allow duplicate aliases: yes
- Automatically generated aliases: yes
Then we define a template variable "host_url" of type "text" and assign it to our template:
What is important is the name "host_url" and the default property "@inherit".
Next, we define a Snippet with the name "http_host" with the following contents:
<?php return $_SERVER['HTTP_HOST']; ?>
We want to create three different domains:
- www.domain1.com
- www.domain2.com
- www.domain3.com
We put the three folders for the three domains in the manager to:
root
+ www.domain1.com
+ www.domain2.com
+ www.domain3.com
+ general content
- general Home
- 404
Additionally, a folder for domain-wide content (if desired) and a general home and error page, which is also in the configuration specified as homepage and error page. This is only a fallback solution, I have not yet needed.
Important: The folder must have the name of the domain as pagetitle, with www. Or all without www, but then you would need to adjust the .htaccess later. It is also possible, but that I will not go into first.
Important: As the "alias" and the TV "host_url" you set a unique name. For example, "domain1-com". It is absolutely important that this name doesn't occur anywhere else as folder name or alias. My example should be sufficient and is also used by me.
So:
- pagetitle: www.domain1.com
- alias: domain1-com
- TV host_url: domain1-com
You will immediately see what the need is and why the same value twice.
2. The .htaccess
Okay, now comes the magic. For each domain, put the following lines before the actual mod_rewrite, but (of course) after the RewriteEngine ON. So:
RewriteEngine On RewriteBase / # First be sure to rewrite the request to a unique domain RewriteCond %{HTTP_HOST} . RewriteCond %{HTTP_HOST} ^domain1\.com [NC] RewriteCond %{HTTP_HOST} !^www\.domain1\.com [NC] RewriteRule (.*) http://www.domain1.com/$1 [R=301,L] # rewrite the homepage of each domain to index.html RewriteRule ^domain1-com\.html$ /index.html # do the other magic: Rewrite the the requests to for MODx readable folders RewriteCond %{HTTP_HOST} domain1\.com [NC] RewriteCond %{REQUEST_URI} !^/manager RewriteCond %{REQUEST_FILENAME} index\.php [OR] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(index\.(html?|php)|domain1-com)?(.*)$ index.php?q=/domain1-com/$3 [L,QSA]
Let us look closer to:
RewriteEngine On RewriteBase / # First be sure to rewrite the request to a unique domain RewriteCond %{HTTP_HOST} . RewriteCond %{HTTP_HOST} ^domain1\.com [NC] RewriteCond %{HTTP_HOST} !^www\.domain1\.com [NC] RewriteRule (.*) http://www.domain1.com/$1 [R=301,L]
We already know from the standard .htaccess. This provides only that both requests for http://www.domain1.de as well as http://domain.de the same name, namely www.domain1.com. Is an absolute must to avoid the risk of duplicate content in search engines.
RewriteRule ^domain1-com\.html$ /index.html
treats the special case of the direct call of our domain directory. As in our example we have domain1-com as alias, we want a clean index.html as a link. So a direct call of our domain folder www.domain1.com/domain1-com.html (which is the start page of each domain) will display as www.domain1.com/index.html.
RewriteCond %{HTTP_HOST} domain1\.com [NC
When our domain is requested...
RewriteCond %{REQUEST_URI} !^/manager
... but not the manager...
RewriteCond %{REQUEST_FILENAME} index\.php [OR] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(index\.(html?|php)|domain1-com)?(.*)$ index.php?q=/domain1-com/$3 [L,QSA
we rewrite our links so that they MODx internally processes them correctly. Have we, for example, a document in the tree:
root - www.domain1.com - - products - - - cars - - - - BMW
Thus the actual link would look like: www.domain1.com/domain1-com/products/cars/BMW.html
(or any other domain which points to the MODx installation)
But this is not want we want as clean "speaking" URL. So the .htaccess checks which domain is requested, rewrites the requested URL in a way MODx can handl and display the desired content from that domain folder. Maybe this sounds complicated, but is actually quite simple.
This is what we are doing now for all three domains, so that our .htaccess looks like this:
RewriteEngine On RewriteBase / # First be sure to rewrite the request to a unique domain RewriteCond %{HTTP_HOST} . RewriteCond %{HTTP_HOST} ^domain1\.com [NC] RewriteCond %{HTTP_HOST} !^www\.domain1\.com [NC] RewriteRule (.*) http://www.domain1.com/$1 [R=301,L] # rewrite the homepage of each domain to index.html RewriteRule ^domain1-com\.html$ /index.html # do the other magic: Rewrite the the requests to for MODx readable folders RewriteCond %{HTTP_HOST} domain1\.com [NC] RewriteCond %{REQUEST_URI} !^/manager RewriteCond %{REQUEST_FILENAME} index\.php [OR] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(index\.(html?|php)|domain1-com)?(.*)$ index.php?q=/domain1-com/$3 [L,QSA] # First be sure to rewrite the request to a unique domain RewriteCond %{HTTP_HOST} . RewriteCond %{HTTP_HOST} ^domain2\.com [NC] RewriteCond %{HTTP_HOST} !^www\.domain2\.com [NC] RewriteRule (.*) http://www.domain2.com/$1 [R=301,L] # rewrite the homepage of each domain to index.html RewriteRule ^domain2-com\.html$ /index.html # do the other magic: Rewrite the the requests to for MODx readable folders RewriteCond %{HTTP_HOST} domain2\.com [NC] RewriteCond %{REQUEST_URI} !^/manager RewriteCond %{REQUEST_FILENAME} index\.php [OR] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(index\.(html?|php)|domain2-com)?(.*)$ index.php?q=/domain2-com/$3 [L,QSA] # First be sure to rewrite the request to a unique domain RewriteCond %{HTTP_HOST} . RewriteCond %{HTTP_HOST} ^domain3\.com [NC] RewriteCond %{HTTP_HOST} !^www\.domain3\.com [NC] RewriteRule (.*) http://www.domain3.com/$1 [R=301,L] # rewrite the homepage of each domain to index.html RewriteRule ^domain3-com\.html$ /index.html # do the other magic: Rewrite the the requests to for MODx readable folders RewriteCond %{HTTP_HOST} domain3\.com [NC] RewriteCond %{REQUEST_URI} !^/manager RewriteCond %{REQUEST_FILENAME} index\.php [OR] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(index\.(html?|php)|domain3-com)?(.*)$ index.php?q=/domain3-com/$3 [L,QSA] # The Friendly URLs part for the rest (or the main domain) RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
The three lines at the end are the standard rewrite entries as fallback.
3. Plugin for internal links
Since MODx creates URLs with our identification folders, we have to remove them from the internal links.
Create plugin "multisite":
global $modx;
$host_url = $modx->getTemplateVarOutput('host_url',$modx->documentIdentifier);
$host_url = $host_url['host_url'];
// delete our identification folder from output
$modx->documentOutput=preg_replace("#/?".$host_url."/#i","/", $modx->documentOutput);
// Homepage
$modx->documentOutput=preg_replace("#/?".$host_url."\.html#i","index.html", $modx->documentOutput);
and check "OnWebPagePrerender" as event.
What happens? The plugin reads our TV "host_url" which returns the the domain folder`s alias. This folder is deleted from all our internal weblinks. In addition, links to the homepage of each domain (eg. www.domain1.com/domain1-com.html) are rewritten to index.htm. You remember.
4. Templates
The template header (put it in a chunk, so you only have to create one chunk for all domains must contain the following line:
<base href="http://[!http_host!]/" />
This will ensure that the links in your documents contain the correct domain.
If you want to use different templates for your domains, just set the domain name as folder name for the templates and create entries like
<link href="/assets/templates/[!http_host!]/default.css" rel="stylesheet" type="text/css" />
for your css, js and so on.
5. Custom error pages
Now the system should be already running. But we also want custom 404 error pages for each domain. So create a plugin "404":
/*#### # # Name: 404 Redirect for multisite # Version: 0.1 # Author: Marc "MadeMyDay" Hinse (http://www.madeyourweb.com) # ####*/ global $modx; $e = &$modx->Event; switch($e->name) { case 'OnPageNotFound': global $modx; $table= $modx->getFullTableName('site_content'); $host_url = $_SERVER['HTTP_HOST']; $host_url = $modx->db->escape($host_url); $where = 'pagetitle = "'.$host_url.'"'; $result = $modx->db->select('id',$table,$where); $row = $modx->db->getRow($result); $redirectParent = $row['id']; if ($redirectParent){ $errorPage= $modx->db->select('id',$table,'alias=404 AND parent='.$redirectParent); $rowerror = $modx->db->getRow($errorPage); $error = $rowerror['id']; $this->sendForward($error, 'HTTP/1.0 404 Not Found'); } }
and check the event "OnPageNotFound".
If a visitor enters a URL that doesn`t exist (like www.domain1.com/fubarz), the plugin reads the typed in domain, looks which document has the domain as pagetitle (remember?!) and then looks for a document in this folder with alias "404".
That is, under each domain folder put a custom error document "page not found" or similar that has 404 as alias. The alias is important and must always be so worded as specified in the plugin (just for example, "404")!
6. Wayfinder
If you want to display your homepage in one hierarchy level with your other level 1 items:
- home
- company
- products
- contact
but as the doman folder represents your homepage and so you have been forced to have this structure:
- www.domain1.com (home)
- - company
- - products
- - contact
just define a weblink, which refers to the domain folder (that is "home"):
- www.domain1.com (home)
- - home (weblink to above folder)
- - company
- - products
- - contact
Problem is that in the current version of Wayfinder 2.0 a weblink to an internal document is not active when the remote document is being called. So we use Wayfinder 2.5, still beta but functional. Meaningful is the use of selfClass instead or in addition to the activeClass in the Wayfinder parameters. Why, do you see if it comes. In short: As the parent folder is already active when the link is active, the link always gets the status "active". This is not true in the selfClass which only is applied when the current document is active and not its parents.
That`s it. Questions and comments in the forums.