Intro
My team and I recently ran into an issue while working on a Sitecore XM Cloud project that turned out to be pretty interesting. The issue had to do with images published to Experience Edge not loading as expected within certain renderings. The GET requests for these images in Experience Edge would return a 404 status code rather than the expected 200. This issue only seemed to happen for recently updated images and net-new images recently published to Experience Edge. Additionally (and confusingly), older, existing images (that weren’t recently published) still loaded as expected.
For context, our XM Cloud solution features a Next.js head application hosted on Vercel using Next.js version 12.2.5 and @sitecore-jss/sitecore-jss-nextjs version 21.0.0. Our solution exclusively uses the English language (en).
Investigation
After ruling out the typical things like making sure everything was published (and republished), checking associations to data source items, verifying the GraphQL response from the layout service, and even clearing the Experience Edge cache, we opted to open a ticket with Sitecore. I wanted to see if other members of the Sitecore community had experienced something similar, so I asked a question in the Sitecore Slack community and eventually created a corresponding question on Sitecore Stack Exchange.
While the Sitecore ticket simmered, our team continued troubleshooting the image URLs. The URL coming down from the layout service was something like this (as an example):
https://edge.sitecorecloud.io/<tenant>/media/foo.jpg?h=495&iar=0&w=692&sc_lang=en (200)
Okay, cool; that URL resolved when browsed directly. Looking at the rendered markup on the problematic pages, it was noted that only those components using responsive images (reference) exhibited the issue. The Image component (from the @sitecore-jss/sitecore-jss-nextjs package) includes a property, srcSet, to support responsive images. The rendered <img> tag looked something like this:
<img … width=”692″ height=”495″ class=”img-responsive” loading=”lazy” srcset=”https://edge.sitecorecloud.io/<tenant>/media/foo.jpg?mw=991 991w” src=”https://edge.sitecorecloud.io/<tenant>/media/foo.jpg?h=495&iar=0&w=692&sc_lang=en”>
The src attribute’s URL resolved as expected. However, the srcset URL attribute associated to the 991 width did not resolve as expected:
https://edge.sitecorecloud.io/<tenant>/media/foo.jpg?mw=991 (404)
Interestingly, after manually appending the sc_lang parameter to the URL, it did resolve as expected:
https://edge.sitecorecloud.io/<tenant>/media/foo.jpg?mw=991&sc_lang=en (200)
Why would sc_lang be required to fetch images when it hadn’t been previously? Well, Sitecore support had an answer: a bug fix affecting versioned media items had recently been deployed to Experience Edge. Looking at the XM Cloud changelog (here), I think this was the specific update; however, I’m not certain as there are several ostensibly adjacent entries in the changelog.
If languageEmbedding is set to never in the urlbuilder.config file, mediaItem entities now return the sc_lang parameter.
The change as explained by Sitecore support:
[In Experience Edge] When a media item is published, a MediaItem entity is generated by the following method on CM: Sitecore.ExperienceEdge.Connector.EntityGenerator.MediaDeliveryEntityGenerator.GenerateMediaEntity(…)
This MediaItem entity has multiple fields containing media URLs and paths.
For versioned media items, each of these fields is expected to contain the sc_lang parameter.
Net out: As of ~2 months ago, if you want versioned images published to Experience Edge to load correctly, you have to include the sc_lang parameter. Experience Edge does not default to en and does not include a fallback otherwise–if sc_lang isn’t present, you’ll get a 404.
That change, coupled with the fact that the Image component in the @sitecore-jss/sitecore-jss-nextjs package strips out the sc_lang parameter when resolving the various srcset URLs meant that our versioned images used as responsive images weren’t loading correctly on the front-end.
It was kind of a perfect storm considering the following:
The Experience Edge fix for versioned media items went out presumably at some point in late February.
Our development team and content authors hadn’t been updating, adding, or publishing media items for a while, so the issue wasn’t immediately noticed. There weren’t a lot of versioned images being published to Experience Edge, in other words.
The Image component’s srcSet property was being used which transformed the URL coming down from the layout service accordingly but, in doing so, stripped off the (now required) sc_lang parameter.
The Fix
Armed with the knowledge that, moving forward, versioned images published to Experience Edge require the sc_lang parameter, I set about implementing a fix for our renderings using the srcSet property of the Image component. I ended up creating a small helper function to abstract away the query string parsing and conditional inclusion of the sc_lang parameter into the array passed into the srcSet property (as recommended by Sitecore support). The helper function looked like this:
export const buildSrcSetParams = (imageSizeParameters: ImageSizeParameters[] | undefined, image: ImageField): ImageSizeParameters[] | undefined => {
let retVal = imageSizeParameters;
if (!image?.value?.src) {
return retVal;
}
const queryParams = new URLSearchParams(image.value.src.split(‘?’)[1]);
const scLangParam = queryParams.get(‘sc_lang’);
if (retVal && scLangParam) {
// add sc_lang parameter
retVal = retVal.map((isp) => ({
…isp,
sc_lang: scLangParam,
}));
}
return retVal;
};
Previously, the Image components looked something like this:
<Image field={props?.fields?.Image} srcSet={ [ { mw: 991 } ] } … />
Using the new helper function, they looked like this instead:
<Image field={props?.fields?.Image} srcSet={buildSrcSetParams([ { mw: 991 } ], props?.fields?.Image) } … />
Now, instead of the srcset URL resolving to:
https://edge.sitecorecloud.io/<tenant>/media/foo.jpg?mw=991 (404)
It resolved to:
https://edge.sitecorecloud.io/<tenant>/media/foo.jpg?mw=991&sc_lang=en (200)
Closing Thoughts
Obviously, having to make, test, and deploy a code change to production in response to an Experience Edge bug fix wasn’t great. Arguably, Experience Edge would fall back to a configurable default language (e.g. en) or to an unversioned image, if available.
As of 4/17/2024, Sitecore support confirmed that this scenario was reproducible using version 21.6.0 of the @sitecore-jss/sitecore-jss-nextjs package and that the product team would be taking a further look.
Â
On 4/23/2024, Sitecore support recognized this issue as a bug and issued a reference number: JSS-1902.
Sitecore’s SaaS products such as XM Cloud and Experience Edge are constantly evolving and improving, which is great; in fact, it’s a major selling point when it comes to adopting XM Cloud. Ideally, our team would have somehow known about or been informed of this breaking change ahead of time. How would we have done that? I’m not sure, other than perhaps by following the XM Cloud change log a bit more closely .
Source: Read MoreÂ