One of those Sitecore issues that looks simple at first, but turns into a few hours of digging.
We had Sitecore Pages working perfectly against the open *.vercel.app URL, but the moment we switched to our protected custom domain, editing started failing with a rather unhelpful 500. The interesting part was that the problem wasn’t really Pages, Vercel, or the domain restriction on its own — it was how the Content SDK makes an internal request back to the app during editing.
Here’s what we found, and what finally fixed it.
The symptom
In Sitecore Pages, editing against our custom preview host failed with 500 on:
GET /api/editing/render?...&route=/Components&mode=edit
Vercel function logs showed:
TypeError: Failed to parse URL from /Components
Same app, same commit, same item — but pointed at the open *.vercel.app host — worked with no errors.
What /api/editing/render actually does
With Content SDK’s editing optimization, the render handler does not only set draft mode and redirect. It performs an internal server-side fetch of the page route and returns HTML to Pages.
By default, that internal host comes from the incoming request’s Host header (or from SITECORE_INTERNAL_EDITING_HOST_URL / sitecoreInternalEditingHostUrl when configured). Docs:
Two problems that look like one
Pages sends route=/Components (relative). On the server, fetch('/Components') is invalid — Node needs an absolute URL. That produces exactly:
Failed to parse URL from /Components
This is not a 401/403 from your domain gate. It’s a URL construction failure before (or instead of) a successful loopback.
Community echo of the same class of error:
Even after the URL is absolute, if the internal fetch targets a restricted custom domain, it must carry the same bypass your editors use.
Sitecore’s official guidance for stock Vercel Deployment Protection uses x-vercel-protection-bypass on rendering-host URLs:
If you use a custom query bypass (for example a firewall rule keyed off a token query string), remember: Content SDK only forwards allow-listed params. Configure something like:
export const { GET, POST, OPTIONS } = createEditingRenderRouteHandlers({
sitecoreInternalEditingHostUrl: scConfig.sitecoreInternalEditingHostUrl,
allowedQueryParams: ['sitecore-editing-host'], // your bypass query name
});
Docs for allow-listing:
Why *.vercel.app “just worked”
Open preview host → Host header resolves → internal fetch gets an absolute URL → no restriction on that host → Pages happy.
Restricted custom domain → either absolute host isn’t applied (you see the parse-URL 500), or absolute host is applied but the loopback is blocked without the bypass (you’d typically see auth/firewall failures instead).
Long-term you don’t need to keep the *.vercel.app hostname. Point editing at the custom domain and make the loopback succeed there:
SITECORE_INTERNAL_EDITING_HOST_URL=https://your-custom-preview-domain.example
…and forward your bypass query/header on the internal request.
- Confirm the 500 text in Vercel logs (
Failed to parse URLvs 401/403). - Wire
sitecoreInternalEditingHostUrlinsitecore.config.tsand pass it intocreateEditingRenderRouteHandlers. - Set
SITECORE_INTERNAL_EDITING_HOST_URLto the custom preview origin if Host-based default isn’t enough. - Add your bypass query name to
allowedQueryParams(or use Vercel’s official protection-bypass params on the rendering host). - Retest Pages on the custom domain; retire the open
*.vercel.apphost when green.
Takeaway
Sitecore’s editing render path is a server-to-server call inside your Next app. Anything that assumes “the browser already passed the gate” will miss the loopback. Absolute internal host + bypass on that second hop is what makes a restricted custom domain behave like the open Vercel URL.
If this saved you an afternoon of log diving, share it with the next person staring at Failed to parse URL from /YourPage in a Pages session.



No comments:
Post a Comment