← Back to Feed CACHED · 2026-07-18 13:32:21 · CACHE_KEY CVE-2026-16117
CVE-2026-16117 · CWE-20 · Disclosed 2026-07-18

@fastify/http-proxy vulnerable to prefix escape via URL-encoded characters

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

The bouncer checks IDs at the door but forgets they can be written in leetspeak

@fastify/http-proxy lets you mount an upstream service under a URL prefix and rewrite that prefix off before forwarding — e.g. /pub/* at the edge is stripped to /* at the backend. The bug is that Fastify's router decodes percent-encoded characters when matching the route, but the prefix-rewriting logic then operates on the raw, still-encoded request.url. A request to /%61pi/data matches the /api route for handler dispatch, but the rewriter can't find the literal string /api in the encoded URL, so no rewrite happens and the raw path is proxied through. Every version up to and including 11.5.0 is affected; 11.6.0 contains the fix.

Vendor scored this CVSS 10.0 Critical with Scope:Changed, C:H/I:H — reasoning that the flaw in the proxy directly compromises a separate upstream security zone. That framing is defensible for the worst-case deployment (a proxy fronting an unauthenticated internal admin API), but it treats every install as that worst case. In real fleets @fastify/http-proxy is often a routing convenience in front of already-authenticated services, and there is no RCE, no data destruction, and no lateral movement primitive on its own — the blast radius is *whatever the upstream chose to hide behind the prefix*. That is a HIGH, not a universal 10.

"Trivial prefix bypass against any Fastify proxy that trusts rewritePrefix as an authorization boundary. HIGH, not the vendor's 10.0."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Locate a Fastify proxy in front of a segmented upstream

Attacker enumerates services that expose @fastify/http-proxy — fingerprintable via Fastify's default headers, error bodies, or route probing. They confirm a prefix + rewritePrefix deployment by observing that /pub/anything returns 404s or upstream errors while /pub/ returns the upstream root.
Conditions required:
  • Reachable HTTP endpoint running vulnerable @fastify/http-proxy ≤ 11.5.0
  • Proxy uses prefix + rewritePrefix to hide a subset of upstream paths
Where this breaks in practice:
  • Fastify's overall enterprise footprint is modest vs. Express/Nest
  • Many deployments front the proxy with nginx/Cloudflare/WAF which may normalize encoded paths before Fastify sees them
Detection/coverage: Shodan/Censys can fingerprint Fastify via headers; no dedicated GreyNoise tag for this CVE as of disclosure day
STEP 02

Percent-encode the first character of the target path

Attacker crafts /%61pi/admin (where %61 == a). Fastify's router decodes the path for route matching and dispatches to the /api proxy handler. The rewrite step then does a literal string search for /api inside request.url — which still contains the encoded form — and finds no match, so it forwards /%61pi/admin upstream unchanged. Trivial one-liner with curl or Burp Repeater; no exploit tool required.
Conditions required:
  • No upstream WAF/CDN normalizing percent-encoding before Fastify
  • Upstream tolerates encoded paths in routing (Node http, Express, most WSGI stacks do)
Where this breaks in practice:
  • Cloudflare, Akamai, AWS CloudFront, and nginx merge_slashes/normalize_uri configs will canonicalize %61a before it reaches Node
  • Some upstreams reject encoded reserved-set characters
Detection/coverage: WAF rulesets that flag encoded alphanumerics in path (rare); no Nuclei template published at time of writing
STEP 03

Reach paths the proxy was meant to hide

The upstream now receives requests to paths outside the intended rewritePrefix scope — e.g. /internal/*, /admin/*, or sibling services on the same origin. If the upstream trusted the proxy to enforce path scoping (a common but risky pattern), the attacker now has unauthenticated access to those endpoints.
Conditions required:
  • Upstream uses proxy prefix as its access-control boundary
  • Sensitive endpoints exist on the same upstream origin
Where this breaks in practice:
  • Any upstream that enforces its own authn/authz on /admin is unaffected
  • Zero-trust / mTLS between proxy and upstream neutralizes the impact
Detection/coverage: Upstream access logs will show requests to unexpected paths originating from the proxy's IP — cheap to alert on with a NOT startswith(/expected_prefix) filter
STEP 04

Chain to SSRF / data theft / admin action

Depending on what the upstream exposes: internal debug endpoints (/actuator, /metrics, /debug/pprof), cloud metadata proxies, admin APIs, or other tenant paths. No exploit toolkit needed — this is a raw HTTP request. No published PoC repo beyond the advisory description as of 2026-07-18.
Conditions required:
  • Sensitive functionality reachable at the newly-accessible upstream paths
Where this breaks in practice:
  • Depends entirely on what the specific upstream exposes — many deployments have nothing juicy behind the prefix
Detection/coverage: Standard upstream anomaly detection; SIEM rules on admin-endpoint access from the proxy identity
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo known exploitation as of disclosure (2026-07-18). Not in CISA KEV.
Proof-of-conceptAdvisory itself publishes the working payload (/%61pi/data). No standalone GitHub PoC repo yet; expect Nuclei template within days.
EPSSNot yet scored (disclosure day). Comparable path-normalization bugs typically land 0.5–3% in the first week.
KEV statusNot listed
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N — network, unauth, Scope:Changed drives the 10.0 (impact crosses to the upstream service)
Affected versions@fastify/http-proxy ≤ 11.5.0 (all releases). Prior to the 2022 npm scope migration, published as fastify-http-proxy.
Fixed version11.6.0no backports to older majors; upgrade is the only path
Exposure populationnpm weekly downloads for @fastify/http-proxy are in the low hundreds of thousands — meaningful but 1–2 orders of magnitude below Express-based proxies. Public-internet Fastify fingerprints on Shodan number in the low tens of thousands.
Disclosure date2026-07-18 via GitHub Security Advisory GHSA-mx7v-qhg9-2mvv
Reporter / fixReported by tndud042713; patched by Matteo Collina (mcollina, Fastify lead maintainer)
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to HIGH (8.5/10)

Reassessed HIGH rather than the vendor's 10.0 because the single most decisive factor is *conditional blast radius*: the vulnerability only converts to real compromise when the upstream service trusts rewritePrefix as an authorization boundary — an insecure-by-design pattern present in a minority of deployments. Where that pattern exists the outcome is severe (unauthenticated access to hidden admin/internal paths), which is why we hold the line at HIGH and refuse to drop to MEDIUM.

HIGH Technical accuracy of the flaw and fix version
HIGH Exploit simplicity (single crafted URL, no auth)
MEDIUM Real-world blast radius — depends on upstream trust model per install
LOW Exploitation velocity — no telemetry yet; disclosed today

Why this verdict

  • Trivial exploitation: unauthenticated, network-reachable, one crafted URL. AC:L/PR:N/UI:N is accurate — this is the easiest possible bug to fire.
  • Conditional impact: the actual damage requires the upstream to have delegated path-based authz to the proxy. Many mature deployments authenticate independently at the upstream — those installs see zero material impact.
  • Role multiplier — API gateway in front of unauthenticated internal admin API: chain succeeds, blast radius is tenant-scale data / admin-action exposure. Plausible in perhaps 10–25% of @fastify/http-proxy installs given the library's ergonomics encourage exactly this pattern. Floor stays at HIGH.
  • Role multiplier — proxy fronting a fully-authenticated upstream (JWT/session at backend): chain reaches new paths but hits auth walls. Blast radius = information disclosure at most.
  • Role multiplier — hobby / low-traffic sites: irrelevant.
  • No supply-chain or fleet-wide amplifier: this is a per-service bug, not a domain/PKI/EDR/CI compromise vector. That's the reason CRITICAL is not the floor.
  • Upstream WAF / CDN often neutralizes: Cloudflare, CloudFront, Akamai, and default nginx configs normalize %61a before Fastify sees it, silently killing the attack for a large fraction of internet-exposed instances.

Why not higher?

Not CRITICAL because @fastify/http-proxy is not canonically a high-value-role component (it's not an IdP, hypervisor, CA, backup, or kernel-mode agent), and the deployment share where the chain ends in fleet-scale or supply-chain compromise is not ≥10%. The CVSS 10.0 assumes universal Scope:Changed catastrophic impact; real installs vary from 'no impact' to 'tenant admin access' depending on upstream authz.

Why not lower?

Not MEDIUM because when the vulnerable pattern *is* present, the attack is unauthenticated, one-request, no user interaction, and directly yields access to intentionally-hidden endpoints. Same-day advisory PoC means weaponization is measured in hours, and downgrading below HIGH would fail teams whose gateways trust rewritePrefix — a non-trivial minority.

05 · Compensating Control

What to do — in priority order.

  1. Upgrade @fastify/http-proxy to ≥ 11.6.0 — Only real fix. Bump the dependency, run integration tests against your prefix routes, redeploy. For a HIGH verdict noisgate mandates rollout within 30 days; if the proxy is internet-exposed AND fronts an admin API, treat as CRITICAL and roll within 3 days.
  2. Front the proxy with a normalizing edge (nginx, Cloudflare, CloudFront) — Configure the edge to decode and re-canonicalize the request path before forwarding to Fastify. In nginx, this is default behavior for the location URI; verify with curl --path-as-is against staging. Deploy within 30 days as a defense-in-depth belt-and-suspenders even after patching.
  3. Enforce authn/authz at the upstream, not at the proxy prefix — Architectural fix — if /admin requires being authenticated at the upstream, no prefix bypass matters. Treat rewritePrefix as *routing*, never as *authorization*. Do this alongside the patch, tracked as a hardening item within 30 days.
  4. Add SIEM alert for upstream requests outside the expected prefix scope — The upstream sees the proxy's identity as the client. Alert on any request whose path does not begin with the intended rewritePrefix target — this catches both this CVE and any future prefix-bypass regression. Deploy within 30 days.
  5. SBOM sweep for fastify-http-proxy (pre-scope) and @fastify/http-proxy (post-scope) — The package was renamed during the npm-scoped migration; both names exist in older lockfiles. Use npm ls @fastify/http-proxy fastify-http-proxy or your SCA (Snyk, Dependabot, Renovate) across all 10k hosts within 30 days.
What doesn't work
  • Blocking % in URLs at the edge — breaks legitimate percent-encoded query strings, form submissions, and non-ASCII paths. Overbroad and will cause user-visible outages.
  • Fastify's own route-level onRequest hook to normalize paths — the bug is in the proxy plugin's rewrite step, not in Fastify core. A hook that mutates request.url before the proxy handler can help, but it's fiddly and unsupported. Just patch.
  • Rate limiting — irrelevant. A single request achieves impact.
  • Network segmentation between proxy and upstream — does not help; the attacker is riding the proxy's authorized channel to the upstream.
  • Rotating credentials / secrets — no credential compromise is involved.
  • mTLS between proxy and upstream — does NOT help by itself; the proxy still authenticates to the upstream and forwards the malicious path.
06 · Verification

Crowdsourced verification payload.

Run on any host or CI runner with Node/npm project checkouts. From the project root, invoke ./check-fastify-http-proxy.sh . (or a path to a directory to scan recursively for package-lock.json / pnpm-lock.yaml / yarn.lock). No elevated privileges required — just read access to the lockfiles.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# noisgate verification — CVE-2026-16117 @fastify/http-proxy prefix escape
# Usage: ./check-fastify-http-proxy.sh <path-to-scan>
# Exit codes: 0 PATCHED, 1 VULNERABLE, 2 UNKNOWN
set -u
ROOT="${1:-.}"
FIXED_MAJOR=11
FIXED_MINOR=6
FIXED_PATCH=0
STATUS="UNKNOWN"
FOUND=0

ver_ge() {
  # returns 0 if $1 >= FIXED
  local v="$1"
  local maj min pat
  IFS='.' read -r maj min pat <<< "$v"
  maj=${maj:-0}; min=${min:-0}; pat=${pat%%[!0-9]*}; pat=${pat:-0}
  if   (( maj > FIXED_MAJOR )); then return 0
  elif (( maj < FIXED_MAJOR )); then return 1
  elif (( min > FIXED_MINOR )); then return 0
  elif (( min < FIXED_MINOR )); then return 1
  elif (( pat >= FIXED_PATCH )); then return 0
  else return 1
  fi
}

scan_lockfile() {
  local f="$1"
  # Match both scoped (@fastify/http-proxy) and legacy (fastify-http-proxy)
  local versions
  versions=$(grep -Eo '"(@fastify/http-proxy|fastify-http-proxy)"[^{]*"version"[[:space:]]*:[[:space:]]*"[0-9]+\.[0-9]+\.[0-9]+"' "$f" 2>/dev/null \
    | grep -Eo '"version"[[:space:]]*:[[:space:]]*"[0-9]+\.[0-9]+\.[0-9]+"' \
    | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')
  # yarn.lock style
  local yv
  yv=$(grep -E '^("?@?fastify[/-]http-proxy)' -A2 "$f" 2>/dev/null | grep -Eo 'version "[0-9]+\.[0-9]+\.[0-9]+"' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')
  versions="$versions"$'\n'"$yv"
  while IFS= read -r v; do
    [ -z "$v" ] && continue
    FOUND=1
    if ver_ge "$v"; then
      echo "[OK]   $f -> @fastify/http-proxy $v (>= 11.6.0)"
    else
      echo "[VULN] $f -> @fastify/http-proxy $v (< 11.6.0 — CVE-2026-16117)"
      STATUS="VULNERABLE"
    fi
  done <<< "$versions"
}

while IFS= read -r -d '' lock; do
  scan_lockfile "$lock"
done < <(find "$ROOT" -type f \( -name package-lock.json -o -name pnpm-lock.yaml -o -name yarn.lock -o -name npm-shrinkwrap.json \) -print0 2>/dev/null)

if [ "$STATUS" = "VULNERABLE" ]; then
  echo "RESULT: VULNERABLE"; exit 1
fi
if [ $FOUND -eq 1 ]; then
  echo "RESULT: PATCHED"; exit 0
fi
echo "RESULT: UNKNOWN (no @fastify/http-proxy references found under $ROOT)"
exit 2
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: run the SBOM/lockfile sweep across every Node service in your estate for both @fastify/http-proxy and legacy fastify-http-proxy. Any hit below 11.6.0 goes on the fast track. Per the noisgate mitigation SLA for HIGH, you have 30 days to deploy compensating controls — front the proxy with a path-normalizing edge (nginx/Cloudflare) and add a SIEM alert on upstream requests outside the expected prefix; per the noisgate remediation SLA for HIGH, the actual upgrade to 11.6.0 must land within 180 days. Escalate any specific instance to CRITICAL timelines (3-day mitigation, 90-day remediation) if the proxy is internet-exposed AND fronts an upstream that relies on the prefix for access control — that combination is the only shape of this bug that reaches domain/tenant blast radius, and it deserves same-week attention.

Sources

  1. GitHub Security Advisory GHSA-mx7v-qhg9-2mvv
  2. fastify/fastify-http-proxy repository
  3. Prior related advisory GHSA-c4qr-gmr9-v23w (CVE-2021-21322)
  4. OpenJS Foundation CNA advisories
  5. Snyk — fastify-http-proxy package
  6. OpenCVE — Fastify vendor listing
  7. CISA Vulnerability Summary (week of 2026-06-29)
  8. NorthIT prior-art writeup on fastify-http-proxy prefix escape
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.