English

Developer tools · UUID generator

Why crypto.randomUUID() Fails on HTTP Pages: Secure Contexts Explained

· How it works

uuid cryptography browser-apis

Three origins shown: HTTPS with crypto.randomUUID available, localhost with crypto.randomUUID available, HTTP staging server with randomUUID blocked
Original ToolAcre vector illustration

crypto.randomUUID works on localhost and on HTTPS, then vanishes on a plain-HTTP staging host. This post explains the secure-context rule behind that behaviour and how to generate a UUID safely where it applies.

TypeError on staging, fine everywhere else — the symptom and the environment difference that causes it

A developer checks their work on localhost:3000 and the UUID generator works perfectly. They deploy to staging at http://staging. internal. example. com (plain HTTP on the company LAN) and the code throws TypeError: crypto.randomUUID is not a function. The same code in production on https://example. com works fine. The inconsistency is baffling until they read the MDN docs: crypto.randomUUID is restricted to secure contexts. A secure context is either HTTPS or localhost; a plain-HTTP origin on a LAN is not secure by the browser rule, even if the network is private. The fix is to use crypto.getRandomValues with manual bit operations, or to upgrade the staging server to HTTPS. The secure-context rule was introduced to prevent sensitive APIs from leaking to unencrypted connections.

What a secure context is — the browser rule that reserves certain APIs for HTTPS origins and for localhost

A page over plain HTTP can be intercepted by a network attacker; exposing cryptographic APIs to such a page would make it possible for an attacker to generate identifiers using a compromised API. HTTPS encrypts the page and all API communications so that an attacker on the network cannot intercept or modify the code. Localhost is treated as inherently secure because it only exists on the local machine and cannot be intercepted over the network. Any other HTTP origin (a LAN address, a public domain without HTTPS, a reverse proxy that forwards to HTTP) is not secure by definition. The Web Crypto API is split across two functions: crypto.randomUUID, restricted to secure contexts, and crypto.getRandomValues, available in both secure and non-secure contexts. Both use the same operating-system CSPRNG.

Which parts of Web Crypto are gated — crypto.randomUUID and crypto.subtle require a secure context while crypto.getRandomValues does not

The difference is that getRandomValues does not hide the fact that you are using cryptography; a page that uses it must be explicitly requesting random bytes. The randomUUID function is a convenience that also enforces secure context. If your application needs to generate UUIDs on a non-secure page, you must use getRandomValues and manually set the version and variant bits. RFC 9562 specifies the bit operations: set byte 6 to (byte6 & 0x0f) | 0x40 for version 4, and byte 8 to (byte8 & 0x3f) | 0x80 for RFC variant. The ToolAcre library does exactly this as a fallback when randomUUID is unavailable. Reproducing the failure: serve a simple page from a plain HTTP origin that is not treated as potentially trustworthy. Check whether randomUUID is exposed, then compare getRandomValues, which the Web Crypto reference permits in insecure contexts.

Building a v4 UUID from getRandomValues — the masking-and-formatting fallback that keeps you on the CSPRNG when randomUUID is missing

The same code in a file served over HTTPS can expose randomUUID. If production reports "randomUUID is not a function," first inspect whether the origin is a secure context, then verify browser support and whether another script replaced the crypto object. The remedy may be HTTPS or a getRandomValues implementation that sets the UUID bits explicitly. A Math.random polyfill is not an equivalent fallback: it reproduces the shape while dropping the cryptographic-source contract. Tests that assert only dashes and version digits will miss that substitution, so review the source path as well as the resulting string.

Worked example — reproducing the failure on an http:// origin and confirming the fix

But the identifier is now predictable. An attacker who captures a few UUIDs from your system can predict the next one. If an application mistakenly treats such an identifier as a bearer credential, predictability becomes an authorization failure rather than a cosmetic defect. The correct strategy is to upgrade the server to HTTPS (always the right move for any page with authentication or sensitive data) or to use getRandomValues with explicit bit operations (which takes more code but is cryptographically sound). Secure context is enforced by the browser; you cannot work around it with configuration or environment variables. The ToolAcre generator is deployed over HTTPS, so crypto.randomUUID is available. When you generate a UUID in the tool, it uses either randomUUID (if the secure context check passes) or getRandomValues with bit operations (if you are on plain HTTP, though this is rare).

Why you should not polyfill with Math.random — the tempting shortcut and the security cost it carries

Neither path falls back to Math.random. If you are building your own UUID generator and targeting non-secure origins, use getRandomValues and do the bit operations yourself. Test on both HTTPS and localhost to confirm randomUUID works, then test on an http:// origin to confirm your getRandomValues fallback is correct. Understanding the secure-context restriction helps you design deployment strategies. If your application must run on a private LAN without HTTPS (legacy infrastructure, embedded systems), the getRandomValues fallback is your path forward. If you have the choice, upgrade to HTTPS everywhere; it is free with Let's Encrypt, and the investment pays back in security across the entire application. Development on localhost has no restriction, so test your UUID generator on localhost and on HTTPS staging before deploying to production. Production should always be HTTPS.

What this does not cover — server runtimes such as Node.js and Deno, which expose the API without a secure-context rule

The restriction is not a bug or a nuisance; it is a security feature that prevents accidents and forces you to think about encryption. The broader pattern is that web cryptography APIs are gated by secure context. crypto.getRandomValues, crypto. subtle. encrypt, crypto. subtle. generateKey, and all other sensitive operations require HTTPS or localhost. There is no exception, no override, no way to disable the check. A single insecure page breaks the security guarantees for your users. Even if you are careful to use crypto APIs only on certain pages, a mistake (or a dependency that includes a UUID generator) can leak randomness generation to an unencrypted page. The ToolAcre generator enforces this at the code level: if randomUUID is unavailable (non-secure context), it uses getRandomValues, which is available but alerts any code reviewer that something unusual is happening.

Takeaway: fix the origin, not the generator — ToolAcre is served over HTTPS, so its generator runs in a secure context by design

Better yet, it refuses to generate an identifier if the secure context is truly unavailable (in environments where getRandomValues is also not available, which is rare but possible in older or embedded systems). Migrating an existing system to HTTPS to support secure cryptography APIs is a common project. Start with the origin where UUIDs are generated (your authentication server, API backend, or key application service). Acquire a TLS certificate (Let's Encrypt provides them for free). Configure your web server to serve HTTPS by default and redirect HTTP requests to HTTPS. Test with multiple browsers and API clients to ensure everything works. Then audit your code for any remaining crypto APIs that might be called on unencrypted pages and fix them. The ToolAcre generator assumes HTTPS; if you are using it, you are already part of the way there.