Safeguard
Application Security

Comparing Node.js frameworks for security: Express, Fastify, NestJS

Express, Fastify, and NestJS compared on real CVE history, default security posture, and dependency risk — plus how to close the gaps framework choice alone can't.

Priya Mehta
DevSecOps Engineer
7 min read

Express, Fastify, and NestJS together power a large majority of production Node.js APIs, and security teams increasingly ask which one is "safest" before greenlighting a new service. The honest answer is that framework choice matters less than dependency hygiene and configuration — but the frameworks are not identical. Express carries 12+ years of accumulated middleware sprawl and a routing engine that shipped a ReDoS vulnerability as recently as September 2024. Fastify ships JSON Schema validation and a smaller default dependency tree. NestJS adds architectural guardrails but usually sits on top of Express by default, inheriting its risk profile unless explicitly configured otherwise. This post compares the three on concrete CVE history, default security posture, and dependency exposure, then explains how Safeguard helps teams catch the gaps that framework choice alone can't close — regardless of which one you run in production.

Which Node.js framework has the fewest publicly disclosed CVEs?

Fastify has the shortest CVE history of the three, but that reflects smaller install base and a leaner dependency graph more than inherent superiority. Express, with roughly 35 million weekly npm downloads as of mid-2025, is the most heavily scrutinized and therefore the most frequently patched. Its core has had a manageable number of direct CVEs, but its historical reliance on the path-to-regexp and qs packages pulled in vulnerabilities that affected every Express app transitively. CVE-2024-45296, disclosed in September 2024, was a denial-of-service (ReDoS) flaw in path-to-regexp versions before 0.1.10, 3.3.0, 6.3.0, and 8.0.0 — a library Express used internally for route matching for over a decade. CVE-2022-24999, a prototype pollution bug in qs (versions before 6.10.3), affected Express's query-string parsing pipeline the same way. Fastify, at roughly 3-4 million weekly downloads, has had far fewer such transitive incidents, partly because it uses find-my-way for routing instead of path-to-regexp, and partly because it simply has fewer eyes probing it. NestJS inherits whichever CVE surface its underlying HTTP adapter carries — Express by default, Fastify if you swap it in during project setup.

Does Fastify's built-in schema validation actually reduce vulnerabilities?

Yes, measurably, because it closes an entire class of input-validation bugs before a request handler ever runs. Fastify requires route schemas to be defined using JSON Schema and validates every incoming request against them via ajv at the framework level, rejecting malformed payloads with a 400 before any application code executes. Express has no equivalent built in — validation is opt-in via middleware like express-validator or joi, and it's trivial to ship a route with zero validation, which is precisely the pattern behind a large share of the injection and mass-assignment bugs Safeguard's research team sees in customer codebases. NestJS narrows this gap with its class-validator and ValidationPipe decorators, which apply schema-like constraints declaratively across an entire module rather than per-route, but that protection depends on developers remembering to attach @UsePipes(new ValidationPipe()) — it is not on by default the way Fastify's is. In practice, teams running Fastify in strict mode report catching malformed or malicious payloads at the framework boundary that would have reached business logic in an unvalidated Express route.

How much of Express's risk actually comes from middleware, not the core?

Almost all of it — Express core is deliberately minimal, so nearly every security-relevant decision happens in third-party middleware you choose. A default express() app has no CORS policy, no rate limiting, no security headers, and no body-size limits until you add cors, express-rate-limit, helmet, and body-parser configuration yourself. This is by design (Express calls itself "unopinionated"), but it means the same framework can be hardened or wide open depending entirely on which of the roughly 25,000+ npm packages tagged "express-middleware" a team happens to install and how current those packages are. Helmet alone has shipped over a dozen minor releases since 2023 adjusting default CSP and header behavior as browser security models evolved. Fastify folds equivalent protections into first-party plugins (@fastify/helmet, @fastify/rate-limit, @fastify/cors) maintained by the core team under the same versioning and support cadence as the framework itself, which reduces the chance of a team running an abandoned or misconfigured third-party package. NestJS wraps its adapter's middleware in guards and interceptors, but a NestJS app running on Express still inherits Express's blank-slate defaults underneath the abstraction.

Is NestJS more secure simply because it enforces structure?

No — NestJS enforces consistency, not security, and consistency only helps if the enforced patterns are themselves secure. NestJS's dependency-injection architecture and decorator-based guards (@UseGuards, @Roles) make it dramatically easier to apply authentication and authorization checks uniformly across a large codebase, which matters at scale: a 200-endpoint NestJS app can enforce a single AuthGuard globally in one line, while the equivalent Express app requires that middleware be manually attached to every router or route, and a single missed attachment is an unauthenticated endpoint in production. That said, NestJS was built on Express through version 10 by default (with Fastify as an opt-in adapter since NestJS 7), so it directly inherits whatever CVEs and default-config gaps its adapter has — the structural benefits are additive, not a substitute. NestJS also pulls in a noticeably larger dependency tree than either Express or Fastify alone (core packages plus @nestjs/platform-express, reflect-metadata, rxjs, and typically a validation and config package), which widens the supply-chain attack surface even as it tightens application-layer discipline.

What actually determines whether a Node.js API is secure in production?

The framework's default posture matters, but the transitive dependency tree and how quickly patches get shipped matter more. A 2024 audit pattern Safeguard's research team observes repeatedly: teams running Express 4.x with path-to-regexp pinned below 0.1.10 remained exposed to CVE-2024-45296 for months after the patch shipped, not because Express itself was unpatched, but because path-to-regexp is a nested dependency several layers deep that dependency-update tooling deprioritized. Express 5.0, released as stable in September 2024 after roughly a decade in development, replaced the vulnerable routing internals and dropped support for Node.js versions below 18 — but migration from Express 4 has been slow, and Express 4 remains in active production use across a large share of existing deployments. Fastify 5, also released in 2024, similarly requires Node 20+ and tightened several plugin APIs. The uncomfortable finding across all three frameworks is the same: framework choice sets a baseline, but a well-configured Express app with current dependencies and Helmet, rate limiting, and strict validation middleware can be more secure in practice than a Fastify or NestJS app running two-year-old, unpatched dependencies.

How Safeguard Helps

Framework selection is a starting posture, not a guarantee — which is why Safeguard focuses on what's actually running in your environment rather than what a framework's defaults promise. Safeguard's reachability analysis determines whether a vulnerable path like the path-to-regexp or qs chains described above is actually invoked by your application's code paths, so security teams can triage CVE-2024-45296-style alerts by exploitability instead of drowning in every transitive match a scanner flags. Griffin AI, Safeguard's investigation agent, correlates that reachability data against your Express, Fastify, or NestJS route definitions and middleware stack to explain in plain language why a finding is or isn't a real risk. Safeguard generates and ingests SBOMs across all three ecosystems to track exactly which routing, validation, and middleware packages each service depends on as new advisories land, and where a fix is available, Safeguard opens an auto-fix PR that bumps the affected dependency and runs your test suite — so patching a framework-level CVE takes minutes instead of a multi-team migration project.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.