Like a bouncer who only checks IDs at the front door while the valet entrance uses a different address format entirely
CVE-2026-85184 affects @fastify/middie versions 9.1.0 through 9.3.3 and is fixed in 9.3.4. The vulnerability exploits an interpretation conflict (CWE-436) between middie's middleware path-matching logic and Fastify's core router. In HTTP/1.1, a client can send a request using *absolute-form* — e.g., GET http://target.com/admin/secret HTTP/1.1 instead of the typical origin-form GET /admin/secret HTTP/1.1. Node.js's http module faithfully preserves the absolute-form URI in req.url. Middie's path-scoped middleware checks if req.url starts with a registered prefix like /admin, but http://target.com/admin/secret doesn't match that check — so authentication, authorization, rate-limiting, and audit middleware are silently skipped. Fastify's router, however, normalizes the request target and routes it to the correct handler, granting the attacker unauthenticated access to protected endpoints.
The vendor scored this CRITICAL at 9.1 (AV:N/AC:L/PR:N/UI:N), and on paper the vector is accurate — it's unauthenticated, network-reachable, and low-complexity. But the vendor score ignores a decisive real-world friction point: virtually all production Fastify deployments sit behind a reverse proxy (Nginx, HAProxy, AWS ALB, Cloudflare, Traefik, Envoy) that normalizes absolute-form request targets to origin-form before forwarding traffic to the Node.js backend. This means the malicious request never reaches middie in its exploitable shape for the majority of deployments. The CRITICAL rating overstates practical risk for enterprises running standard ingress architectures, though the minority of deployments with direct Node.js exposure face genuine, trivially exploitable auth bypass.
4 steps from start to impact.
Identify direct Node.js exposure
X-Powered-By: Fastify headers, or responses that reflect absolute-form URIs unchanged. If a reverse proxy is present and normalizes the request target, this chain is dead.- Target runs @fastify/middie ≥9.1.0 and <9.3.4
- Target is reachable over HTTP/HTTPS
- ~80% of production Node.js deployments sit behind Nginx, ALB, Cloudflare, or similar proxies that rewrite absolute-form to origin-form
- Container orchestration platforms (ECS, K8s) typically inject an ingress controller or service mesh sidecar
Craft absolute-form request
GET http://target.com/admin/secret HTTP/1.1. This is a valid request format per RFC 7230 §5.3.2. No special tooling is required — curl --request-target or raw netcat suffice. The request is syntactically legitimate and will not be rejected by compliant HTTP stacks.- Direct TCP/HTTP access to the Node.js process (no normalizing proxy)
- Standard HTTP client libraries (browsers, axios, fetch) do not generate absolute-form requests by default — attacker must use low-level tooling
GET http:// in request lineMiddie path matching fails silently
req.url is set to http://target.com/admin/secret. Middie's Holder.run function in lib/engine.js evaluates registered middleware paths via prefix or regex matching against req.url. Since the URL starts with http:// rather than /admin, no path-scoped middleware fires. Authentication, authorization, CSRF, rate-limiting, and audit middleware are all silently skipped.- Application uses path-scoped middleware via middie for security controls (e.g.,
app.use('/admin', authMiddleware))
- Idiomatic Fastify uses
preHandlerhooks rather than Express-style middleware — apps usingpreHandlerfor auth are NOT affected - Only apps porting Express middleware patterns via @fastify/middie are vulnerable
Fastify router normalizes and routes
find-my-way router extracts the path component from the absolute-form URI and matches it against registered route handlers. The request is dispatched to the handler for /admin/secret as if the middleware had run. The attacker receives the full response — admin data, user records, configuration, or whatever the protected endpoint serves — without any authentication or authorization checks.- A route handler exists for the targeted path
- Blast radius is limited to what the specific Fastify application exposes — no lateral movement, no domain compromise
The supporting signals.
| In-the-Wild Exploitation | No known exploitation campaigns. Not listed in CISA KEV. The absolute-form bypass class is understood by researchers but has not been widely weaponized against Node.js targets. |
|---|---|
| Proof of Concept | Trivial to reproduce with curl --request-target 'http://target.com/admin' http://target.com/admin or raw TCP. The sibling CVEs (CVE-2026-22031, CVE-2026-2880, CVE-2026-14198) in the same codebase have published PoCs and detailed writeups, lowering the barrier for this variant. |
| EPSS Score | Not yet scored (CVE is very recently published). Sibling CVE-2026-22031 carries EPSS 37.31% — expect a similar or slightly lower score once calculated given the reverse-proxy friction. |
| KEV Status | Not listed. No evidence of active exploitation by threat actors. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N — 9.1 CRITICAL. The vector is technically accurate for direct-exposure scenarios but does not account for the reverse-proxy normalization that shields most deployments. |
| Affected Versions | @fastify/middie ≥9.1.0 and <9.3.4 (npm). All prior bypass fixes (9.1.0, 9.2.0, 9.3.2, 9.3.3) did not address the absolute-form variant. |
| Fixed Version | @fastify/middie 9.3.4 — released September 4, 2026. No backports to older major lines. Upgrade path is npm install @fastify/middie@9.3.4. |
| Scanning / Exposure | @fastify/middie has ~470K–550K weekly npm downloads. Shodan/Censys fingerprinting for directly-exposed Fastify instances is limited — most are behind reverse proxies. Actual exposure population for this specific bypass is estimated at <5% of installs. |
| Disclosure Date | September 4, 2026 (coordinated disclosure via GitHub Security Advisory GHSA-hx87-8wv7-pjv8). |
| Reporting Researcher | Not publicly attributed in advisory. This is the sixth path-matching bypass in @fastify/middie in 2026, following CVE-2026-22031, CVE-2026-2880, CVE-2026-6270, CVE-2026-33804, and CVE-2026-14198 — indicating a systemic canonicalization weakness in the middleware engine. |
noisgate verdict.
Downgraded from CRITICAL to HIGH because reverse-proxy normalization of absolute-form request targets shields the majority (~80%+) of production Fastify deployments, making the exploitable population a minority of the installed base. The bypass remains unauthenticated and trivially exploitable against the exposed minority, preventing further downgrade.
Why this verdict
- Reverse-proxy friction is decisive. Nginx, HAProxy, AWS ALB, Cloudflare, Traefik, and Envoy all normalize absolute-form request targets to origin-form before forwarding — this breaks the attack chain for the majority of production deployments without any deliberate mitigation.
- No authentication required, trivial complexity. For the minority of directly-exposed deployments, exploitation requires nothing more than
curl --request-target— no credentials, no user interaction, no race conditions. This prevents downgrade below HIGH. - Idiomatic Fastify reduces blast radius further. The Fastify ecosystem encourages
preHandlerhooks over Express-style middleware. Only applications that specifically useapp.use('/path', middleware)via @fastify/middie for security controls are affected — apps using Fastify-native hooks are immune. - Role multiplier: @fastify/middie is a web application framework plugin, not canonically a high-value infrastructure component (not an IdP, hypervisor, DC, CA, backup server, or network edge). The blast radius is application-scoped — confidentiality and integrity of whatever the specific Fastify app manages. In the rare case where a Fastify app serves as an API gateway or auth service, the impact escalates to tenant-scale, but this represents a small fraction of the installed base. No floor elevation triggered.
- Sixth bypass in the same codebase in 2026. The pattern of repeated canonicalization failures in @fastify/middie (URL encoding, double slashes, child scope prefixing, %2F in params, deprecated options, now absolute-form) suggests further variants are likely. This systemic risk argues against downgrading to MEDIUM.
Why not higher?
The vendor's CRITICAL rating assumes all deployments are directly reachable, which ignores the dominant deployment pattern. Reverse proxies normalize absolute-form URIs before they reach Node.js, neutering the attack for the majority of installs. There is no KEV listing, no active exploitation, and the blast radius is application-scoped rather than fleet-scale or identity-scale.
Why not lower?
The bypass is unauthenticated, zero-click, and trivially exploitable against directly-exposed Node.js instances — a population that, while a minority, is non-negligible (dev/staging environments, microservice meshes without ingress normalization, serverless platforms with direct routing). The repeated pattern of bypasses in this codebase also signals that compensating controls may be needed even if patched, preventing a MEDIUM verdict.
What to do — in priority order.
- Confirm a normalizing reverse proxy sits in front of every Fastify instance — Nginx, HAProxy, AWS ALB, Cloudflare, Traefik, and Envoy all rewrite absolute-form request targets to origin-form before forwarding. Verify this is in place for all environments (including staging and dev) — this single control neutralizes the attack. Deploy within 30 days per the noisgate mitigation SLA for HIGH.
- Migrate security middleware from middie to Fastify-native preHandler hooks — Fastify's
preHandlerhooks execute after routing, so they are immune to request-target normalization mismatches. Replacingapp.use('/path', authMiddleware)withfastify.addHook('preHandler', authFn)eliminates the entire class of middie path-matching bypasses — including future variants. This is the most durable fix. - Add WAF/IDS rule to block absolute-form request lines — Create a rule matching
^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS) https?://in the HTTP request line. This is a low-false-positive signature since browsers and standard clients never generate absolute-form requests to origin servers (only to forward proxies). - Upgrade to @fastify/middie ≥9.3.4 — The definitive fix. Run
npm install @fastify/[email protected]or update your lockfile. Test middleware behavior with absolute-form requests in CI before deploying.
- Rate limiting at the application layer via middie — if your rate limiter is registered as path-scoped middie middleware, it is also bypassed by this same attack vector. It cannot protect against a bypass of itself.
- Input validation on request body/parameters — the bypass occurs at the request-target level before any body parsing. Validating POST bodies or query parameters does not help.
- Helmet or CORS middleware via middie — these are also middie-registered middleware and are subject to the same bypass. They provide no compensating value here.
Crowdsourced verification payload.
Run this on any machine with npm and network access to your application's package source (e.g., a CI runner or the app host itself). Invoke with: bash check_middie.sh /path/to/your/app. No special privileges required — it reads node_modules only.
#!/usr/bin/env bash
# check_middie.sh — Detect CVE-2026-85184 (@fastify/middie absolute-form bypass)
# Usage: bash check_middie.sh /path/to/app
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
APP_DIR="${1:-.}"
PKG_JSON="$APP_DIR/node_modules/@fastify/middie/package.json"
if [ ! -f "$PKG_JSON" ]; then
echo "UNKNOWN — @fastify/middie not found in $APP_DIR/node_modules"
exit 2
fi
VERSION=$(node -e "console.log(require('$PKG_JSON').version)" 2>/dev/null)
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not parse version from $PKG_JSON"
exit 2
fi
echo "Detected @fastify/middie version: $VERSION"
# Compare version using node semver logic
RESULT=$(node -e "
const v = '$VERSION';
const parts = v.split('.').map(Number);
const [major, minor, patch] = parts;
// Affected: >=9.1.0 and <9.3.4
if (major < 9 || (major === 9 && minor < 1)) {
console.log('NOT_AFFECTED'); // below affected range
} else if (major === 9 && (minor < 3 || (minor === 3 && patch < 4))) {
console.log('VULNERABLE');
} else if (major >= 9) {
console.log('PATCHED');
} else {
console.log('UNKNOWN');
}
" 2>/dev/null)
case "$RESULT" in
VULNERABLE)
echo "VULNERABLE — @fastify/middie $VERSION is affected by CVE-2026-85184"
echo " Upgrade to >=9.3.4: npm install @fastify/[email protected]"
exit 1
;;
PATCHED)
echo "PATCHED — @fastify/middie $VERSION is not affected"
exit 0
;;
NOT_AFFECTED)
echo "PATCHED — @fastify/middie $VERSION predates the affected range (9.1.0–9.3.3)"
exit 0
;;
*)
echo "UNKNOWN — could not determine status for version $VERSION"
exit 2
;;
esacIf you remember one thing.
npm install @fastify/[email protected]). Under the noisgate mitigation SLA for HIGH, deploy a compensating control — either confirm a normalizing reverse proxy is in front of every Fastify instance, or add a WAF rule blocking absolute-form request lines — within 30 days. Under the noisgate remediation SLA, complete the actual patch rollout across all environments (including staging and dev, which are most likely to be directly exposed) within 180 days. If you have *any* Fastify instances directly exposed to the internet without a reverse proxy, treat those as your emergency — patch or add a proxy within days, not weeks. Given that this is the sixth middie path-bypass CVE in 2026, strongly consider migrating security-critical middleware from middie's app.use() pattern to Fastify-native preHandler hooks as a permanent architectural fix.Sources
- GitHub Advisory — GHSA-hx87-8wv7-pjv8 (CVE-2026-85184)
- @fastify/middie npm package
- GHSA-72c6-fx6q-fr5w — CVE-2026-6270 child scope bypass
- GHSA-2v46-jxjm-7q3v — CVE-2026-14198 encoded slash bypass
- GHSA-v9ww-2j6r-98q6 — CVE-2026-33804 duplicate slash bypass
- GHSA-cxrg-g7r8-w69p — CVE-2026-22031 URL encoding bypass
- Node.js absolute-form request handling research
- SentinelOne — CVE-2026-14198 analysis
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.