Direct answer: the July 2026 Node.js security patches are available now. If your service remains on release line 22, 24, or 26, update to at least 22.23.2 LTS, 24.18.1 LTS, or 26.5.1 Current, respectively. Do not try to patch the runtime with npm update: npm manages application packages; it does not replace the Node.js binary.
The Node.js project published the releases on July 29, 2026, after postponing the rollout planned for July 27 and then July 28 because of additional validation and infrastructure issues. The official security advisory confirms 11 CVEs and links to all three patched releases.
Executive summary
- Minimum action: keep the same major release line during the immediate response and move it to 22.23.2, 24.18.1, or 26.5.1. Plan a major-line migration separately so compatibility work does not delay the security fix.
- Highest-priority exposure: Internet-facing HTTP/2, trusted code running with
--permissionwhen an attacker can influence paths or inputs, HTTPS agents that reuse mTLS or distinct identity policies, and Node-based forwarding proxies. - Scope: Node.js rates three issues High, five Medium, and three Low. The advisory does not publish numeric CVSS scores.
- Exploitation status: The official sources reviewed do not confirm active exploitation. That does not make the update optional: several flaws can exhaust memory, terminate the process, or break a security boundary.
- End of life: if you run an EOL line, migrate to a supported line; this advisory provides no public patch for EOL releases.
Patched Node.js release and action by branch
| Branch | Status on Jul 29, 2026 | Patched release | Bundled dependencies | Recommended action |
|---|---|---|---|---|
| 22.x | Maintenance LTS | 22.23.2 LTS | llhttp 9.4.3; undici 6.28.0 | Update to 22.23.2 and schedule a move to a newer LTS based on compatibility. |
| 24.x | Active LTS | 24.18.1 LTS | llhttp 9.4.3; undici 7.29.0 | Update to 24.18.1 and keep line 24 in the normal maintenance cycle. |
| 26.x | Current | 26.5.1 Current | llhttp 9.4.3; undici 8.9.0 | Update to 26.5.1; validate compatibility because Current changes more frequently. |
| EOL | Unsupported publicly | No patch on that line | Not applicable | Migrate to a supported release, preferably an LTS line for production. |
Branch status comes from the official Release Working Group schedule. The distribution list also marks these builds as security releases in the official Node.js release index.
What the 11 CVEs fix
This table separates severity, component, consequence, and affected lines. “Affected” reflects the scope published by Node.js; an individual application's exposure depends on the features it uses and the inputs an attacker can influence.
CVEs in the July 2026 Node.js security release
| CVE | Official severity | Component | Practical impact | Affected lines |
|---|---|---|---|---|
| CVE-2026-56846 | High | HTTP/2 | Retained headers can bypass maxSessionMemory accounting and remotely exhaust memory. | 22.x, 24.x; not 26.x |
| CVE-2026-56848 | High | HTTP/2 / nghttp2 | A reentrant send can trigger a heap-use-after-free while HTTP/2 input is being processed. | 22.x, 24.x, 26.x |
| CVE-2026-58043 | High | Permission Model | Radix-tree prefix matching can grant filesystem reads or writes outside the intended path. | 22.x, 24.x, 26.x |
| CVE-2026-56850 | Medium | HTTPS Agent / mTLS | PFX object-array key collisions can reuse a client identity between requests configured with different certificates. | 22.x, 24.x, 26.x |
| CVE-2026-58040 | Medium | HTTPS Agent / TLS | TLS session reuse can skip hostname verification across different identity policies. | 22.x, 24.x, 26.x |
| CVE-2026-58041 | Medium | node:sqlite / SQLTagStore | A stale iterator can re-execute writes after a prepared statement is reset and rebound. | 24.x, 26.x; not 22.x |
| CVE-2026-58042 | Medium | DNS | dns.resolveAny() can abort the process on a response containing more than 256 A records, causing denial of service. | 22.x, 24.x, 26.x |
| CVE-2026-58045 | Medium | node:zlib | A spoofed TypedArray length can reach an assertion and crash synchronous zlib APIs. | 22.x, 24.x, 26.x |
| CVE-2026-56847 | Low | Permission Model / trace events | Trace events can write logs outside paths authorized by --allow-fs-write. | 22.x, 24.x, 26.x |
| CVE-2026-58039 | Low | Permission Model / process.report | Process reports can write or overwrite files outside the filesystem allowlist. | 22.x, 24.x, 26.x |
| CVE-2026-58044 | Low | HTTP client / proxies | Header truncation can desynchronize requests in Node forwarding proxies that rebuild visible headers and pipe the original body to a reused backend connection. | 22.x, 24.x, 26.x |
The release notes for 22.23.2, 24.18.1, and 26.5.1 document which fixes landed in each line and the bundled llhttp and undici versions.
Prioritize by actual exposure
A raw CVE count cannot set the same priority for every service. Start with the attack surface your deployment actually presents:
- Internet-facing HTTP/2: treat 56846 and 56848 as urgent. Check Node servers that terminate HTTP/2 directly and deployments where a front proxy passes HTTP/2 to Node.
- Permission Model around trusted code with attacker-influenced paths or inputs: prioritize 58043 and review 56847 and 58039 as well. The official Permission Model documentation describes it as a defense-in-depth control for trusted code, not a sandbox for malicious code. Operating-system and container isolation remain independent controls.
- mTLS or multiple identity contexts on one HTTPS agent: prioritize 56850 and 58040, especially for B2B integrations, internal agents, and clients that pool connections.
node:sqlitewith SQLTagStore: 58041 applies only to 24.x and 26.x. Include write behavior and idempotency in validation.- DNS lookups influenced by external input: 58042 can terminate the process when
dns.resolveAny()handles a response containing more than 256 A records. Prioritize services that resolve names supplied by users or third parties. - Objects passed to synchronous zlib APIs: 58045 requires influence over the object or spoofed
TypedArraylength passed to the API. The advisory does not say that ordinary attacker-supplied compressed bytes alone trigger the flaw. - A forwarding proxy implemented in Node: 58044 matters despite its Low rating. The vulnerable pattern combines rebuilt visible headers, the original piped body, and backend connection reuse.
Temporarily disabling unnecessary HTTP/2, separating agents by identity, constraining attacker-influenced inputs, or strengthening isolation can reduce exposure while a maintenance window is prepared. These are partial mitigations, not replacements for a patched runtime.
Safe update procedure
1. Inventory the runtime that actually executes
Do not rely on package.json alone. Record the version and binary path used by your shell, process manager, systemd unit, and every container. Also review .nvmrc, Dockerfile FROM instructions, Compose images, and CI/CD configuration.
node --version
node -p process.execPath
node -p 'JSON.stringify(process.versions)'
command -v node
npm --version
pm2 list
systemctl list-units --type=service --state=running
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'npm --version is useful inventory, but npm update does not install a patched Node.js runtime. Apply this fix through the mechanism that supplies the Node binary.
2. Prepare backup, tests, and rollback before changing anything
Save the previous version, service configuration, dependency inventory, and immutable image identifier. Use your application's normal data-backup procedure as well; copying a Node binary does not protect a database. Run only the commands that match your deployment. Review and redact any secret, and save only the minimum inventory in access-restricted storage; never attach raw output to the change record.
node --version > node-version.before.txt
node -p process.execPath > node-path.before.txt
npm ls --depth=0 > npm-tree.before.txt 2>&1
pm2 jlist | jq '[.[] | {name, pid, status: .pm2_env.status, exec_interpreter: .pm2_env.exec_interpreter, pm_exec_path: .pm2_env.pm_exec_path, node_version: .pm2_env.node_version}]' > pm2-processes.before.json
systemctl show myapp.service -p User -p ExecStart -p FragmentPath -p EnvironmentFiles > systemd-metadata.before.txt
docker image inspect myapp:current --format '{{.Id}}' > docker-image-id.before.txt
docker image inspect myapp:current --format '{{json .RepoDigests}}' > docker-image-digests.before.jsonIf jq is unavailable, use pm2 list and record only those fields manually. Define success criteria—health checks, latency, errors, queues, and critical workflows—and an explicit threshold for returning to the previous image or runtime. A rollback is temporary if it restores a vulnerable release.
3. Update through the existing runtime manager
NVM
Choose one target: 22.23.2 for 22.x, 24.18.1 for 24.x, or 26.5.1 for 26.x. This example uses line 24; change TARGET if you run another line. Confirm that the service account uses the same NVM installation.
TARGET=24.18.1
nvm install $TARGET
nvm use $TARGET
nvm alias default $TARGET
node --version
node -p process.execPathA runtime change may require native add-ons to be rebuilt in the application's normal pipeline. Use the project's lockfile and documented build process; do not turn an indiscriminate npm update into part of the runtime patch.
Docker
Official images can lag the upstream binary by hours and appear gradually by architecture, as the docker-node release availability documentation explains. Inspect the exact tag and its platforms with docker buildx imagetools inspect first. If it returns manifest unknown or no matching manifest, stop this path: do not substitute 24.18.0 or another unpatched release for 24.18.1.
The Dockerfile must explicitly consume the base you verified. One safe pattern declares ARG BASE_IMAGE before FROM ${BASE_IMAGE} and passes a resolved digest; the digest can also be pinned directly in FROM. An application image tag does not prove which runtime it contains.
set -euo pipefail
TARGET_TAG='node:24.18.1-bookworm-slim'
docker buildx imagetools inspect "$TARGET_TAG"
docker pull "$TARGET_TAG"
BASE_IMAGE=$(docker image inspect "$TARGET_TAG" --format '{{index .RepoDigests 0}}')
test -n "$BASE_IMAGE"
docker buildx imagetools inspect "$BASE_IMAGE"
docker build --pull --build-arg BASE_IMAGE="$BASE_IMAGE" -t myapp:node-24.18.1 .
docker run --rm --entrypoint node myapp:node-24.18.1 --version
docker exec api node --version
docker exec api node -p process.execPathThe docker run --rm check validates the built image before deployment and removes only that ephemeral verification container. Run the two docker exec commands after a controlled deployment; they should return v24.18.1 and the binary path inside the container. This example is valid only when the Dockerfile actually uses ARG BASE_IMAGE in FROM; otherwise, fix the Dockerfile first.
Distribution or provider packages
Inspect the candidate version first. Repositories can lag the upstream release or use a provider-specific package version with backported fixes. Check the provider's documentation and stop if you cannot show that the candidate contains these fixes. Run only the section for your operating system.
# Debian or Ubuntu: inspect and update the candidate package
apt-cache policy nodejs
sudo apt-get update
apt-cache policy nodejs
sudo apt-get install --only-upgrade nodejs
dpkg-query -W -f='${Package} ${Version}\n' nodejs
# Fedora, RHEL, or derivatives: inspect and update
sudo dnf check-update nodejs
sudo dnf upgrade nodejs
rpm -q nodejs
# Alpine: inspect and update
apk policy nodejs
sudo apk update
sudo apk upgrade nodejs
apk info -v nodejs
node --versionFor a backport, retain the full package version and the provider advisory that confirms the fixed CVEs; node --version alone does not prove a backport. Do not add apt autoremove or image/package cleanup to this response. Retain previous artifacts until the observation window closes.
4. Restart the right process
A new binary on disk does not change a process already loaded in memory. Restart or reload in a controlled way, then confirm status. Replace the example names with your service names.
Update Node.js with control over your environment
Plan the runtime change in an environment you administer, with version checks and a defined rollback.


