Skip to content

Fix: Error: error:0308010C:digital envelope routines::unsupported

FixDevs · (Updated: )

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 MD4 Hash That OpenSSL 3 Refuses

Personally, I think this error is one of the cleanest cause-and-effect bugs in modern JavaScript tooling: Node 17 upgraded OpenSSL to 3.0, OpenSSL 3.0 unloads MD4 by default, webpack 4 calls MD4 for cache keys, the build crashes. The fix is editorial: either upgrade webpack to a version that uses a supported hash, or re-enable MD4 explicitly. 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.

Quick Reference Before You Dive In

If you arrived here from Google with a fresh OpenSSL error, the five facts that resolve roughly 90 percent of cases:

  1. The error means Node 17+ (OpenSSL 3.0) refused MD4. webpack 4 needs MD4 for cache keys. Upgrading webpack to v5 makes the error vanish. The Node.js 17 release notes and the OpenSSL 3.0 migration guide are the canonical sources.
  2. The TEMPORARY fix is NODE_OPTIONS=--openssl-legacy-provider. Add it to package.json scripts with cross-env for cross-platform compatibility. Treat it as a transitional measure, not a permanent solution.
  3. cross-env is required for Windows compatibility. NODE_OPTIONS=value cmd (POSIX inline syntax) does not work in PowerShell or cmd.exe.
  4. webpack 5+ uses xxhash64 by default. Upgrading to webpack 5.54+ removes MD4 entirely from the codebase.
  5. CRA 4, Vue CLI 4, Angular CLI <15, Storybook 6 all use webpack 4 internally. Upgrading the framework to its current major version fixes the error at the root.

The rest of this article walks through each cause in detail, plus the failure modes most other guides skip.

Why MD4 Disappeared in Node 17

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 times faster than 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:

  1. You upgrade Node.js to 17, 18, 19, 20, 21, or 22.
  2. Your project still uses webpack 4 or an older build tool.
  3. 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 build

Or inline:

NODE_OPTIONS=--openssl-legacy-provider npm run build

Windows Command Prompt syntax:

set NODE_OPTIONS=--openssl-legacy-provider
npm run build

Windows PowerShell syntax:

$env:NODE_OPTIONS = "--openssl-legacy-provider"
npm run build

WSL2. 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.

When to Use Which Fix

The next eight sections cover the fixes in detail. The table below maps your situation to the recommended fix.

Your situationRecommended fixWhy
Can upgrade build toolingFix 1: upgrade webpack to v5Removes MD4 dependency
Need it working NOW, will fix laterFix 2: NODE_OPTIONS=--openssl-legacy-providerQuick re-enable
Cannot upgrade tools and stuckFix 3: downgrade Node to 16 (temporary)OpenSSL 1.1 still supports MD4
Webpack 4 config, want to keepFix 4: monkey-patch crypto.createHash to use SHA-256Replace MD4 calls in-process
Create React App projectFix 5: upgrade to CRA 5 or migrate to ViteCRA 4 uses webpack 4
Angular / Vue / Gatsby / Storybook / Nuxt 2Fix 6: upgrade to the framework’s current majorEach has a fixed version
Docker build failsFix 7: set ENV NODE_OPTIONS in DockerfilePer-stage env
GitHub Actions / GitLab CI failsFix 8: env: at job or step levelCI does not inherit shell vars

If multiple rows apply, pick the topmost one.

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-dev

For Create React App, upgrade to v5:

npx react-scripts@latest start

Or migrate to a new project:

npx create-react-app my-app

For 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@latest

For Angular CLI:

ng update @angular/cli @angular/core

Angular 15+ uses webpack 5 internally and does not have this issue.

A pragmatic observation: I treat the webpack 5 upgrade as a forcing function whenever this error fires. The migration cost is real (loaders change, plugins update, configuration shifts) but the upside is significant: persistent caching, faster builds, tree-shaking improvements, and module federation. Every time I have used --openssl-legacy-provider as the “permanent” fix, the team has come back to it within 18 months because something else broke. Schedule the migration; do not put it off.

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 start

Windows (Command Prompt):

set NODE_OPTIONS=--openssl-legacy-provider
npm start

Windows (PowerShell):

$env:NODE_OPTIONS = "--openssl-legacy-provider"
npm start

Add 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"
  }
}

