Rob Austin - Jul 2, 2026
Why Shadcn CLI registry auth breaks on non-www URLs
Every so often something breaks that isn’t really anyone’s bug — it’s just two reasonable behaviors meeting at an awkward angle. This is one of those. We started getting reports that installing our blocks was suddenly failing with a 401 Unauthorized, even though the same API key and the same components.json had been working for months. Nothing had changed on our end. Nothing had changed on theirs. And yet.
After some digging, the culprit turned out to be a quiet change in the shadcn CLI — and it’s something we think will bite a lot of private registries, not just ours. So here’s the whole story.
The symptom
Some people could install our pro blocks. Some couldn’t. Same key, same config, same command. The ones who failed all had one thing in common: their components.json used our non-www registry URL.
For context, our registry used to happily answer to both:
https://www.shadcnblocks.com/r/{name}https://shadcnblocks.com/r/{name}
Both worked. Then, seemingly overnight, the non-www one started returning 401 for a growing number of people — while the www one kept working perfectly.
The root cause
Our apex domain redirects to www. That’s an extremely ordinary setup — it’s the default on Vercel, Netlify, and Cloudflare. You can see it with a plain curl:
$ curl -I https://shadcnblocks.com/r/hero36
HTTP/2 308
location: https://www.shadcnblocks.com/r/hero36
Here’s the important part: shadcnblocks.com and www.shadcnblocks.com are different origins. When an HTTP client follows a redirect to a different origin, it drops the Authorization header — on purpose, so your credentials don’t leak to an unexpected host. So the request that finally lands on www arrives with no API key, and the server has no choice but to answer 401.
The redirect strips the token. The token was the whole point. Hence the failure.
So why did it used to work?
Because whether the header survives a redirect depends entirely on the HTTP client — and the shadcn CLI recently changed which one it uses.
Older versions of the CLI fetched with node-fetch, which has a lenient rule: it keeps the Authorization header when the redirect target is the same domain or a subdomain. Since www.shadcnblocks.com is a subdomain of shadcnblocks.com, the token rode along and everything worked.
The latest CLI (4.11.1+) dropped node-fetch in favor of the platform’s native fetch (with undici for proxy support). Native fetch follows the stricter, spec-aligned rule: it strips Authorization on any origin-changing redirect, apex ↔ www included. Same config, different plumbing, different outcome.
| CLI version | fetch impl | cross-host redirect w/ auth |
|---|---|---|
| ≤ 4.11.0 | node-fetch | header preserved (same-domain/subdomain) |
| 4.11.1+ / current | native fetch + undici | header stripped → 401 |
And because npx shadcn (unpinned) always resolves to the latest version, a config that worked yesterday can fail today after a routine upgrade — with no change on the user’s side at all. That’s exactly why it looked like it was “randomly” affecting some people and not others: they were simply on different CLI versions.
To confirm it wasn’t imagination, we tested the same apex → www 308 across clients:
| Client | Keeps Authorization? |
|---|---|
node-fetch (≤ 4.11.0) | ✅ kept on same-domain/subdomain |
native fetch / undici (4.11.1+) | ❌ stripped |
curl -L | ❌ stripped |
The fix (and why it reaches beyond us)
The fix is refreshingly boring: use the canonical, post-redirect host in your components.json. For us, that’s the www URL:
{
"registries": {
"@shadcnblocks": {
"url": "https://www.shadcnblocks.com/r/{name}",
"headers": { "Authorization": "Bearer ${SHADCNBLOCKS_API_KEY}" }
}
}
}
That’s it. No redirect, no stripped header, no 401.
The reason we’re writing this up rather than just quietly patching our docs is that any authenticated registry sitting behind a host-level redirect will hit this — and apex → www is one of the most common redirects on the web. If you run a private registry, two things will save your users a lot of confusion:
- Publish the canonical (final, post-redirect) host in the config you hand out.
- Avoid cross-host redirects on your registry paths entirely, so the token-bearing request never crosses an origin boundary in the first place.
This also affects bunx, Deno, and CI runners, which strip cross-host auth regardless of CLI version — so the canonical-URL habit is worth adopting everywhere.