# PM2
pm2 describe api
pm2 reload ecosystem.config.js --only api --update-env
pm2 list
pm2 logs --lines 100 --nostream
# systemd
systemctl show myapp.service -p User -p ExecStart -p FragmentPath -p EnvironmentFiles
sudo systemctl restart myapp.service
sudo systemctl status myapp.service --no-pager
sudo journalctl -u myapp.service --since=-10min --no-pagerIf PM2 or systemd points to an old absolute path, correct the deployment before closing the change. A shell that prints the new version does not prove which binary the production process is using.
5. Validate version, behavior, and telemetry
Check the release from every execution context, exercise a functional endpoint, and observe errors, restarts, memory, CPU, latency, and connections. These commands are a documented checklist; adapt names and endpoints to your system.
node --version
curl -fsS http://127.0.0.1:3000/health
pm2 describe api
pm2 logs --lines 100 --nostream
sudo systemctl status myapp.service --no-pager
sudo journalctl -u myapp.service --since=-10min --no-pager
docker exec api node --version
docker exec api node -p 'JSON.stringify(process.versions)'- With NVM, upstream binaries, and official images, every worker, replica, and job should report the exact patched release for its line.
- With distribution packages, record the full version through
dpkg-query,rpm -q, orapk info -vand retain the provider advisory that documents the backport; do not declare success fromnode --versionalone. - Run smoke tests for authentication, APIs, scheduled jobs, bots, WebSockets, HTTP/2, TLS/mTLS calls, and SQLite where those features exist.
- Compare the post-change window with baseline 5xx rate, p95/p99 latency, memory, restarts, TLS/DNS errors, and queue depth.
- Observe long enough to cover at least one relevant traffic and scheduled-job cycle.
Troubleshooting before rollback
manifest unknownorno matching manifest: confirm the tag and platform through Buildx. If the patched image is not available for your architecture, stop the Docker path and wait for that image or use another verifiable runtime source; do not deploy the prior tag.- The shell changed, but PM2 or systemd did not: inspect the real PID executable through
/proc. Fix the interpreter path in version-controlled configuration and restart onlyapiormyapp.service. - The candidate package does not prove a backport: do not update blindly or declare success. Record the full package version and require a provider advisory that maps it to these CVEs.
- A native add-on fails: if you see an ABI or
NODE_MODULE_VERSIONerror, rebuild the add-on in CI or staging from the lockfile and redeploy. Do not usenpm updateas a production shortcut. - The health check fails: inspect logs and telemetry. If the pre-defined threshold is crossed or a critical workflow is unavailable, trigger the matching rollback.
TARGET_TAG='node:24.18.1-bookworm-slim'
docker buildx imagetools inspect "$TARGET_TAG"
mapfile -t PM2_PIDS < <(pm2 jlist | jq -er '.[] | select(.name == "api" and .pm2_env.status == "online") | .pid')
((${#PM2_PIDS[@]} > 0)) || { printf 'no online PM2 processes named api\n' >&2; exit 1; }
for PM2_PID in "${PM2_PIDS[@]}"; do
PM2_NODE=$(readlink -f "/proc/$PM2_PID/exe")
test -x "$PM2_NODE"
printf 'PID %s -> %s\n' "$PM2_PID" "$PM2_NODE"
"$PM2_NODE" --version
done
SYSTEMD_PID=$(systemctl show myapp.service -p MainPID --value)
readlink -f "/proc/$SYSTEMD_PID/exe"
dpkg-query -W -f='${Package} ${Version}\n' nodejs
rpm -q nodejs
apk info -v nodejsRun only the package query for your distribution. These checks diagnose state; they do not replace functional validation.
6. Roll back only against a defined criterion
Trigger a rollback only when the threshold defined before the change is met. Use the exact recorded values and restore the version-controlled configuration that matches the previous artifact. Returning to a vulnerable release reopens exposure, so the rollback must be temporary and documented.
NVM and PM2
Restore the version-controlled process file first if it contains an absolute interpreter path. Replace the example value with the exact recorded release and reload only the affected application.
set -euo pipefail
PREVIOUS_NODE='REPLACE_WITH_RECORDED_VERSION'
if [[ "$PREVIOUS_NODE" == *REPLACE* ]]; then
printf 'PREVIOUS_NODE must be an exact semver version\n' >&2
exit 1
fi
if [[ ! "$PREVIOUS_NODE" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
printf 'PREVIOUS_NODE must be an exact semver version\n' >&2
exit 1
fi
nvm use "$PREVIOUS_NODE"
pm2 reload ecosystem.config.js --only api --update-env
mapfile -t PM2_PIDS < <(pm2 jlist | jq -er '.[] | select(.name == "api" and .pm2_env.status == "online") | .pid')
((${#PM2_PIDS[@]} > 0)) || { printf 'no online PM2 processes named api\n' >&2; exit 1; }
for PM2_PID in "${PM2_PIDS[@]}"; do
PM2_NODE=$(readlink -f "/proc/$PM2_PID/exe")
test -x "$PM2_NODE"
PM2_VERSION=$("$PM2_NODE" --version)
test "$PM2_VERSION" = "v$PREVIOUS_NODE"
printf 'PID %s -> %s (%s)\n' "$PM2_PID" "$PM2_NODE" "$PM2_VERSION"
done
pm2 describe api
curl -fsS http://127.0.0.1:3000/healthDocker
Redeploy the immutable digest of the previous application image, not just the Node base image. This example applies only when Compose consumes APP_IMAGE; with another orchestrator, use its documented deployment mechanism with the same recorded digest. If your Compose version does not support --wait, use an explicit wait with a timeout; do not omit it.
set -euo pipefail
PREVIOUS_APP_IMAGE='REPLACE_WITH_RECORDED_IMAGE_AND_DIGEST'
if [[ "$PREVIOUS_APP_IMAGE" == *REPLACE* ]]; then
printf 'PREVIOUS_APP_IMAGE must be an image reference with a sha256 digest\n' >&2
exit 1
fi
if [[ ! "$PREVIOUS_APP_IMAGE" =~ ^[^[:space:]]+@sha256:[0-9a-fA-F]{64}$ ]]; then
printf 'PREVIOUS_APP_IMAGE must be an image reference with a sha256 digest\n' >&2
exit 1
fi
docker buildx imagetools inspect "$PREVIOUS_APP_IMAGE"
docker pull "$PREVIOUS_APP_IMAGE"
EXPECTED_IMAGE_ID=$(docker image inspect "$PREVIOUS_APP_IMAGE" --format '{{.Id}}')
APP_IMAGE="$PREVIOUS_APP_IMAGE" docker compose up --no-deps --wait --wait-timeout 120 api
mapfile -t RUNNING_CONTAINER_IDS < <(docker compose ps -q --all api)
((${#RUNNING_CONTAINER_IDS[@]} > 0)) || { printf 'no containers found for api\n' >&2; exit 1; }
for RUNNING_CONTAINER_ID in "${RUNNING_CONTAINER_IDS[@]}"; do
RUNNING_STATE=$(docker inspect "$RUNNING_CONTAINER_ID" --format '{{.State.Running}}')
test "$RUNNING_STATE" = 'true'
RUNNING_IMAGE_ID=$(docker inspect "$RUNNING_CONTAINER_ID" --format '{{.Image}}')
test "$RUNNING_IMAGE_ID" = "$EXPECTED_IMAGE_ID"
docker exec "$RUNNING_CONTAINER_ID" node --version
done
curl -fsS http://127.0.0.1:3000/healthsystemd and operating-system packages
Restore the previous unit and application configuration from its versioned source through the approved deployment workflow, then reload systemd and restart only the affected service. For an OS package, use only the provider's documented rollback and only when the exact previous artifact was retained; do not offer a generic downgrade.
set -euo pipefail
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
SYSTEMD_PID=$(systemctl show myapp.service -p MainPID --value)
if [[ ! "$SYSTEMD_PID" =~ ^[1-9][0-9]*$ ]]; then
printf 'myapp.service does not have a valid MainPID\n' >&2
exit 1
fi
SYSTEMD_NODE=$(readlink -f "/proc/$SYSTEMD_PID/exe")
test -x "$SYSTEMD_NODE"
printf 'PID %s -> %s\n' "$SYSTEMD_PID" "$SYSTEMD_NODE"
"$SYSTEMD_NODE" --version
sudo systemctl status myapp.service --no-pager
curl -fsS http://127.0.0.1:3000/healthAfter each reversal, check the real process version and executable, the health check, and the same telemetry used as the trigger. Document the reopened exposure, retain partial mitigations, and schedule the compatibility fix required to reapply the patch.
If your Node.js release is EOL
Node.js states that out-of-support lines should be treated as affected when a security release occurs. In July 2026, 22.x is Maintenance LTS, 24.x is Active LTS, and 26.x is Current; lines such as 20.x are already EOL under the current schedule. Do not wait for a public patch on an EOL branch.
- Inventory runtime, native add-on, and dependency compatibility.
- Choose a supported line; an LTS line usually limits unrelated change for production workloads.
- Test in staging with representative data and traffic.
- Roll out in stages, validate telemetry, and retain a time-bounded rollback.
For APIs, bots, and Next.js applications, keep runtime updates separate from framework remediation. See our guide to the July 2026 Next.js vulnerabilities: updating Node.js does not replace a Next.js patch, and updating Next.js does not patch the runtime.
Frequently asked questions
Which Node.js version should I install?
Install 22.23.2 if you must remain on 22.x, 24.18.1 if you are on 24.x, or 26.5.1 if you are on 26.x. Do not jump release lines during the urgent response unless your current line is EOL or the migration is already validated.
Does npm update fix these CVEs?
No. These CVEs are fixed in the Node.js runtime and in dependencies shipped with it, including llhttp and undici. npm manages application dependencies; auditing or updating them is a separate task.
Am I protected behind a reverse proxy?
Not necessarily. It depends on where HTTP/2 terminates, the protocol between the proxy and Node, and whether the application uses other affected surfaces: HTTPS Agent, Permission Model, DNS, zlib, SQLite, or proxy logic implemented in Node. Verify the architecture and update the runtime.
Is there active exploitation or an official CVSS score?
The official sources reviewed do not confirm active exploitation. The advisory labels severity High, Medium, or Low but does not include numeric CVSS scores. If Node.js later publishes CVSS data, confirmed exploitation, or an erratum, this same URL should be updated with that information.
Can I mitigate the issues without restarting?
Some controls reduce exposure, such as removing unused HTTP/2 or separating agents by identity. They do not repair the binary. Schedule the controlled restart required to load the patched runtime.
Does installing the patch guarantee my service works?
No. The patched version removes the documented flaws, but application compatibility still needs smoke tests and telemetry. The commands in this guide are documented instructions; they are not tests performed by Teramont against your service.
Conclusion
This is no longer a pre-release warning: the patches are available. Update to 22.23.2, 24.18.1, or 26.5.1 for your release line, verify the binary inside the real process or container, and preserve a traceable rollback.










