✨ Strapi MCP is now Generally Available - let your agents manage your Strapi content ✨

Security & Privacy●13 min read

Node.js Brotli vulnerability (CVE-2026-111497): what it means for Strapi

●October 23, 2026
Node.js Brotli vulnerability (CVE-2026-111497): what it means for Strapi

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.jsFixed in
2222.23.4 or later
2424.22.1 or later
2626.12.1 or later
20 and olderNo 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 projectProtected right now?What we're doingWhat you need to do
Node.js 22 or 24, no custom domainShould be, by the edge patchNothing elseRedeploy 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 domainNot until it's redeployedRedeploying it for youNothing, unless you'd like to redeploy it yourself sooner.
Node.js 20 or older, no custom domainShould be, by the edge patchWe can't change the Node.js version for youSwitch to Node.js 22 or 24 and redeploy.
Node.js 20 or older, with a custom domainNo. The edge patch doesn't cover it, and redeploying on Node.js 20 doesn't fix it.We can't protect it from our sideSwitch 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.2Only if it has no custom domainWe can't protect it on a custom domainUpgrade 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

  1. Check which Node.js version you're running with node -v, or under Settings > Overview in the Strapi admin panel.
  2. Upgrade to 22.23.4, 24.22.1, or 26.12.1, or any later release of the same major version.
  3. 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 versionNode.js 22Node.js 24Node.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.com addresses.
  • 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.br

Compare 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 seeWhat it means
Both responses are the same, usually a 400 or 401 login errorStrapi 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 429The 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 415

HAProxy

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/32

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

Derrick Mehaffy Support Engineer Team Lead
Security Disclosure of Vulnerabilities for June
Security & Privacy·8 min read

Security Disclosure of Vulnerabilities: CVE-2024-31217, CVE-2024-29181, and CVE-2024-34065 for June 2024

Security Disclosure of Vulnerabilities: CVE-2024-31217, CVE-2024-29181, and CVE-2024-34065 for June 2024

·June 12, 2024
Security & Privacy·10 min read

Security Disclosure of Vulnerabilities: CVE-2023-36472, CVE-2023-38507, CVE-2023-37263, and CVE-2023-39345

Security Disclosure of Vulnerabilities: CVE-2023-36472, CVE-2023-36472, CVE-2023-37263, and CVE-2023-39345

·September 13, 2023
API security
Security & PrivacyBeginner·11 min read

Best Practices of API Security in Strapi

Learn about the best practices for API security in Strapi to protect your data from potential threats and vulnerabilities.

·August 9, 2023