A pattern I have seen repeated: teams add --openssl-legacy-provider to their build scripts as a “quick fix,” do not schedule a follow-up, and discover years later that the flag is still there and re-enables algorithms with known weaknesses. The flag is a transitional bridge; it is not the destination. Add a TODO with a date attached to every use of it, and revisit on the date.

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@16

Node.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" > .nvmrc

Then 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 --save

Check 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-provider

Option 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@latest

Gatsby 4+ uses webpack 5.

Storybook:

npx storybook@latest upgrade

Storybook 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-provider

Better 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/html

ENV 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 build

Or pin to Node.js 16:

- uses: actions/setup-node@v4
  with:
    node-version: 16

If the CI step fails with a permission error rather than the OpenSSL error, the workflow runner user lacks write access to the working directory.

Stranger Causes I Have Tracked Down

Check for PostCSS plugins. Some older PostCSS plugins use MD4 directly. Update them:

npm update postcss postcss-loader css-loader

Check for mini-css-extract-plugin:

npm install mini-css-extract-plugin@latest --save-dev

Check for sass-loader issues:

npm install sass-loader@latest sass --save-dev

Audit 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.

What Other Tutorials Get Wrong About This Error

Most tutorials list the same fixes but frame them in ways that produce subtle bugs.

They recommend --openssl-legacy-provider as the fix, not as a workaround. Re-enabling deprecated cryptography is a TRANSITIONAL bridge. Articles that present the flag as “the solution” leave teams with insecure defaults that compound over time.

They omit cross-env for Windows. NODE_OPTIONS=value cmd inline syntax fails in PowerShell and cmd.exe. Articles that show only the POSIX form leave Windows team members debugging a different error.

They miss the framework-level fixes. CRA 5, Vue CLI 5, Angular 15+, Storybook 7+, Gatsby 4+ all moved off webpack 4. Articles that focus only on NODE_OPTIONS miss the durable framework upgrade.

They confuse Docker base image with Node version. node:18-alpine and node:18-bullseye both ship Node 18 with OpenSSL 3.0. Switching base images does nothing; you must change the Node major version. Articles that suggest “try Alpine” miss this.

They miss CI environment variable propagation. NODE_OPTIONS in your shell does NOT propagate to GitHub Actions, GitLab CI, or other CI runners. Articles that show local fixes without flagging CI separately leave teams with green local builds and red CI.

They miss test runner inheritance. Jest 27 and below trigger the same error in tests. Storybook 6 triggers it in start-storybook. Articles that focus only on the main build script miss these auxiliary workflows.

Frequently Asked Questions

What does 0308010C:digital envelope routines::unsupported actually mean?

0308010C is OpenSSL’s hex error code for “EVP routines: unsupported algorithm.” digital envelope routines is OpenSSL’s internal name for the EVP subsystem. In plain English: OpenSSL was asked to compute an MD4 hash (or another algorithm in the legacy provider) and refused because the legacy provider is not loaded.

Why did Node 17 do this?

Node 17 upgraded to OpenSSL 3.0, which introduced a provider model. The “default” provider has modern algorithms; the “legacy” provider has deprecated algorithms (MD4, MD2, Blowfish, RC4). The legacy provider ships with OpenSSL but is not loaded by default, so calling deprecated algorithms now requires explicit opt-in.

Is --openssl-legacy-provider a security risk?

It re-enables algorithms that have known cryptographic weaknesses. For webpack’s specific use (cache key hashing of build inputs), the weakness is not exploitable in practice. For applications that use MD4 elsewhere for security-sensitive operations, yes. Audit what else in your dependency tree uses the legacy provider before flipping it on globally.

Should I downgrade to Node 16?

Only as a short-term measure. Node 16 reached end-of-life in September 2023; running it in production lacks security patches. The right answer is to upgrade webpack to v5 (which removes the MD4 dependency) and then move to current Node LTS.

Why does my package.json work locally but fail in CI?

CI runners spawn non-login shells that skip your .bashrc. NODE_OPTIONS set in your shell does not propagate to GitHub Actions, GitLab CI, etc. Set env: at the job or step level in the workflow file, or use cross-env in package.json scripts so the env var is set per-command.

Will this error go away in future Node versions?

No, it gets worse. Node will continue to ship OpenSSL 3.x; OpenSSL 4 (whenever it ships) will remove the legacy provider entirely. The flag is a transitional bridge with a known expiration. Plan the migration off webpack 4 now; do not wait.

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.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles