The thing about a vulnerability that has been sitting in your reverse proxy since 2008 is that you have spent eighteen years convincing yourself the code was fine. NGINX powers somewhere north of 33% of the public web, anchors most of the world’s CDNs, and fronts a non-trivial slice of every Kubernetes ingress deployment on earth. So when researchers at DepthFirst Disclosures dropped CVE-2026-42945 on May 13 — a heap buffer overflow in ngx_http_rewrite_module that has lived in the tree since 0.6.27 — and shipped a working unauthenticated RCE proof-of-concept on GitHub the same day, the eighteen-year part stopped being trivia and started being a problem.

The CVSS is 9.2. The advisory is from F5. The PoC is public. The bug is in the part of NGINX you almost certainly use.

What it is

The flaw is a mismatch between two passes inside the rewrite engine. When NGINX evaluates a rewrite directive that contains an unnamed PCRE capture ($1, $2, and so on) with a replacement string that includes a ?, it walks the replacement once to calculate the size of the output buffer, then walks it again to actually copy bytes in. The two passes disagree about how to handle certain characters under the question-mark escaping logic. The first pass allocates a buffer that is too small. The second pass writes past it.

Trigger conditions, from the F5 advisory:

  • A rewrite directive uses an unnamed PCRE capture ($1, $2, …).
  • The replacement string contains a ?.
  • That rewrite is followed in the same scope by another rewrite, an if, or a set directive.

None of those conditions are exotic. The ? in a rewrite target is how you strip query strings or rewrite to a clean URL with appended parameters. Unnamed captures are the default form most engineers reach for. Chained rewrite/if/set blocks are the normal way to compose URL logic. If your nginx.conf has any non-trivial URL handling, you are very likely in scope.

The corruption happens in an NGINX worker process. With ASLR disabled, the published PoC demonstrates full code execution as the worker user. With ASLR enabled, the most reliable observed outcome is a worker crash — and repeated requests produce a crash loop, which is itself a denial-of-service primitive against your entire frontend.

Affected versions

CVE-2026-42945 affects NGINX Open Source 0.6.27 through 1.30.0, and NGINX Plus R32 through R36. The fix landed in:

  • NGINX Open Source: 1.30.1 and 1.31.0
  • NGINX Plus: R32 P6 and R36 P4

If you pin to a distro package, check what your distribution shipped. RHEL, Debian stable, and Ubuntu LTS all carry NGINX versions inside the vulnerable range and are pushing backported patches now. Alpine and the official nginx:stable and nginx:mainline Docker images updated within hours of disclosure; rebuild your images.

Why this is bad even if you “don’t use rewrite”

Two reasons.

First, a lot of NGINX configs use rewrite without the operator thinking of it as rewrite. Ingress controllers, WAF rules, proxy_pass shims, redirect-to-HTTPS blocks, and legacy URL canonicalization all emit rewrite directives. The nginx-ingress Helm chart and Kubernetes Ingress annotations like nginx.ingress.kubernetes.io/rewrite-target produce exactly the directive shape that triggers the bug. If you run Kubernetes with the community ingress controller and any path-rewriting annotation, assume you are exposed until you have patched.

Second, the bug is reachable pre-authentication on the request path. The attacker does not need a valid session, an API key, or a logged-in user. They need a URL that hits a server block with a vulnerable rewrite rule. That is your entire public surface.

What to do right now

Patch. NGINX Open Source 1.30.1 / 1.31.0, NGINX Plus R32 P6 / R36 P4. If you ship containers, rebuild and roll. If you run Kubernetes, update your ingress controller image — ingress-nginx cut a release pulling the patched NGINX the day after disclosure.

If you cannot patch immediately, the F5 advisory provides a workaround: replace unnamed captures with named captures in every affected rewrite. rewrite ^/foo/(.*)$ /bar/$1?baz becomes rewrite ^/foo/(?<tail>.*)$ /bar/$tail?baz. Named captures take a different code path and are not affected. This is a config-only change; reload NGINX with nginx -s reload and you are protected without a binary upgrade. It is more annoying than it sounds in any real config — you have to find every offending rewrite — but it works.

Detection: the PoC on GitHub produces a recognizable URL pattern. A WAF rule or an access_log query for unusually long URIs, sequences of ? characters in the path, or worker process crashes correlated to a particular request is a reasonable first-pass detection. NGINX worker crashes are normally rare; a sudden uptick in worker process exited on signal 11 in your error log is the loudest signal you will get.

What this changes

NGINX is the deepest piece of plumbing most infrastructure organizations operate. The bug has been reachable since 2008. Whatever your assumption was about “old, battle-tested code is safe code,” recalibrate. A pre-auth RCE in a string-handling pass nobody has audited for a decade is exactly the class of finding that will keep happening as more researchers run modern fuzzers against software written in the FreeBSD-mailing-list era.

The patch is one apt upgrade away. The lesson is more expensive.

References

  • F5 advisory K000156729: CVE-2026-42945 (ngx_http_rewrite_module heap buffer overflow)
  • The Hacker News, 18-Year-Old NGINX Rewrite Module Flaw Enables Unauthenticated RCE
  • BleepingComputer, 18-year-old NGINX vulnerability allows DoS, potential RCE
  • DepthFirst Disclosures GitHub PoC: depthfirstdisclosures/nginx-rift
  • SecurityAffairs, NGINX Rift: an 18-year-old flaw in the world’s most deployed web server just came to light