Fix: Error: error:0308010C:digital envelope routines::unsupported
Part of: JavaScript & TypeScript Errors
Quick Answer
How to fix the Node.js digital envelope routines unsupported error caused by OpenSSL 3.0 changes in Node.js 17+, with solutions for webpack, Vite, React, and Angular.
The Error
You run npm start, npm run build, or a webpack command and get:
Error: error:0308010C:digital envelope routines::unsupported
at new Hash (node:internal/crypto/hash:79:19)
at Object.createHash (node:crypto:139:10)
at module.exports (/project/node_modules/webpack/lib/util/createHash.js:135:53)Or the full stack trace ending with:
opensslErrorStack: [
'error:03000086:digital envelope routines::initialization error'
],
library: 'digital envelope routines',
reason: 'unsupported',
code: 'ERR_OSSL_EVP_UNSUPPORTED'Or variations:
Error: error:0308010C:digital envelope routines::unsupported
at Hash.update (node:internal/crypto/hash:...)Node.js 17+ uses OpenSSL 3.0, which removed support for the MD4 hash algorithm that older versions of webpack (and other tools) use internally. The tool tries to create an MD4 hash, OpenSSL 3.0 refuses, and the build crashes.
Why This Happens
Node.js 17 (October 2021) upgraded from OpenSSL 1.1 to OpenSSL 3.0. OpenSSL 3.0 introduced a “provider” model that splits cryptographic algorithms into a default provider (modern, recommended algorithms) and a legacy provider (deprecated algorithms like MD4, MD2, Blowfish, RC4). The legacy provider is shipped but not loaded by default. Calling crypto.createHash('md4') now returns the ERR_OSSL_EVP_UNSUPPORTED error instead of producing a hash.
Webpack 4 uses MD4 internally to fingerprint modules for cache invalidation. The choice predates the OpenSSL 3.0 split and was made because MD4 is fast (about 2× SHA-256) and webpack does not need cryptographic security from the hash — it only needs collision resistance for ordinary build inputs. The algorithm is fine for that purpose, but OpenSSL 3.0 removed it because real applications were still using it for security-sensitive operations elsewhere.
This affects:
- webpack 4 (and tools built on it: Create React App 4, Vue CLI 4, Angular CLI <15)
- Some older PostCSS plugins
- Older versions of
css-minimizer-webpack-plugin - Any tool that calls
crypto.createHash('md4')
You hit this error when:
- You upgrade Node.js to 17, 18, 19, 20, 21, or 22.
- Your project still uses webpack 4 or an older build tool.
- The tool calls an unsupported hash algorithm.
The error never mentions MD4 by name, which makes diagnosis harder. The string digital envelope routines is OpenSSL’s internal subsystem name, not something a JavaScript developer recognizes. The 0308010C code translates (via openssl errstr 0308010C) to “EVP routines: unsupported.”
Platform and Environment Differences
The same project hits or skips this error depending on which Node version runs the build, which shell sets the environment variable, and which CI image wraps the build. The patterns differ enough that fixes that work on one developer’s machine fail silently on a teammate’s.
Node.js 16 and earlier. Ships OpenSSL 1.1, which still supports MD4 in the default provider. Webpack 4 works untouched. Node 16 reached end-of-life in September 2023, so production support is gone, but it still runs.
Node.js 17 (October 2021). First version with OpenSSL 3.0. Released as Current, not LTS, so most teams never deployed it. Still, devcontainers and Docker images that pin to node:17 exist.
Node.js 18 LTS (April 2022). OpenSSL 3.0. First mainstream LTS where this error appears for everyone.
Node.js 20 LTS (April 2023). OpenSSL 3.0.x with periodic updates. Same behavior.
Node.js 22 LTS (April 2024). OpenSSL 3.0.x. Same behavior.
Linux and macOS (bash/zsh) environment variable syntax:
export NODE_OPTIONS=--openssl-legacy-provider
npm run buildOr inline:
NODE_OPTIONS=--openssl-legacy-provider npm run buildWindows Command Prompt syntax:
set NODE_OPTIONS=--openssl-legacy-provider
npm run buildWindows PowerShell syntax:
$env:NODE_OPTIONS = "--openssl-legacy-provider"
npm run buildWSL2. The Linux-style syntax applies inside WSL2. But if you launch your editor (VS Code) on Windows and it spawns a build via a WSL2 terminal, the variables set in PowerShell do not cross the boundary. Set them inside the WSL2 shell or in package.json via cross-env.
Inline NODE_OPTIONS=value cmd syntax does not work in PowerShell or cmd.exe. Only POSIX shells accept the leading KEY=value prefix. On Windows, write the variable in a previous command or use cross-env to keep package.json cross-platform.
webpack 4 vs webpack 5. webpack 5 (October 2020) defaults to xxhash64 for output hashing and md4 only as a legacy fallback. webpack 5.54+ removed even that fallback in favor of xxhash64 everywhere. Upgrading to webpack 5 makes the error vanish without any environment variable.
CI environment variable inheritance. GitHub Actions, GitLab CI, CircleCI, and others all pass environment variables to job steps differently. GitHub Actions reads env: at the step or job level. GitLab CI reads variables: at the job or pipeline level. Setting NODE_OPTIONS in ~/.bashrc on a CI runner has no effect because most CI runners spawn non-login shells that skip .bashrc. Set the variable explicitly in the workflow file.
Cross-env for cross-platform package.json scripts. package.json runs the same command string on every platform. "build": "NODE_OPTIONS=--openssl-legacy-provider webpack" succeeds on Linux/macOS but fails on Windows. cross-env normalizes the syntax:
{
"scripts": {
"build": "cross-env NODE_OPTIONS=--openssl-legacy-provider webpack"
}
}Docker base image. node:18-alpine and node:18-bullseye both ship Node 18 with OpenSSL 3.0. Switching base images does not change OpenSSL behavior — only changing the Node major version (back to 16) does. Pinning a digest like node@sha256:... preserves the OpenSSL version even if the tag floats.
Yarn and pnpm. Both invoke Node with whatever flags NODE_OPTIONS provides. Yarn 1, Yarn 3+, and pnpm 8+ all behave identically here — the issue is the Node OpenSSL provider, not the package manager.
Jest, Mocha, and Storybook. Any test runner or tool that uses webpack internally inherits the same problem. Jest 27 and below use webpack-style hashing for some cache keys; Jest 28+ fixed this. Storybook 6 uses webpack 4; Storybook 7+ uses webpack 5 or Vite.
Fix 1: Upgrade webpack to v5
The best long-term fix. webpack 5 uses a supported hash algorithm by default:
npm install webpack@5 webpack-cli@5 --save-devFor Create React App — upgrade to v5:
npx react-scripts@latest startOr migrate to a new project:
npx create-react-app my-appFor Vue CLI — upgrade or migrate to Vite:
# Upgrade Vue CLI
npm install @vue/cli-service@latest --save-dev
# Or migrate to Vite (recommended)
npm create vue@latestFor Angular CLI:
ng update @angular/cli @angular/coreAngular 15+ uses webpack 5 internally and does not have this issue.
Pro Tip: If your project is on webpack 4, migrating to webpack 5 fixes this error and brings significant performance improvements (persistent caching, tree shaking improvements, module federation). The migration guide is at https://webpack.js.org/migrate/5/.
Fix 2: Use the NODE_OPTIONS Environment Variable
The quickest fix. Tell Node.js to allow the legacy OpenSSL provider:
Linux/macOS:
export NODE_OPTIONS=--openssl-legacy-provider
npm startWindows (Command Prompt):
set NODE_OPTIONS=--openssl-legacy-provider
npm startWindows (PowerShell):
$env:NODE_OPTIONS = "--openssl-legacy-provider"
npm startAdd it to your package.json scripts:
{
"scripts": {
"start": "react-scripts start",
"start:legacy": "NODE_OPTIONS=--openssl-legacy-provider react-scripts start",
"build": "react-scripts build",
"build:legacy": "NODE_OPTIONS=--openssl-legacy-provider react-scripts build"
}
}Cross-platform using cross-env:
npm install cross-env --save-dev{
"scripts": {
"start": "cross-env NODE_OPTIONS=--openssl-legacy-provider react-scripts start",
"build": "cross-env NODE_OPTIONS=--openssl-legacy-provider react-scripts build"
}
}Common Mistake: Relying on
--openssl-legacy-provideras a permanent solution. This flag re-enables deprecated cryptographic algorithms, which may have security implications. Use it as a temporary workaround while planning an upgrade to webpack 5 or a modern build tool.
Fix 3: Downgrade Node.js
If you cannot upgrade your build tools, use a Node.js version that supports the old algorithms:
# Using nvm (Node Version Manager)
nvm install 16
nvm use 16
# Using fnm
fnm install 16
fnm use 16
# Using volta
volta install node@16Node.js 16 LTS is the last version that uses OpenSSL 1.1 by default.
Note: Node.js 16 reached end-of-life in September 2023. Running it in production has security risks. Use this as a temporary measure only.
Use .nvmrc to pin the version for the project:
echo "16" > .nvmrcThen team members run nvm use to switch to the correct version.
Fix 4: Configure webpack to Use a Different Hash
If you are on webpack 4 and cannot upgrade yet, configure the hash function:
// webpack.config.js
const crypto = require("crypto");
// Use SHA-256 instead of MD4
const cryptoOrigCreateHash = crypto.createHash;
crypto.createHash = (algorithm) =>
cryptoOrigCreateHash(algorithm === "md4" ? "sha256" : algorithm);
module.exports = {
// ... your webpack config
};For webpack 5 (if still seeing this error):
// webpack.config.js
module.exports = {
output: {
hashFunction: "xxhash64", // Default in webpack 5.54+
},
};For older webpack 5:
module.exports = {
output: {
hashFunction: "sha256",
},
};Fix 5: Fix Create React App (CRA)
CRA 4 uses webpack 4 internally. You have several options:
Option 1: Upgrade to CRA 5:
npm install react-scripts@5 --saveCheck for breaking changes in your project after upgrading.
Option 2: Use the legacy provider:
{
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
}
}Create a .env file:
NODE_OPTIONS=--openssl-legacy-providerOption 3: Migrate away from CRA:
CRA is no longer actively maintained. Consider migrating to:
- Vite —
npm create vite@latest my-app -- --template react - Next.js —
npx create-next-app@latest - Remix —
npx create-remix@latest
These tools use modern build pipelines (esbuild or SWC) and do not have the OpenSSL issue.
Fix 6: Fix for Specific Frameworks
Angular (older versions):
{
"scripts": {
"start": "ng serve",
"start:legacy": "NODE_OPTIONS=--openssl-legacy-provider ng serve"
}
}Upgrade to Angular 15+ to permanently fix the issue.
Gatsby:
npm install gatsby@latestGatsby 4+ uses webpack 5.
Storybook:
npx storybook@latest upgradeStorybook 7+ uses webpack 5 by default.
Nuxt 2:
{
"scripts": {
"dev": "NODE_OPTIONS=--openssl-legacy-provider nuxt"
}
}Or upgrade to Nuxt 3, which uses Vite.
Fix 7: Fix in Docker
If your Docker build fails with this error:
# Wrong — uses latest Node.js (which has OpenSSL 3.0)
FROM node:22
# Fix Option 1: Use Node.js 16
FROM node:16
# Fix Option 2: Set the environment variable
FROM node:22
ENV NODE_OPTIONS=--openssl-legacy-providerBetter fix — use a multi-stage build with the right Node version:
FROM node:22 AS build
ENV NODE_OPTIONS=--openssl-legacy-provider
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/htmlENV NODE_OPTIONS=... persists for every subsequent RUN in the same stage but does not carry into a different stage. If your runtime stage uses Node (for SSR), set ENV NODE_OPTIONS there too.
Fix 8: Fix in CI/CD
GitHub Actions:
jobs:
build:
runs-on: ubuntu-latest
env:
NODE_OPTIONS: --openssl-legacy-provider
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run buildOr pin to Node.js 16:
- uses: actions/setup-node@v4
with:
node-version: 16If the CI step fails with a permission error rather than the OpenSSL error, the workflow runner user lacks write access to the working directory.
Still Not Working?
Check for PostCSS plugins. Some older PostCSS plugins use MD4 directly. Update them:
npm update postcss postcss-loader css-loaderCheck for mini-css-extract-plugin:
npm install mini-css-extract-plugin@latest --save-devCheck for sass-loader issues:
npm install sass-loader@latest sass --save-devAudit your dependencies for the problematic call:
grep -r "createHash" node_modules --include="*.js" | grep "md4"This shows which packages call createHash('md4'). Update or replace those packages.
Check if the error is from a test runner. Jest, Mocha, and other test runners might also trigger this if they use webpack transforms:
{
"scripts": {
"test": "NODE_OPTIONS=--openssl-legacy-provider jest"
}
}Check the package-lock.json for transitive webpack 4 dependencies. A direct upgrade to webpack 5 does not always bring loaders along. Run npm ls webpack to see every version of webpack in your tree. Multiple webpack copies can produce the error even when your top-level webpack is v5.
Check for a CI image with a frozen Node version. Vercel, Netlify, and Cloudflare Pages let you pin Node via engines in package.json or platform-specific env vars. A build that works locally on Node 16 but fails on the host’s Node 20 means the platform ignored your version pin. Set NODE_VERSION=16 (Netlify) or NODE_VERSION=18 with the legacy flag explicitly.
Check for husky and lint-staged hooks. Pre-commit hooks that run lint or tests can fail with the same OpenSSL error in a shell where NODE_OPTIONS is not exported. Add the flag to the .husky/pre-commit script directly: NODE_OPTIONS=--openssl-legacy-provider npx lint-staged.
For webpack module resolution errors, see Fix: Module not found: Can’t resolve. For JavaScript heap memory issues during builds, see Fix: JavaScript heap out of memory. For Node module loading problems, see Fix: Node: Cannot find module. For npm dependency tree conflicts, see Fix: npm ERESOLVE unable to resolve dependency tree.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Fastify Not Working — 404, Plugin Encapsulation, and Schema Validation Errors
How to fix Fastify issues — route 404 from plugin encapsulation, reply already sent, FST_ERR_VALIDATION, request.body undefined, @fastify/cors, hooks not running, and TypeScript type inference.
Fix: Bun Not Working — Node.js Module Incompatible, Native Addon Fails, or bun test Errors
How to fix Bun runtime issues — Node.js API compatibility, native addons (node-gyp), Bun.serve vs Node http, bun test differences from Jest, and common package incompatibilities.
Fix: Node.js Stream Error — Pipe Not Working, Backpressure, or Premature Close
How to fix Node.js stream issues — pipe and pipeline errors, backpressure handling, Transform streams, async iteration, error propagation, and common stream anti-patterns.
Fix: Webpack Bundle Size Too Large — Reduce JavaScript Bundle for Faster Load Times
How to reduce Webpack bundle size — code splitting, tree shaking, dynamic imports, bundle analysis, moment.js replacement, lodash optimization, and production build configuration.