WebCrypto is unavailable — shared sessions need a secure context
2 min read
subtleCrypto() checks for globalThis.crypto.subtle and throws when it is
absent. Two environments produce that.
Node older than 18
crypto.subtle became available on the global object in Node 18. On 16 and
earlier it exists only behind require('crypto').webcrypto, which this code
deliberately does not reach for — a global check that silently falls back would
hide the version problem until something subtler broke.
Upgrade Node. There is no shim worth adding here.
A browser without a secure context
This is the one that wastes an afternoon. crypto.subtle is undefined on pages
served over plain http:// — not restricted, not throwing, simply not there.
Browsers gate the whole SubtleCrypto interface behind a secure context.
Secure contexts are https:// and http://localhost. Notably http://127.0.0.1
is treated as secure by most browsers but http://192.168.1.x is not, so a
development setup that works on your machine can fail the moment a colleague
opens it over the LAN.
How to fix it
Serve over https, or use localhost rather than a LAN IP during development.
The error text says "https or localhost" for exactly this reason: the failure almost never looks like a crypto problem when you hit it. It looks like the page is broken, because an interface you assumed was always present is missing.
Confirming which environment is at fault
In Node:
node -e "console.log(process.version, !!globalThis.crypto?.subtle)"
In a browser console:
console.log(window.isSecureContext, !!crypto?.subtle)
isSecureContext is the direct answer for the browser case, and it is worth
checking first because it explains the failure in one word rather than sending
you into the crypto code.
Why there is no fallback
A pure-JavaScript AES implementation would make this error disappear and would be a bad trade. Constant-time behaviour is difficult in JavaScript, key material would live in ordinary reachable objects, and the resulting code would carry the same interface with materially weaker guarantees — the worst kind of compatibility shim, because nothing downstream could tell the difference.
