On October 21, the Node.js team shipped security releases for Node.js 22, 24, and 26. The one that matters most for Strapi is CVE-2026-111497, a bug in the Brotli decompression code that's bundled with Node.js. A specially crafted Brotli-compressed request can corrupt memory, and Node.js says it can lead to remote code execution.
Strapi is affected because its body parser decompresses Brotli request bodies before any authentication happens, so anyone who can reach your app can send one. The fix is in Node.js itself, so you need to upgrade to a patched version. The same release also fixes two TLS hostname-verification bypasses (CVE-2026-106069 and CVE-2026-105483) and a few other issues, which are all listed in the Node.js advisory.
Summary: what to do
Any Strapi app, v4 or v5, on Strapi Cloud or self-hosted, is affected if it runs on a Node.js version without the fix.
| Node.js | Fixed in |
|---|---|
| 22 | 22.23.4 or later |
| 24 | 24.22.1 or later |
| 26 | 26.12.1 or later |
| 20 and older | No fix. These versions are end of life. |
If you're on Strapi Cloud, projects without a custom domain should already be covered by a temporary filter we added at our edge, and we're redeploying the projects that do use a custom domain for you. We still recommend redeploying yourself so you're running the patched Node.js. If your project is on Node.js 20 or older, you'll have to change that yourself, and if it also has a custom domain, there's a small middleware you can add until you do. The details are in the Strapi Cloud section.
If you host Strapi yourself, upgrade Node.js to a fixed version and restart Strapi. You don't need to upgrade Strapi for this.
Node.js 20 and older won't get a fix, so you'll need to move to Node.js 22 or 24. Strapi v4 versions older than 4.26.2 can't run on Node.js 22 at all, which means they can't be patched. See Node.js 20 and older, and Strapi v4 for what to do.
If you want to check a specific app, there's a test you can run at the end of this post.
If you use Strapi Cloud
On October 22 at 14:29 UTC, we turned on a temporary filter at our edge, which we're calling the
edge patch. It rejects Brotli-compressed requests before they reach your project, but only on your
project's *.strapiapp.com address. If you've set up a custom domain, such as cms.example.com,
traffic to that domain doesn't go through the filter.
Every deployment since then runs the latest patch release of the Node.js version you selected, which is 22.23.4 for Node.js 22 and 24.22.1 for Node.js 24. Because the edge patch doesn't cover custom domains, we're redeploying the projects that use one for you.
The table below shows where your project stands and what, if anything, you need to do.
| Your project | Protected right now? | What we're doing | What you need to do |
|---|---|---|---|
| Node.js 22 or 24, no custom domain | Should be, by the edge patch | Nothing else | Redeploy when you can so you're on the patched Node.js. We'd also recommend upgrading to the latest Strapi release while you're at it. This issue doesn't need it, but it's good practice. |
| Node.js 22 or 24, with a custom domain | Not until it's redeployed | Redeploying it for you | Nothing, unless you'd like to redeploy it yourself sooner. |
| Node.js 20 or older, no custom domain | Should be, by the edge patch | We can't change the Node.js version for you | Switch to Node.js 22 or 24 and redeploy. |
| Node.js 20 or older, with a custom domain | No. The edge patch doesn't cover it, and redeploying on Node.js 20 doesn't fix it. | We can't protect it from our side | Switch to Node.js 22 or 24 and redeploy as soon as you can. If you can't switch yet, add the Strapi middleware from the end of this post and redeploy. It blocks these requests even on Node.js 20. |
| Strapi v4 older than 4.26.2 | Only if it has no custom domain | We can't protect it on a custom domain | Upgrade to Strapi 4.26.2 and switch to Node.js 22, or migrate to Strapi 5. If it has a custom domain, add the Strapi middleware and redeploy in the meantime. |
To change the Node.js version, open your project's settings in the Cloud dashboard, go to the
Configuration page for your environment, change Node version and redeploy. Your project has
to support the new version first. The engines.node range in your package.json needs to include
it, and so does your Strapi version: Node.js 22 needs Strapi 5.0.4 or later (or 4.26.2 on v4), and
Node.js 24 needs Strapi 5.31.0 or later. If your code doesn't allow it yet, push that change before
you switch.
To see which Node.js version your project is actually running, open Settings > Overview in your
Strapi admin panel once the deployment has finished, or look for the latest startup message in the
Logs tab of the Cloud dashboard. It has a line like Version | 5.58.0 (node v24.22.1). The
Cloud dashboard settings only show the major version, so don't go by those. If you see anything
older than 22.23.4 or 24.22.1, redeploy.
If you host Strapi yourself
- Check which Node.js version you're running with
node -v, or under Settings > Overview in the Strapi admin panel. - Upgrade to 22.23.4, 24.22.1, or 26.12.1, or any later release of the same major version.
- Restart Strapi.
If you run Strapi in Docker, pull the latest base image and rebuild. Pinned tags like
node:22.16.0-alpine won't pick up the fix, and neither will tags tied to an older Alpine release.
The patched builds are only published for Alpine 3.23 and newer, so node:22-alpine3.22, for
example, is still unpatched.
If your Node.js comes from your Linux distribution's packages rather than from nodejs.org or the official Docker images, keep an eye on your distribution's security updates as well. Some distributions build Node.js against the system's Brotli library, which needs its own update.
If you can't upgrade right away, the end of this post has rules for nginx, Caddy, HAProxy and Traefik, plus a small Strapi middleware that blocks these requests in the meantime.
Node.js 20 and older, and Strapi v4
Node.js 20 reached end of life in April 2026 and Node.js 18 a year before that, so neither will get a fix. You'll need to move to a newer version, and which one depends on your Strapi version.
| Strapi version | Node.js 22 | Node.js 24 | Node.js 26 |
|---|---|---|---|
| Strapi 5.48.1 or later | ✅ | ✅ | ✅ |
| Strapi 5.31.0 to 5.48.0 | ✅ | ✅ | ❌ |
| Strapi 5.0.4 to 5.30.x | ✅ | ❌ | ❌ |
| Strapi 5.0.0 to 5.0.3 | ❌ | ❌ | ❌ |
| Strapi v4 4.26.2 | ✅ | ❌ | ❌ |
| Strapi v4 older than 4.26.2 | ❌ | ❌ | ❌ |
Strapi Cloud currently offers Node.js 22 and 24.
Strapi v4 is also end of life, and its security updates stopped in April 2026. It parses request bodies the same way Strapi 5 does, so a v4 app on an unpatched Node.js is exposed too. Strapi 4.26.2, the final v4 release, added support for Node.js 22, and running it on a patched Node.js 22 protects you from this issue. Since v4 won't get any more security fixes, this is a good time to plan your move to Strapi 5.
How the issue reaches Strapi
When Strapi receives a request with a Content-Encoding: br header, its body parser decompresses
the body with Node.js's built-in Brotli code before doing anything else with it. That happens before
routing and before authentication, so a POST to any URL on your app is enough, even one that
doesn't exist, and no account is needed. Disabling registration or other routes won't help. The
GraphQL endpoint handles request bodies the same way.
The bug itself is in Node.js. Strapi uses a common open-source body-parsing library that supports Brotli, like many other Node.js frameworks do, which is why upgrading Node.js fixes this for every version of Strapi.
If your app's dependencies were installed before October 2023, you may still have an older version of that library without Brotli support, in which case your app rejects these requests. The next reinstall or dependency update will pull in the newer version, though, so it isn't something to count on.
Response compression isn't affected. If you've enabled the strapi::compression middleware,
Strapi uses Brotli to compress what it sends, and compressing doesn't touch the vulnerable code. The
admin panel isn't affected either, because it's your browser that decompresses what it receives,
not your Strapi server.
Strapi also makes outgoing requests, for example when it delivers webhooks, signs users in through providers like GitHub or Google, or talks to Strapi's own services, and Node.js decompresses Brotli responses to those automatically. To use that, an attacker would need to control one of those servers, which usually means an administrator has pointed Strapi at a malicious URL. Upgrading Node.js fixes this as well.
Timeline (UTC)
- October 21: Node.js publishes the security release.
- October 22, 14:29: the edge patch goes live on Strapi Cloud for
*.strapiapp.comaddresses. - October 22, afternoon: new Strapi Cloud deployments start using the patched Node.js releases, and we start redeploying projects that use a custom domain.
- October 23: we publish this post.
Test whether your app is affected
Use this if your Node.js version isn't one of the fixed ones, or you're not sure. If you're already on a fixed version, you're not affected.
The test sends the same login attempt, with made-up credentials, to your app twice: once as normal JSON and once compressed with Brotli. If Strapi answers both the same way, it decompresses the Brotli one. It uses the login route from the Users & Permissions plugin, which is installed by default. Run it against the address people actually use to reach your app, which on Strapi Cloud means your custom domain if you have one.
URL=https://your-strapi-domain/api/auth/local
BODY='{"identifier":"nobody@example.com","password":"WrongPass1"}'
node -e "process.stdout.write(require('zlib').brotliCompressSync(process.argv[1]))" "$BODY" > body.br
curl -s -w ' (HTTP %{http_code})\n' -X POST "$URL" -H 'Content-Type: application/json' -d "$BODY"
curl -s -w ' (HTTP %{http_code})\n' -X POST "$URL" -H 'Content-Type: application/json' \
-H 'Content-Encoding: br' --data-binary @body.brCompare the actual text of the two responses. A blocked request can also come back as a 400, so the status code on its own can look like a normal login error.
| What you see | What it means |
|---|---|
| Both responses are the same, usually a 400 or 401 login error | Strapi decompressed the Brotli request. If you're on an unpatched Node.js, you're affected and should upgrade now. On Strapi Cloud, see the Strapi Cloud section. If you host Strapi yourself, you can block these requests until you're able to upgrade. |
The second response is different and rejects the request, for example Encoding not supported from Strapi Cloud's edge patch, a block page from your proxy or CDN, or Unsupported Content-Encoding (415) | The request was stopped before Strapi could decompress it, so you're covered against incoming attacks for now. You should still upgrade Node.js, since a block doesn't cover Strapi's outgoing requests and the edge patch is temporary. If the 415 is a JSON error from Strapi that says Unsupported Content-Encoding: br and you didn't add a block yourself, your app has the older body-parsing library, and your next dependency update will make it vulnerable. |
| Either response is a 404, 405, or 429 | The test didn't work, probably because the login route is turned off or you hit the rate limit. Assume you're affected and upgrade. |
Blocking Brotli-compressed requests until you can upgrade
If you can't upgrade Node.js right away, you can reject requests with Content-Encoding: br before
they ever reach Strapi. If you host Strapi yourself, you can do that in your reverse proxy or with a
small Strapi middleware. On Strapi Cloud, the middleware is the only option, and it's worth adding if
your project has a custom domain and can't move off Node.js 20 yet. Browsers and the usual HTTP
clients don't compress request bodies, so for most apps this won't block anything legitimate. If
you have your own clients that compress uploads, check those first.
Each of the rules below rejects any request whose Content-Encoding header contains br, in any
letter case and whether it's on its own or in a list like gzip, br, and lets everything else
through. Add it everywhere your proxy serves Strapi, HTTPS included. We tested them on nginx 1.31,
Caddy 2.11, HAProxy 3.4, and Traefik 3.7. They only block incoming requests, so you still need to
upgrade Node.js.
nginx
Add this inside each server block that serves Strapi:
if ($http_content_encoding ~* "\bbr\b") {
return 415;
}Caddy
Add this to your site block, before reverse_proxy:
@brotli_body header_regexp Content-Encoding (?i)\bbr\b
respond @brotli_body 415HAProxy
Add this to each frontend that serves Strapi:
http-request return status 415 if { req.hdr(content-encoding) -i -m str br }http-request deny deny_status 415 looks like it should work, but HAProxy refuses to start with
it, because deny_status only accepts codes HAProxy has a built-in error page for, and 415 isn't one
of them.
Traefik v3
This adds a router to your dynamic configuration that matches the header and sends those requests
through a middleware that allows nobody, so they get a 403. Swap in your own host for
cms.example.com and your own service for strapi, and copy entryPoints and tls from your
existing Strapi router. Without those two, the rule won't apply to HTTPS traffic.
http:
routers:
strapi-block-brotli:
rule: 'Host(`cms.example.com`) && HeaderRegexp(`Content-Encoding`, `(?i)\bbr\b`)'
entryPoints:
- websecure
tls: {}
priority: 1000
service: strapi
middlewares:
- deny-all
middlewares:
deny-all:
ipAllowList:
sourceRange:
- 255.255.255.255/32Keep the single quotes around rule. In a double-quoted YAML string, \b turns into a backspace
character and the rule quietly stops matching. If your Strapi service comes from Docker labels,
refer to it as strapi@docker.
Strapi middleware
This works on Strapi v4 and Strapi 5, whether you host Strapi yourself or use Strapi Cloud. We tested it on Strapi 4.25.23 running Node.js 20 and on Strapi 5.59.0. On Strapi Cloud, commit it to your project and redeploy.
Create src/middlewares/reject-brotli-bodies.js:
module.exports = () => {
return async (ctx, next) => {
if (/\bbr\b/i.test(ctx.get('Content-Encoding'))) {
ctx.status = 415;
ctx.body = 'Brotli-encoded request bodies are not accepted';
return;
}
await next();
};
};If your project uses Strapi 5 with TypeScript, save this as
src/middlewares/reject-brotli-bodies.ts instead:
import type { Core } from '@strapi/strapi';
const rejectBrotliBodies: Core.MiddlewareFactory = () => {
return async (ctx, next) => {
if (/\bbr\b/i.test(ctx.get('Content-Encoding'))) {
ctx.status = 415;
ctx.body = 'Brotli-encoded request bodies are not accepted';
return;
}
await next();
};
};
export default rejectBrotliBodies;Then register it in config/middlewares.js (or .ts), on the line just above 'strapi::body':
'global::reject-brotli-bodies',
'strapi::body',Questions
If you have questions, Strapi customers can email us at support@strapi.io. If you've found a security issue in Strapi itself, please report it through a GitHub security advisory.





