Summary
@clerk/nextjs@7.1.0's compiled <ClerkProvider> SSR graph contains an unguarded reference to window.location.origin in the helper that builds the Clerk CDN URL from proxyUrl. When NEXT_PUBLIC_CLERK_PROXY_URL is set to a relative path (e.g. /__clerk, as recommended in the frontendApiProxy docs), server-side rendering on a Node-less runtime like Cloudflare Workers throws ReferenceError: window is not defined. Next.js middleware / OpenNext catch the throw and convert it to a plain 500 Response, so the failure is silent — no exception surfaces in wrangler tail.
This broke every route on our production website-admin worker after we enabled frontendApiProxy per the documented setup. The root cause took several hours of bundle forensics to find because the silent catch hid it from standard observability.
Evidence
Exact offending line, observed in a fresh next build of @clerk/nextjs@7.1.0 at .next/server/chunks/ssr/_0~zk87u._.js:18:
let aU = a => {
var b;
let { proxyUrl: c, domain: d, publishableKey: e } = a;
return c && (!c || /^http(s)?:\/\//.test(b || "") || q(c))
? (!c ? "" : q(c) ? new URL(c, window.location.origin).toString() : c)
.replace(/http(s)?:\/\//, "")
: d && !aT((0, aN.u)(e)?.frontendApi || "")
? (0, aP.t)(d)
: (0, aN.u)(e)?.frontendApi || "";
};
q(c) returns true for relative URLs. The result is new URL(c, window.location.origin) on the server — fatal in any SSR-without-DOM environment (Cloudflare Workers, Deno Deploy, bare Edge Runtime, any workerd-based host).
Reproduction
- Next.js 16.2.3 App Router project on
@opennextjs/cloudflare@1.19.0, deployed to Cloudflare Workers.
proxy.ts (Next 16 middleware) passes { frontendApiProxy: { enabled: true } } to clerkMiddleware().
- Set
NEXT_PUBLIC_CLERK_PROXY_URL=/__clerk as a wrangler secret (or similar relative value at build time).
- Deploy. Hit any route rendering
<ClerkProvider>. 500 returned, body Internal Server Error, wrangler tail shows "exceptions": [].
Clearing NEXT_PUBLIC_CLERK_PROXY_URL (empty string) restores the site because aU() short-circuits on falsy proxyUrl before reaching the window.location.origin branch.
Impact
- Silent 500 on every SSR route. Zero observability in Cloudflare's standard logs (
wrangler tail). The fact that Next's Edge runtime swallows the throw into a plain Response is what makes this hard to diagnose, but the bug itself is the unguarded window reference.
- Affects anyone following Clerk's own
frontendApiProxy documentation on a Workers-class host.
- Version range at minimum: confirmed present in
@clerk/nextjs@7.1.0. 7.0.12 (the version in our last known-good deploy) was not exercised with frontendApiProxy so I can't state whether it was present there. A git-bisect of packages/shared/src/client/clerk-script.tsx (or whichever source file compiles to aU()) should identify the introducing change.
Suggested fixes
Either would resolve it. We're currently parked on #2's manual mitigation and would like to restore the documented frontendApiProxy setup once an upstream fix lands.
- Guard the
window.location.origin reference. Replace new URL(c, window.location.origin).toString() with a runtime-safe builder — on the server, derive origin from request headers (x-forwarded-host, x-forwarded-proto) or from NEXT_PUBLIC_CLERK_PROXY_URL's containing host if it's available at build/config time.
- Document the hazard. If a real fix isn't viable in the SDK, at minimum update the
frontendApiProxy docs to warn that NEXT_PUBLIC_CLERK_PROXY_URL must be an absolute URL on Workers-class runtimes.
Environment
@clerk/nextjs@7.1.0
next@16.2.3
@opennextjs/cloudflare@1.19.0
wrangler@4.81.0
- Cloudflare Workers (workerd),
nodejs_compat + global_fetch_strictly_public compatibility flags
- Production incident: recovered by deleting the
NEXT_PUBLIC_CLERK_PROXY_URL wrangler secret and reverting to clerkMiddleware() without frontendApiProxy.
Related
We've documented the full forensic trail in our internal ADR. Happy to share additional reproducer details, compiled-bundle excerpts, or the rolled-back worker version ID if useful.
Summary
@clerk/nextjs@7.1.0's compiled<ClerkProvider>SSR graph contains an unguarded reference towindow.location.originin the helper that builds the Clerk CDN URL fromproxyUrl. WhenNEXT_PUBLIC_CLERK_PROXY_URLis set to a relative path (e.g./__clerk, as recommended in the frontendApiProxy docs), server-side rendering on a Node-less runtime like Cloudflare Workers throwsReferenceError: window is not defined. Next.js middleware / OpenNext catch the throw and convert it to a plain 500 Response, so the failure is silent — no exception surfaces inwrangler tail.This broke every route on our production
website-adminworker after we enabledfrontendApiProxyper the documented setup. The root cause took several hours of bundle forensics to find because the silent catch hid it from standard observability.Evidence
Exact offending line, observed in a fresh
next buildof@clerk/nextjs@7.1.0at.next/server/chunks/ssr/_0~zk87u._.js:18:q(c)returns true for relative URLs. The result isnew URL(c, window.location.origin)on the server — fatal in any SSR-without-DOM environment (Cloudflare Workers, Deno Deploy, bare Edge Runtime, anyworkerd-based host).Reproduction
@opennextjs/cloudflare@1.19.0, deployed to Cloudflare Workers.proxy.ts(Next 16 middleware) passes{ frontendApiProxy: { enabled: true } }toclerkMiddleware().NEXT_PUBLIC_CLERK_PROXY_URL=/__clerkas a wrangler secret (or similar relative value at build time).<ClerkProvider>. 500 returned, bodyInternal Server Error,wrangler tailshows"exceptions": [].Clearing
NEXT_PUBLIC_CLERK_PROXY_URL(empty string) restores the site becauseaU()short-circuits on falsyproxyUrlbefore reaching thewindow.location.originbranch.Impact
wrangler tail). The fact that Next's Edge runtime swallows the throw into a plain Response is what makes this hard to diagnose, but the bug itself is the unguardedwindowreference.frontendApiProxydocumentation on a Workers-class host.@clerk/nextjs@7.1.0.7.0.12(the version in our last known-good deploy) was not exercised withfrontendApiProxyso I can't state whether it was present there. A git-bisect ofpackages/shared/src/client/clerk-script.tsx(or whichever source file compiles toaU()) should identify the introducing change.Suggested fixes
Either would resolve it. We're currently parked on #2's manual mitigation and would like to restore the documented
frontendApiProxysetup once an upstream fix lands.window.location.originreference. Replacenew URL(c, window.location.origin).toString()with a runtime-safe builder — on the server, derive origin from request headers (x-forwarded-host,x-forwarded-proto) or fromNEXT_PUBLIC_CLERK_PROXY_URL's containing host if it's available at build/config time.frontendApiProxydocs to warn thatNEXT_PUBLIC_CLERK_PROXY_URLmust be an absolute URL on Workers-class runtimes.Environment
@clerk/nextjs@7.1.0next@16.2.3@opennextjs/cloudflare@1.19.0wrangler@4.81.0nodejs_compat+global_fetch_strictly_publiccompatibility flagsNEXT_PUBLIC_CLERK_PROXY_URLwrangler secret and reverting toclerkMiddleware()withoutfrontendApiProxy.Related
We've documented the full forensic trail in our internal ADR. Happy to share additional reproducer details, compiled-bundle excerpts, or the rolled-back worker version ID if useful.