Skip to content
DevOps2026-09-086 min read

error:0308010C digital envelope routines::unsupported — Fixing Node 17+ Breakage in Older Projects

Symptom: worked yesterday, npm run dev explodes today

Error: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:71:19)
    at Object.createHash (node:crypto:130:10)
    at BulkCacheDecorator.cheaderHash (...)   ← inside webpack 4

One-line diagnosis: your Node version is 17+, and the webpack 4 inside the project still uses MD4 — a hash algorithm that OpenSSL 3.0 removed from its defaults. Your application code is usually fine; the runtime and the bundler disagree.

Common triggers:

  • New machine or reinstall; nvm / fnm defaulted to the latest LTS (18 / 20 / 22)
  • CI base image upgrade (node:16 → node:20)
  • A Dockerfile that says FROM node:latest
  • Inheriting a legacy project (Vue CLI 4, create-react-app 4, raw webpack 4)

Confirm with:

node -v
npm ls webpack

If Node ≥ 17 and webpack is 4.x, you are in this failure class.

Root cause: OpenSSL 3.0 vs MD4

Starting with Node 17, Node bundles OpenSSL 3.0. OpenSSL 3.0 moved legacy algorithms like MD4 into the "legacy provider," which is not loaded by default. webpack 4 computes module hashes with crypto.createHash('md4') — so the first call throws.

Decode the hex in the error: 0308010C is ERR_OSSL_EVP_UNSUPPORTED. It is not only webpack 4 — old grunt, gulp-rev, and some crypto-js releases hit the same wall.

Fix 1: enable the legacy provider (fast unblock)

Pass --openssl-legacy-provider so Node loads MD4 again:

# macOS / Linux
export NODE_OPTIONS=--openssl-legacy-provider
npm run dev

# Windows CMD
set NODE_OPTIONS=--openssl-legacy-provider

# Windows PowerShell
$env:NODE_OPTIONS="--openssl-legacy-provider"

The durable version lives in package.json (requires cross-env as a devDependency):

{
  "scripts": {
    "dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve",
    "build": "cross-env NODE_OPTIONS=--openssl-legacy-provider vue-cli-service build"
  }
}

When it fits: the project has a short remaining life, or you need a green build today. Tradeoff: you keep running on a deprecated hash path — fine as a bridge, not as a destination. The flag still works on current LTS lines, including Node 22, but it does not remove the underlying debt.

Fix 2: upgrade to webpack 5 (the durable fix)

Project typeUpgrade path
Vue CLI 4vue upgrade to Vue CLI 5 (webpack 5 built in)
CRA 4migrate to CRA 5, or jump to Vite
Hand-rolled webpack 4npm i webpack@5 webpack-cli@4 -D, then walk breaking changes

webpack 5 switched default hashing to xxhash64 / sha256, compatible with OpenSSL 3. You also get persistent caching and faster production builds.

Post-upgrade gotcha: missing assert / buffer / process — webpack 5 no longer auto-polyfills Node core modules. Add them via resolve.fallback or npm shims as errors surface.

Fix 3: pin an older Node (stopgap only)

nvm install 16
nvm use 16
node -v   # v16.x

Add a .nvmrc at the repo root and mirror it in CI / Docker:

16
FROM node:16-alpine

Warning: Node 16 reached end-of-life in September 2023 and receives no security patches. Use it only as a bridge while the webpack 5 (or Vite) migration is scheduled — never as a destination.

Choosing between the three

FixCostRiskTypical use
--openssl-legacy-providerMinutesLow (process-scoped)Unblock today, short maintenance window
Upgrade webpack 5Hours–daysMedium (build drift)Projects that still ship features
Pin EOL NodeMinutesHigh (no security patches)Temporary CI parity while upgrading

Node 22, Next.js, and adjacent stacks

  • Next.js App Router / Turbopack projects on modern Next rarely hit this — they are not on webpack 4. If you see 0308010C inside a monorepo, check which package still pulls webpack 4 (npm ls webpack from the failing workspace).
  • Custom webpack in Next (next.config.js → webpack) can reintroduce old plugins that call legacy hashes — audit those plugins first.
  • Electron / NW.js builds that embed Node 17+ with an old packager show the same OpenSSL refusal; the same three fixes apply inside the packager's Node, not only on the host CLI.
  • Prefer locking the toolchain with .nvmrc + engines + (optional) Volta / packageManager so every laptop and CI share one story.

Prevention: nail the Node version to the project

The underlying problem is runtime drift. Three lines of defense:

  1. .nvmrc plus engines in package.json:
{
  "engines": { "node": ">=20 <21" }
}
  1. Enforce it in CI (engine-strict=true in .npmrc) so a wrong Node fails loudly instead of producing cryptic crypto errors
  2. Never use node:latest in a Dockerfile — pin the major (and preferably minor) version

Why this error became an epidemic in 2022–2023

OpenSSL 3.0 removed legacy algorithms (MD4 among them) from the default provider — webpack 4's hashing used MD4, and every Node 17+ run of a webpack 4 project hit this at once. The message names none of that ("digital envelope routines::unsupported" — a provider-level refusal phrased as a crypto mystery). Priority order: upgrade the bundler (removes the MD4 dependency), legacy-provider flag (unblocks today), pin EOL Node (defers everything). Lesson: hash algorithms inside build tools are invisible dependencies — "our app doesn't use MD4" can be true of the app and false of the build.

Checklist

  1. node -v — is it ≥17? Is webpack 4.x (npm ls webpack)?
  2. Error contains 0308010C / digital envelope routines — confirmed diagnosis
  3. Emergency: NODE_OPTIONS=--openssl-legacy-provider to get running
  4. Schedule the webpack 5 (or Vite) upgrade; pin EOL Node only as a bridge
  5. Re-check .nvmrc, engines, and Dockerfile agree on the same Node major

What the legacy-provider flag does not fix

--openssl-legacy-provider re-enables algorithms OpenSSL 3 moved out of the default provider. It does not:

  • Make an abandoned webpack 4 toolchain secure or supported long-term
  • Fix native addons compiled against a different OpenSSL
  • Replace upgrading CI images that still pull node:16 while laptops run 20

Use it as a bridge on the build machine that is failing today, then remove it when the bundler no longer needs MD4-era hashing. Leaving the flag in production Dockerfiles silently papers over the next OpenSSL policy change.

Triage notes

The fix-chooser table slices by cost, risk, and typical use, but the body repeats one sentence: the legacy provider and pinning an EOL Node are stopgaps, not destinations — the priority is upgrading the bundler to drop the MD4 dependency. The intro's trigger list (new machine, CI base-image bump, FROM node:latest) all point to the same diagnosis, environment drift, which is why all three preventive lines (.nvmrc, engine-strict, a pinned Dockerfile base image) are version pins. The epidemic section's lesson deserves a reread: hashes inside build tooling are invisible dependencies — "our app doesn't use MD4" can be true of the app and false of the build.


Provided by ToolVault. Related: ERESOLVE conflicts, npm registry mirror guide, Cannot find module. See the homepage for more developer tools.