Error reference

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.