ERR_OSSL_EVP_UNSUPPORTED / error:0308010C — Find the Crypto Consumer

ERR_OSSL_EVP_UNSUPPORTED means a crypto operation requested through Node.js is not supported by the active OpenSSL configuration. The well-known error:0308010C:digital envelope routines::unsupported fingerprint became common around the Node/OpenSSL 3 transition, but the useful question is not “which flag hides it?” It is which dependency, build tool or application code requested the unsupported crypto behavior?

Node.js · OpenSSL · cryptoWebpack · build tools · applicationLast reviewed Sep 27, 2026
🎯 This guide matches when:
Node exits with ERR_OSSL_EVP_UNSUPPORTED.The stack may include error:0308010C:digital envelope routines::unsupported, createHash, crypto, webpack or another build/runtime dependency.The failure appeared after changing Node/runtime/container/CI environment, or an older dependency now runs against a newer crypto policy.
Find who owns the unsupported crypto request
Started after Node/runtime upgrade →Confirm the runtime/OpenSSL boundary, then identify the first project dependency in the stack.Stack points into webpack/build tooling →Upgrade the owning toolchain/dependency rather than making the legacy provider permanent.Stack points into your application crypto →Inspect the requested algorithm/key/operation and migrate to supported crypto behavior.Legacy provider makes the build run →Treat that as diagnostic evidence and a temporary compatibility bridge, then remove it.Error text differs / TLS or certificate failure →Do not force this guide onto a different OpenSSL/TLS problem just because the stack mentions crypto.
Difficulty: ModerateRisk: Medium if compatibility flags become permanent
🔎 Quick Check — capture the runtime boundary and stack owner
node -p "process.version" node -p "process.versions.openssl" node -p "process.execArgv"

Then rerun the exact failing command and read the first meaningful project/tool frame above Node internals. Preserve the full error code and stack. A Node/OpenSSL version pair tells you the boundary; the stack tells you who is asking for the crypto operation.

Guardrail: Never turn --openssl-legacy-provider into the permanent fix before identifying what actually requires legacy crypto behavior.

Verify: You know the Node version, OpenSSL version, exact failing command and likely crypto consumer.

Did you identify the runtime and likely owner?
ADSENSE · reserved slot after the first useful step

Diagnostic flow — boundary → owner → supported remediation

1
Runtime/OpenSSL boundary
What Node and OpenSSL versions run the failing command?
2
Exact crypto consumer
Which stack frame/dependency/application code requests the operation?
3
Owner
Is it your code, a direct dependency, framework/build tool or transitive package?
4
Supported remediation
Upgrade/migrate the owner or crypto behavior using supported versions.
5
Bridge removed
Run without legacy-provider compatibility and verify the exact original workflow.

Fix #2 — confirm whether the runtime boundary changed

Node 17 introduced OpenSSL 3.0 and documented tighter restrictions on allowed algorithms/key sizes; Node exposed --openssl-legacy-provider as a temporary workaround for applications affected by those restrictions. Do not assume every current occurrence is “a Node 17 bug”: modern projects may hit the same error family when old crypto assumptions meet a newer Node/OpenSSL policy.

Compare the failing environment with the last known working one: local vs CI, Docker base image, build image, deployment runtime and version-manager selection. Check what actually runs rather than what package.json merely declares. A lockfile can be identical while the Node/OpenSSL runtime changes underneath it. process.execPath identifies the executable running that diagnostic process; for npm scripts, also print the same values inside the failing script/job because shell PATH/version-manager behavior can differ from your interactive terminal.

Production version choice: do not “fix” this by permanently downgrading to an old Node major. Node's current release policy says production applications should use Active LTS or Maintenance LTS. As of Sep 27, 2026, Node 24 and 22 are LTS, Node 26 is Current, and Node 20 and older are EOL. Do not select a major only because it is newest: choose a currently supported LTS release that your framework/toolchain supports.

For containers and CI, record the image tag/digest and run the version check inside the failing job/container. A developer shell's node -v does not prove what a Docker build stage, CI runner or deployment image executes.

node -p "process.version + ' / OpenSSL ' + process.versions.openssl" which node node -p "process.execPath"

Verify: You can explain whether the failure correlates with a runtime/environment change and which Node binary executes the failing workflow.

Fix #3 — if build tooling owns it, repair the toolchain

If the first useful stack frames point into webpack, a framework CLI, loader/plugin or another dependency, identify the owning package and its installed version. Do not upgrade every package blindly. Trace the direct/transitive relationship, then check the framework/tool's supported Node matrix and release path.

npm ls webpack npm explain webpack npm ls <package-from-stack>
MD4/webpack is a historical case, not the definition of this error. Webpack has historically exposed output.hashFunction with md4 behavior, and webpack 5.54+ added xxhash64 support. That explains many famous Node/OpenSSL 3 build failures, but ERR_OSSL_EVP_UNSUPPORTED itself only tells you that an EVP/crypto operation is unsupported under the active provider/policy. Confirm the stack owner before applying webpack-specific advice.

If webpack is actually the owner, prefer the framework/webpack upgrade path supported by that project. Webpack's current documentation still documents output.hashFunction with md4 as the baseline option and notes xxhash64 support from webpack 5.54+, so the exact framework/configuration still matters. Avoid cargo-cult edits such as forcing a hash function in generated configuration that the framework owns or will overwrite.

