
General
Next.js Fixes Two Critical RCEs: Upgrade to 16.3.3 or 15.5.24
Next.js 16.3.3 and 15.5.24 fix two Critical RCEs involving Windows servers and AVIF optimization. Check exposure and upgrade production safely.
Read More7 min read

7/25/2026 ·Mizael Segovia· 17 min read ·
301 views
Our team is ready to help with any questions or issues you may have.
Contact UsDirect 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.
--permission when an attacker can influence paths or inputs, HTTPS agents that reuse mTLS or distinct identity policies, and Node-based forwarding proxies.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.
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.
A raw CVE count cannot set the same priority for every service. Start with the attack surface your deployment actually presents:
node:sqlite with SQLTagStore: 58041 applies only to 24.x and 26.x. Include write behavior and idempotency in validation.dns.resolveAny() handles a response containing more than 256 A records. Prioritize services that resolve names supplied by users or third parties.TypedArray length passed to the API. The advisory does not say that ordinary attacker-supplied compressed bytes alone trigger the flaw.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.
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.
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.
If 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.
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.
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.
The 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.
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.
For 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.
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.
Plan the runtime change in an environment you administer, with version checks and a defined rollback.


If 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.
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)'dpkg-query, rpm -q, or apk info -v and retain the provider advisory that documents the backport; do not declare success from node --version alone.manifest unknown or no 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./proc. Fix the interpreter path in version-controlled configuration and restart only api or myapp.service.NODE_MODULE_VERSION error, rebuild the add-on in CI or staging from the lockfile and redeploy. Do not use npm update as a production shortcut.Run only the package query for your distribution. These checks diagnose state; they do not replace functional validation.
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.
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.
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.
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.
After 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Find our next articles first
Mark Teramont as a preferred source to see more of our guides and news in Google, Top Stories, and its AI experiences.

Keep exploring related guides, news, and analysis.

General
Next.js 16.3.3 and 15.5.24 fix two Critical RCEs involving Windows servers and AVIF optimization. Check exposure and upgrade production safely.
Read More7 min read
General
CVE-2026-65643 affects supported cPanel & WHM releases. Understand the real scope, patched builds, update process, and compromise checks.
Read More4 min read
General
Next.js 16.2.11 and 15.5.21 fix nine SSRF, bypass, DoS, and cache flaws. Review affected versions, mitigations, and safe upgrade steps.
Read More6 min readnode --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.jsonset -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.execPath# 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 --version# 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-pagerTARGET_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 nodejsset -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/healthset -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/healthset -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/health