The instances of Sitecore with more than one site are on the rise. SXA allows for templated Site and Tenant Management, and Sitecore even released a special Multisite Addon which handles all the resolution of sites by hostname, just like the way things used to be!

This could have easily been Borat

However, we were having some issues locally when trying to run a second site on the same rendering host. Looking at the .env file in your Headless App, you see these lines:

# Your Sitecore site name. 
# Uses your `package.json` config `appName` if empty.
# When using the Next.js Multisite add-on, the value of the variable represents the default/configured site.

SITECORE_SITE_NAME=MainSite

Any time we’d try to access SecondSite on the right URL, we’d be served up the content from MainSite. It was peculiar, to say the least. Looking at the logs…nothing much of note was there, until we turned on logging. You can see all the logs for the multisite plugin by enabling DEBUG:

DEBUG=sitecore-jss:multisite

Lo and behold, we had logs!

What did the logs show us? Well, first a quick refresher on how the Multisite Addon works:

  1. You make a request
  2. Sitecore does a query to find all the sites (specifically the hostname and sitename of each SXA site)
  3. Sitecore matches the incoming hostname to a site
  4. Sets a cookie and a rewrite header
  5. Serves up content from the right path from your SSG’d content

The fact we were getting the default site all the time, regardless of which hosts were were using, meant something was…amiss:

sitecore-jss:multisite multisite middleware start: { pathname: '/', language: 'en', hostname: '[' } +11m
sitecore-jss:multisite multisite middleware end in 1ms: {
rewritePath: '/_site_MainSite/',
siteName: 'MainSite',
headers: {
set-cookie: 'sc_site=MainSite; Path=/',
x-middleware-rewrite: 'https://localhost:3000/_site_MainSite',
x-sc-rewrite: '/_site_MainSite/'
},
cookies: ResponseCookies {
"sc_site":{"name":"sc_site","value":"MainSite","path":"/"}}
} +1ms

Take a look at that first line, there. The hostname header was being set to ‘[‘ which is clearly…innaccurate

I dug a little into the multsite addon directly and came across this line:

/**
* Extract 'host' header
* @param {NextRequest} req request
*/
protected getHostHeader(req: NextRequest) {
return req.headers.get('host')?.split(':')[0];
}

It turns out, the addon is only looking at the host header. That seems…questionable, knowing that many frameworks (including Sitecore using the Sitecore.LoadBalancing.HostHeader setting) allow you to set what header the hostname comes across. Proxies, Load Balancers, Gateways, and the likes love to eat this host header up. Traefik, it turns out, is the same.

I added a console.log(req.headers) inside \src\lib\middlware\plugins\multisite.ts:

async exec(req: NextRequest, res?: NextResponse): Promise<NextResponse> {

console.log(req.headers);

return this.multisiteMiddleware.getHandler()(req, res);

}

Which yielded some interesting results:

[ 'accept-encoding', 'gzip, deflate' ],
[ 'accept-language', 'en-US,en;q=0.5' ],
[ 'cache-control', '' ],
[ 'connection', 'keep-alive' ],
[ 'cookie', 'sc_site=MainSite' ],
[ 'host', '[::1]:49176' ],
[ 'sec-fetch-dest', 'document' ],
[ 'sec-fetch-mode', 'cors' ],
[ 'sec-fetch-site', 'none' ],
[ 'sec-fetch-user', '?1' ],
[ 'te', 'trailers' ],
[ 'upgrade-insecure-requests', '1' ],
[ 'user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0'],
[ 'x-forwarded-for', '172.24.64.1' ],
[ 'x-forwarded-host', 'secondsite.local' ],
[ 'x-forwarded-port', '443' ],
[ 'x-forwarded-proto', 'https' ],
[ 'x-forwarded-server', 'de1240d005a6' ],
[ 'x-invoke-output', '' ],
[ 'x-invoke-path', '' ],
[ 'x-invoke-query', '' ],
[ 'x-middleware-invoke', '1' ],
[ 'x-real-ip', '172.24.64.1' ]

It looks like traefik is magically setting the host header to the IPV6 version of localhost…which is…odd. We do see, however, that the ‘x-forwarded-host’ contains the correct value! The fix ended up being pretty easy, all things considered:

async exec(req: NextRequest, res?: NextResponse): Promise<NextResponse> {
if (process.env.RENDERINGHOST_HOST_HEADER) {
req.headers.set('host', req.headers.get(process.env.RENDERINGHOST_HOST_HEADER) as string);
}

return this.multisiteMiddleware.getHandler()(req, res);
}

And you simply need to set the following environment variable

RENDERINGHOST_HOST_HEADER=x-forwarded-host.

With that setup, things were smooth sailing, and everything worked the way we anticipated!