On publish instances for SEO purposes you may want to remove /content/[sitename] from the URLs. At the same time, you want these short URLs to be resolved to resources when a user requests them.To achieve this, you need to configure Mapping for Resource Resolution. Usually, it’s a trivial task, but it’s getting complicated when you need to host several projects on the same AEM instance.
So, let’s imagine, you have several projects hosted on the same AEM instance. One of them lives under /content/site1 and should be accessible via https://site1.com. And another one – under /content/site2 and accessible via https://site2.com. There are several ways to configure resource resolution in Sling and configuration via configuration tree (by default it’s /etc/map) is the most flexible way and we will use it in this article.
Sling Resource Mapping Configuration
To not mess with existing mappings, let’s create a separate folder for our example – /etc/map.muldomains. Path to the configuration we will change with property resource.resolver.map.location of configuration with PID org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl.
Now we should create Root Level Mappings for our sites. Such mappings include scheme, host, port and URI path.
Since HTTPS is widely used, we will support it as well. As a result, we have to create folder /etc/map.muldomains/https – this will be the start of our mapping tree, which tells that all mapping rules beneath it should be applied only for https scheme.
Now it’s time to specify the rule for our domain /etc/map.muldomains/https/site1.com:
{ "jcr:primaryType": "sling:Mapping", "sling:redirect": "/home.html" }
Here we create a rule for the host so that user will be redirected to the home page when access site by host only. The node name is used to match the host from the request.
Now we need to specify how to resolve any other requested URI. For this let’s create a child node of the host rule – /etc/map.muldomains/https/site1.com/all.
{ "jcr:primaryType": "sling:Mapping", "sling:match": "(.+)$", "sling:internalRedirect": [ "/content/site1/$1", "/$1" ] }
Here we use the sling:match property to specify the regexp to match the path from URI, instead of the node name. We do it in this way, because we can give a more human readable name to the node and, also, pretty often such regexp do contain special chars which can not be used in the node’s name. Hence, it’s safer to put it into the property’s value. In the rule above we prepend /content/site1 to the path in all requested URIs and try to resolve the resource, in case it’s missing – Sling will try to resolve origin path (note the second entry of the sling:internalRedirect value). The same steps should be repeated for “site2” by copying created rules and replacing “site1” with “site2”. To test mapping I’ve created dummy content structure:
But to test it, we will use Resource Resolver Web Console – /system/console/jcrresolver. It’s handy to see effective Resolver Map Entries and to test configuration. So let’s check our sites (Resolve button):
We got 302 redirects to the home.html page. That is fine. Now it’s time to test specific pages:
In both cases, Sling Resource Resolver resolves URL to the correct resource.
Reverse Resource Resolving Configuration
But before we add /content/[sitename] to the path, we should remove it. Let’s see if resource mapping works (button Map):
Wow, what just happened? Why content is removed and “site2” – not? Let’s check section “Mapping Map Entries” on the same page:
So there is a rule for replacing /content/ with / and nothing about site1/site2. It appears, that when we use regexp in the rule, it can not be used for the mapping (when we transform path to the resource to the URI for the end user). To overcome this issue, we should create separate rules (not root level mappings) where we specify regexp in sling:internalRedirect property and transformation instructions in sling:match (so in opposite to configs we previously specified).
Here is a rule for the reverse mapping for the site2 – /etc/map.muldomains/site2-rev:
{ "jcr:primaryType": "sling:Mapping", "sling:match": "/$1", "sling:internalRedirect": "^/content/site2/(.*)" }
Here for all paths which start with /content/site2/ we remove it. Let’s test:
Repeat the same steps for site1.
Resource Resolving Configuration For Direct Access
In some cases (debugging usually) you may need to access publish instance directly. But we have configured resolving mapping only for HTTPS scheme so far. Let’s fix this – /etc/map.muldomains/http/site1_noDIsp:
{ "jcr:primaryType": "sling:Mapping", "sling:redirect": "/home.html", "sling:match": "site1.com.\\d{4}" }
Here we create folder /etc/map.muldomains/http for HTTP scheme and mapping node. We match our domain and additionally, we specify any 4-digit port. So common 4503/4504 will be matched here. And for all other pages – /etc/map.muldomains/http/site1_noDisp/all:
{ "jcr:primaryType": "sling:Mapping", "sling:match": "(.+)$", "sling:internalRedirect": [ "/content/site1/$1", "/$1" ] }
The same as we have for HTTPS scheme. Before testing – repeat above steps for site2.
In addition, if you cache your HTML pages you would want to prepend /content/[sitename] to all requested pages on Apache side to cache them under the correct (full) path as this prevents from cache invalidation problems.
This example can be found in the github repository.
why site1.com. ?
i want this to be dynamic based on request IP/host
RAj, you may try to use regexp for the domain, but in this case you may run into more problems with Mapping.
Also, it’s really common to manage rewriting rules (so to resolve short url -> full resource path) on Apache side, so you can go this way.