Verify: After a supported dependency/framework upgrade, rerun the exact build under the intended Node version without legacy-provider compatibility.

Fix #4 — if your application owns the crypto call, migrate the crypto behavior

If the stack reaches your own crypto.createHash(), signing, encryption or key-loading code, inspect the exact algorithm, key format/size and operation being requested. Do not replace an algorithm merely because another name “works”; crypto choices are protocol/security decisions and may need compatibility with stored data or external systems.

Use Node's supported crypto APIs and the algorithm/protocol requirements appropriate to your application. If this protects real credentials, signatures or encrypted data, treat migration as a security/protocol change—not a syntax fix. Identify whether the algorithm is used for non-security build hashing, password hashing, signatures, encryption, key derivation or an external protocol. Those use cases are not interchangeable.

Crypto migration safety: do not silently change algorithms for persisted hashes, signatures, encrypted records or protocol-visible values. Determine compatibility, re-key/re-hash or versioning requirements, test old and new data paths, and involve the system's security owner when the choice affects real confidentiality/authentication. A build-content hash can have very different requirements from a password or signature algorithm.

Verify: The same application test succeeds on the intended Node/OpenSSL runtime using the supported crypto design, and any required interoperability/security tests still pass.

Fix #5 — use legacy provider only as a diagnostic/temporary bridge

Node provides the --openssl-legacy-provider option to enable OpenSSL 3's legacy provider. Node introduced it specifically as a temporary workaround for tightened OpenSSL 3 restrictions. If enabling it makes the exact failing workflow succeed, that is strong evidence that legacy crypto behavior is involved—but it still does not tell you which package owns the request.

Security boundary: the legacy provider expands the set of legacy cryptographic implementations available to that Node process. Treat the flag as a scoped compatibility exception, not as a security upgrade. Do not infer that every enabled legacy algorithm is appropriate for security-sensitive application use.
node --openssl-legacy-provider your-script.js

For npm scripts, environment-specific ways of setting NODE_OPTIONS differ across shells and CI systems. Node processes NODE_OPTIONS before command-line options; command-line singleton options can override the corresponding environment option. That makes a hidden NODE_OPTIONS in a CI secret/variable, Docker ENV, shell profile or process manager easy to miss while comparing environments.

node -p "process.env.NODE_OPTIONS || '(unset)'" node -p "process.execArgv"

Important: process.execArgv shows Node-specific options passed explicitly for the current process; inspect process.env.NODE_OPTIONS separately when hunting inherited compatibility flags. Prefer an explicit, short-lived diagnostic run and document why it exists. Avoid silently baking the legacy-provider flag into global shell profiles, base images, organization-wide CI variables or production process-manager configuration.

Verify: The bridge changes the failure as expected, you have identified the owner, and the final supported remediation works after the bridge is removed.

Fix #6 — make sure this is actually the same OpenSSL problem

ERR_OSSL_EVP_UNSUPPORTED is specific enough to investigate EVP/crypto support, but “OpenSSL error” is not. Certificate verification failures, TLS protocol negotiation, native-addon ABI problems and missing shared libraries have different evidence and fixes. Preserve the complete Node error code and top stack frames before applying this guide.

Verify: The final diagnosis explains the original error code and stack—not merely that a compatibility flag made one command proceed.

What not to do

Do not make --openssl-legacy-provider a permanent global default before identifying the crypto consumer.Do not pin an obsolete/EOL Node release indefinitely just because it predates the OpenSSL policy change; use a supported LTS release compatible with the application/toolchain.Do not hide --openssl-legacy-provider in global NODE_OPTIONS, CI organization variables, Docker base images or production process-manager config.Do not upgrade or downgrade the entire dependency tree blindly; trace the package/application frame that owns the crypto call.Do not replace application cryptography with an arbitrary algorithm solely to clear the exception.Do not assume every error:0308010C stack is webpack without checking the actual stack and dependency tree.Do not confuse npm ERESOLVE dependency-resolution failures with this runtime crypto compatibility failure.

Still seeing ERR_OSSL_EVP_UNSUPPORTED?

Escalate with the runtime boundary and ownership evidence. “It works with legacy provider” is useful evidence, but it is not a root-cause report.

Before escalating, capture:complete Node error code and the first meaningful stack framesNode and process.versions.openssl values from the failing environmentexact failing command and whether local/CI/container environments differpackage/version owning the first non-Node crypto framerelevant npm ls/npm explain relationshipwhether legacy provider changes the result, used only as diagnostic evidencelast known working runtime/toolchain combination if knowneffective NODE_OPTIONS and whether the flag comes from shell, CI, container or process-manager configurationcontainer image tag/digest or CI runtime identity when the failing environment is not local

Official references

Still stuck? Ask the community

Share the Node/OpenSSL versions, exact command, package/tool frame and a redacted stack trace.

Keep it safe: redact tokens, private repository paths, environment secrets, keys, certificates and proprietary source details.
Powered by GitHub DiscussionsSign in with GitHub to comment. Reading comments does not require sign-in.

Loading community discussion…