Node.js — error:0308010C digital envelope routines::unsupported
Fix error:0308010C digital envelope routines::unsupported by mapping it to the OpenSSL 3 md4 removal, then upgrade the toolchain or enable the legacy provider deliberately.
Symptoms
- error:0308010C:digital envelope routines::unsupported appears during a webpack 4 build after upgrading Node to 17 or later
- The build fails at the hashing step of an older toolchain (webpack 4, vue-cli 4, react-scripts 4) while the source code is untouched
- Stack frames point into crypto.createHash with the md4 algorithm
Possible causes
- Node 17+ bundles OpenSSL 3, which removes legacy algorithms like md4 from the default provider
- The toolchain hashes content with md4 to generate cache keys and file digests
- CI moved to a newer Node image while local development stayed on Node 16, so the failure appears only in CI
Diagnosis steps
Confirm the algorithm behind the failure
node -e "require('crypto').createHash('md4')" 2>&1 | head -3Interpretation
Reproducing the same error on a bare createHash call proves the failure is the runtime crypto policy, not the bundler logic.
Check the Node version boundary
node --version && docker run --rm node:16 node --version 2>/dev/nullInterpretation
A failure on 17+ and success on 16 pins the cause to the OpenSSL 3 transition bundled with Node 17.
Locate the md4 usage in the toolchain
grep -R "md4" node_modules/webpack/lib/util/ | head -5Interpretation
Finding createHash('md4') inside the installed dependency confirms which layer must be upgraded or worked around.
Root cause
Node 17 and later bundle OpenSSL 3, which moves md4 and other legacy algorithms out of the default provider. The build tooling computes integrity hashes with md4, and createHash throws error:0308010C because the algorithm is unavailable without the legacy provider. The regression arrives with the runtime upgrade, not with any code change.
Fix
Prefer upgrading the toolchain: webpack 5, vue-cli 5, react-scripts 5 all replaced md4 hashing. When an upgrade is not immediately possible, run with NODE_OPTIONS=--openssl-legacy-provider as a scoped, temporary workaround pinned to the build step only, and plan the upgrade with a sunset date.
Risk note
The legacy provider re-enables deprecated, unvalidated crypto paths process-wide. Scope it to the build command, never to production servers, and time-box it.
Verify the fix
Run the full build on the new toolchain without the flag and confirm success; if using the workaround, assert the flag exists only in the build script and document its removal date.
Symptom walkthrough
The hex blob decodes to a specific decision:
Error: error:0308010C:digital envelope routines::unsupported
at new Hash (node:internal/crypto/hash:71:19)
0308010C means the provider layer rejected the requested digest. Reproduce it in one line on Node 18:
node -e "require('crypto').createHash('md4')"
# throws error:0308010C ... unsupported
The same line on Node 16 succeeds. That version pair is the entire story: OpenSSL 3 removed md4 from the default provider, and the installed webpack 4 computes its cache digests with md4. The stack usually lands in webpack/lib/util/createHash.js, naming both the victim and the algorithm.
Fix in practice
The durable fix is upgrading the hash consumer. Webpack 5 switched to xxhash64 and md5 fallbacks; vue-cli 5 and react-scripts 5 inherited the fix. Check the boundary first:
node -e "require('crypto').createHash('md4')" 2>/dev/null && echo ok || echo affected
For the transition period, scope the workaround to the build command only:
{
"scripts": {
"build": "NODE_OPTIONS=--openssl-legacy-provider webpack --mode production"
}
}
Add a CI guard so the flag cannot silently become permanent: a scheduled build that runs without the flag and files an issue while it fails. Do not set NODE_OPTIONS globally in the image, and never apply it to production servers — the flag re-enables deprecated crypto paths for the whole process.
Verification
After upgrading webpack (or the scaffolding tool), run the production build with no NODE_OPTIONS at all and confirm success and stable output hashes across two consecutive builds. If the workaround is still in place, grep the repository for every occurrence of the flag: it must appear only in build scripts, never in runtime Dockerfiles or service definitions. Attach the planned removal date to those scripts so the debt is visible, and let the scheduled no-flag CI job turn red the day the upgrade lands.