For: anyone running an API or app on Express (Node.js). No attack steps here — this is a working reference for the defenses you add to a minimal framework: a priority-ordered checklist, per-area guidance, and self-verification. For the cross-framework picture, see the security-by-framework hub.
Priority-ordered hardening checklist
Do this table top-down. P0 is a prerequisite, P1 is the most frequent source of incidents, P2 is ongoing maintenance.
P0 ── Prerequisite (do first)
Headers (helmet) + disable x-powered-by / npm dependency CVE monitoring / secrets from env, no stack exposure
P1 ── Top source of incidents
Input validation and injection defense / owner-scoped authorization
P2 ── Ongoing maintenance
Rate limiting and size caps / sessions, cookies, CSRF / SSRF
| Priority | Control | Specifics (Express) |
|---|---|---|
| P0 | Security headers | helmet-style: CSP/HSTS/X-Content-Type, etc. Disable x-powered-by |
| P0 | npm dependency CVEs | Monitor with npm audit/osv-scanner; judge by running version, patch fast |
| P0 | Secrets and errors | Secrets from env vars. Don't expose stack traces in production |
| P1 | Input validation | Validate/sanitize inputs (type/range/allowed). Body-size caps |
| P1 | Injection | Bind DB queries. Watch NoSQL operator injection ($). Don't pass input to eval |
| P1 | Authorization | Authentication + owner-scoped authorization on each route. JWT: verify signature, pin alg |
| P2 | Rate limiting | Attempt/throughput limits on login/APIs. Curb brute force, abuse, DoS |
| P2 | Sessions/cookies | secure/httpOnly/sameSite. CSRF (cookie sessions). Production session store |
| P2 | SSRF | Allowlist server-side fetches + block internal IPs/metadata |
1. Security headers and reducing exposure (P0)
Express doesn't set security headers by default. Add them first.
- Use helmet-style middleware to add CSP, HSTS, X-Content-Type-Options, frameguard, etc.
- Disable
x-powered-byto reduce framework/version exposure. - Check your own site with the security headers checker.
2. Dependencies (npm) and supply chain (P0)
- Machine-monitor dependency CVEs with
npm auditor osv-scanner, judge by the running version, and patch fast (→ monitoring dependency CVEs). - Node has a large dependency tree and supply-chain risk (typosquatting,
postinstallhooks). Vet at install time and keep the count down. - Keep Node on a supported (LTS) version and don't leave EOL versions in place.
3. Input validation and injection (P1)
- Validate/sanitize all input (body, query, params, headers). Set a body-size cap to curb DoS.
- SQL: bind with placeholders; never build queries by string concatenation (→ what SQL injection is).
- NoSQL: watch for operator injection (
$) via object-typed input (don't pass raw objects into queries). - Don't pass external input to dangerous evaluation like
eval.
4. Authentication and authorization (P1)
Common (dangerous)
- no authorization — "logged in = allowed"
- relying on middleware order/presence alone
- trusting client-supplied IDs/flags as-is
- JWT signature unverified /
alg:noneallowed
Correct
- authentication plus owner-scoped authorization on each route
- verify close to the data (don't rely on order)
- re-verify on the server (resist ID swaps)
- JWT signature verification + pinned alg (reject
alg:none)
See what IDOR is · what JWT is. You can inspect a JWT's contents yourself (JWT decoder).
5. Rate limiting and abuse (P2)
- Apply attempt/throughput limits to login, APIs, and password reset to curb brute force, abuse, and DoS.
- Set size caps on request bodies and uploads (prevent large payloads from using up memory and other resources).
6. Sessions and cookies (P2)
- Set
secure/httpOnly/sameSiteon session cookies. - For cookie-session apps, add CSRF protection (→ what CSRF is).
- Use a proper session store in production (the default in-memory store isn't for production). Regenerate the session on login.
7. SSRF and server-side fetches (P2)
- Server-side fetching of user-supplied URLs should restrict targets to an allowlist and block reaching internal IPs / cloud metadata at connection time (→ what SSRF is).
8. Error handling and production config (P0–P2)
- Use a central error handler and don't expose stack traces externally.
- Run with
NODE_ENV=production. Behind a proxy, settrust proxycorrectly so scheme and client IP aren't misjudged. - Terminate TLS/HTTPS reliably.
Verify: is your Express actually hardened?
Building it isn't the end — it's done only once you've checked. These are defensive self-checks against your own environment.
Headers and exposure
x-powered-by is gone, via the headers checker.Errors don't leak internals
Authorization holds
Dependencies, rate limiting, SSRF
npm audit/osv is clean, login rate-limiting works, and outbound fetches can't reach internal IPs.This site's view: with a minimal framework, you design the defenses yourself
Express's appeal is its lightness and freedom — which means you design the defense too. What Rails and Laravel guard by default (headers, CSRF, built-in authorization support), on Express you wire in deliberately. The focus is working through the table above from the top: set headers, validate input, write authorization on public endpoints, and monitor dependencies for CVEs. Node especially has a large dependency count, so keeping dependencies up to date largely decides whether incidents happen. Drop the assumption that "the framework will guard it" and add the defenses explicitly.
Read next
- Hub: security by framework · Next.js security (also Node)
- Practice: monitoring dependency CVEs · the vulnerability-response playbook
- Glossary: what IDOR is · what SSRF is · what JWT is · what CSRF is
- Tools: security headers checker · JWT decoder
Sources and scope
These primary references support the settings, scope or remediation discussed above; links were checked on October 23, 2026. Consult their version and configuration conditions. Checklist priorities and lessons are this site's guidance, not a full-article revalidation or a safety guarantee.