Sitecore Link Resolving across sites and in EXM
Getting links to resolve correctly is trickier than one would expect.
The scenario that I want to go through here is like this:
- 2 separate SXA sites
- An email campaign where we want to have links to both sites
- Both Rich Text and General Link fields can be used
The current Sitecore documentation on this is both very misleading and in one case downright incorrect unfortunately. I will go into that later.
I would like to say that having SXA shouldn’t make a difference, but unfortunately it does. So I’ll explain two ways to set this up, one with SXA, and one without.
Overview of what we’re trying to achieve
It’s all about the LinkManager and configured LinkProviders. We want them to do all the work for us, as we would expect if we were doing cross-site linking in normal Sitecore. So getting cross site linking working in your normal sites will also make it start working in SXA.
The part where Sitecore documentation is really misleading is it makes it seem you have to do something special with their Hostname Mapping tool. I know they also mention this:
The logic for generating URLs in an email message to content items is the same approach that Sitecore uses when rendering pages on a website.
But going on to give an example with multisite using just the Hostname Mapping tool is not going to work.
So the real key to using all of Sitecore’s OOTB logic is to make sure of the following
- Ensure that the link provider has
alwaysIncludeServerUrl=true
when the context site isexm
targetHostName
is set correctly on your sites
targetHostName: The host name to use when generating URLs to items within this site from the context of another site. If the targetHostName attribute is absent, Sitecore uses the value of the hostName attribute instead. Used only when the value of the Rendering.SiteResolving setting is true.
With SXA
- Ensure that you are using the default SXA
<linkManager defaultProvider=”switchableLinkProvider”>
More info here. - On all sites in your solution, make sure that a single
targetHostname
is set. Ensure there’s no wildcards and it is the actual site URL that people use. - Make sure that you’re using the
LocalizableLinkProvider
on all your sites (or if you need further customizations, base your code on theLocalizableLinkProvider
and make sure to keep the magic around it setting thealwaysIncludeServerUrl
to true if thetargetHostname
is set. - That’s pretty much it.
Without SXA
- Override the LinkProvider to make sure that if the current context site is not the same as the site that it retrieves for the item set
AlwaysInlcudeServerUrl
to true in the URL options. - As above, on all sites in your solution, make sure that a single
targetHostname
is set. Ensure there’s no wildcards and it is the actual site URL that people use.
Testing Notes
- EXM will run most of its pipelines in preview mode when it comes to links. So it’s fine to test by using the quick test feature or navigating to the email item in the Content Editor and using the Message Preview tab. When you kick off an actual dispatch it will do a couple extra things like encrypt links with the contact ID but that shouldn’t break your links.
- You’ll want to do an actual dispatch, then view the source of the email and make sure that the tracking pixel URL is resolvable. This is really important to get stats and analytics working. If the tracking pixel URL isn’t right, that’s when you can start looking at Hostname Mapping.
Gotcha #1 — Subdomain and top level domain
One gotcha I saw was that if one site is a subdomain of the top level domain then it won’t resolve correctly.
Example:
Site 1: https://test.com
Site 2: https://subdomain.test.com
It won’t actually work in this situation. If you can, change to using https://www.test.com. If you can’t, then a workaround is to set a link provider with AlwaysInlcudeServerUrl=true
on your exm
site.
Gotcha #2 — Multiple matching sites
You may have multiple sites that would match an item. This might be because you have your CM environment configured in Live mode (reading from master).
EXM will use the normal hostname match the same as any request, so if you are using a virtual directory on the CM site then it will appear in the URL.
I think this is a bug and it should be possible to instead choose the first site in configuration order, so feel free to raise with support, but for now I’ve gone with not having virtual directories on CM sites that need this feature.
Sitecore Docs
EXM verifies and generates hyperlinks according to the following process that is specified in the
modifyHyperlink
pipeline
This isn’t correct. It will in many cases have resolved the link before it gets here. A few situations where it won’t have resolved the link yet, in which case this logic does kick in.
Sitecore Link Provider / URL Builder weirdness
Why do we even need to go to all this extra effort of playing with AlwaysInlcudeServerUrl
value? To me, the default item URL builder should be taking care of this, and it should somewhat take into account the current Site Context.
- If we are not in the same Site Context as the target item, automatically set
AlwaysInlcudeServerUrl=true
- If we are in the same Site Context as the target item and
AlwaysInlcudeServerUrl=true
, then resolve the server URL by taking into account thetargetHostname
. Currently it does not do this — it instead checks if the request URL matches thehostName
and then uses the request URL instead! This is totally a bug, especially where we might have a load balancer pinging the site on an internal URL, this result may then get cached.
Sitecore Versions Applicable
- I confirmed the above behaviour on Sitecore 9.1.1 and Sitecore 10.1.0
- The exact behaviour around the item URL builder has changed in 9.3+
- There is a known issue in 9.3 and 10.0 around this — public ref 391005. See the fix in 10.1.0 release notes.
- In 10.1.3 (and possibly all the latest 10.x.x cumulative hotfixes), the behaviour has changed yet again.
LinkProviderService
has been changed so that if the current site does not have theIsSxaSite=true
property, it will force using thesitecore
provider. So going forward, you’ll need to pretty much always patch thesitecore
provider with either the SXALocalizableLinkProvider
or your custom implementation discussed in the “non SXA” section above.