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 code isn't broken; your runtime and your build tool disagree.
Common triggers:
- New machine or reinstall, and
nvmdefaulted to the latest LTS (Node 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)
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 very first call throws.
Decode the hex in the error: 0308010C is ERR_OSSL_EVP_UNSUPPORTED. And it's not only webpack 4 — old versions of grunt, gulp-rev, and some crypto-js releases hit the same wall.
Fix 1: enable the legacy provider (5-minute 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 and works cross-platform (requires the cross-env 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 to use it: the project has a few months of life left, or you're just passing through. The tradeoff: MD4 is considered insecure, so you're extending technical debt — fine for now, but the upgrade question comes back eventually.
Fix 2: upgrade to webpack 5 (the real fix)
| Project type | Upgrade path |
|---|---|
| Vue CLI 4 | vue upgrade to Vue CLI 5 (webpack 5 built in) |
| CRA 4 | migrate to CRA 5, or jump straight to Vite |
| Hand-rolled webpack 4 | npm i webpack@5 webpack-cli@4 -D, then work through breaking changes |
webpack 5 switched its default hashing to xxhash64/sha256, fully compatible with OpenSSL 3. You also get persistent caching and faster production builds as a bonus.
Post-upgrade gotcha: PolyfillPlugin errors about missing assert/process — webpack 5 no longer auto-polyfills Node core modules. Add them one by one via resolve.fallback or install the npm shims as errors surface.
Fix 3: pin Node 16 (stopgap)
nvm install 16
nvm use 16
node -v # v16.x
Add a .nvmrc at the repo root:
16
Mirror it in CI and your Dockerfile:
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 upgrade is scheduled — never as a destination.
Choosing between the three
| Fix | Cost | Risk | Best for |
|---|---|---|---|
| --openssl-legacy-provider | 5 min | Low (process-scoped) | Fast unblock, legacy maintenance mode |
| Upgrade webpack 5 | 0.5–3 days | Medium (build behavior drift) | Projects with a future |
| Pin Node 16 | 10 min | High (no security patches) | CI parity, temporary holdover |
Prevention: nail the Node version to the project
The underlying problem is runtime drift. Three lines of defense:
.nvmrcplus theenginesfield inpackage.json:
{
"engines": { "node": ">=18 <19" }
}
- Enforce it in CI (add
engine-strict=trueto.npmrc) so a wrong Node version fails loudly instead of producing cryptic errors; - Never use
node:latestin a Dockerfile — pin the major version.
Checklist
node -v— is it ≥17? Is webpack 4.x (npm ls webpack)?- Error contains
0308010C/digital envelope routines— confirmed diagnosis - Emergency:
NODE_OPTIONS=--openssl-legacy-providerto get running - Schedule the webpack 5 upgrade, or
.nvmrcpin to 16 as a stopgap - Re-check
.nvmrc,engines, and Dockerfile agree on the same Node version
Provided by ToolVault. Related: ERESOLVE conflicts, npm registry mirror guide, Cannot find module. See the homepage for more developer tools.
Related Tools
Related Articles
Permission denied (publickey): 6 Reasons Git Push Fails Over SSH (and the Fix for Each)
git clone or push rejected with Permission denied (publickey) fatal: Could not read from remote repository? Covers missing keygen, key not loaded in the agent, public key not added to GitHub/GitLab, multi-account key routing with ~/.ssh/config, deploy key limits, and wrong remote URLs — with ssh -v diagnostics.
ECONNREFUSED: Connection Refused — 5 Causes Explained (Including Docker)
Node, Java, or curl reporting connect ECONNREFUSED 127.0.0.1:3306? It means nothing is listening on that port. Covers service not running, wrong port, 127.0.0.1-only binding, Docker container networking, and firewall REJECT rules — with ss/lsof diagnostic commands.
Docker permission denied on docker.sock: the Right Fix (and Its Security Cost)
docker commands fail with permission denied while trying to connect to the Docker daemon socket? The cause: your user isn't in the docker group. One-step fix with usermod -aG, the newgrp trick to avoid re-login, why chmod 777 is a trap, and why docker group membership equals passwordless root — plus the rootless alternative.