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):
✅ (
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=" 991w" src="
The src
attribute’s URL resolved as expected. However, the srcset
URL attribute associated to the 991
width did not resolve as expected:
❌ (
404)
Interestingly, after manually appending the sc_lang
parameter to the URL, it did resolve as expected:
&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 theurlbuilder.config
file,mediaItem
entities now return thesc_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’ssrcSet
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:
❌ (
404
)
It resolved to:
&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 😅.