# What This Checklist Covers (and Who It’s For)#
This guide is a practical web application security checklist for 2026, written for teams shipping production apps with Next.js (App Router or Pages Router), Node.js APIs, and common services (Postgres, Redis, S3, Stripe, etc.). It maps the most common real-world failures to the OWASP Top 10 and shows what to implement, where in Next.js to implement it, and how to verify it.
Security is not “one big task.” It’s a chain of small, testable controls. A single missing header, misconfigured cookie, or permissive CORS rule can undo everything else.
You’ll also see process guidance because security work fails most often due to missing ownership and missing checks in CI/CD. If you want a baseline delivery process to attach these controls to, use our reference: Web Development Process Step-by-Step.
# Checklist Summary Table (Print This)#
Use this table as a release gate. If you can’t check a row, you’re shipping with a known class of risk.
| Area | Minimum for 2026 | Where in Next.js | How to verify |
|---|---|---|---|
| HTTPS + HSTS | TLS 1.2+ (prefer 1.3), HTTP→HTTPS redirect, HSTS (preload if stable) | Vercel/Cloudflare/NGINX + next.config.js headers | SSL Labs grade A, browser devtools strict-transport-security present |
| Security headers | CSP (nonce-based), X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Frame-Options (or CSP frame-ancestors) | next.config.js + Middleware for nonce | curl -I + CSP evaluator |
| Auth | MFA for admins, strong password policy, rate limit login, short-lived sessions, secure cookies | NextAuth/Auth.js or custom + edge rate limit | Try credential stuffing simulation; cookie flags set |
| Session | HttpOnly, Secure, SameSite=Lax/Strict, rotate on auth | API routes / server actions | Inspect cookies; session fixation test |
| Input validation | Schema validation on all inputs (server-side) | Route handlers / API routes / server actions | Send invalid payloads; ensure 4xx + no stack leaks |
| Injection | Parameterized queries, ORM safe APIs, no string-built SQL | DB layer | Attempt SQLi payloads; query logs clean |
| File upload | Type+magic byte checks, size limits, AV scanning, private bucket | API route + storage | Upload polyglot, huge file, wrong mime |
| CORS | Explicit allowlist, no * with credentials | API route responses | Preflight checks; verify browser blocks |
| Secrets | No secrets in client bundle, rotate keys, use KMS/secret store | Env + CI | next build bundle inspection; secret scanning |
| Dependencies | Lockfile, automated patching, remove abandoned deps | CI + Renovate/Dependabot | npm audit + SBOM + SCA reports |
| Logging/monitoring | Auth events, admin actions, anomaly alerts | App + platform | Alert on spikes; trace request IDs |
| Backups | DB backups tested; RPO/RTO defined | DB provider | Restore drill results documented |
🎯 Key Takeaway: Treat this checklist as a release gate, not a “nice-to-have.” Most incidents come from skipped basics, not advanced attacks.
# OWASP Top 10 (2021) → Next.js Controls (2026 Practical Mapping)#
OWASP Top 10 is still the best shared language for prioritization. In production Next.js apps, these risks show up in predictable places: API routes, server actions, middleware, auth callbacks, and third-party scripts.
A01: Broken Access Control#
What it looks like in Next.js
- Client-side checks only (hiding UI) without server authorization.
- API routes that rely on
userIdfrom the body instead of the session. - Insecure direct object references (IDOR):
/api/invoices?id=123returns other users’ invoices.
Checklist
- Enforce authorization server-side on every data access.
- Use deny-by-default permissions: explicit allow rules.
- Validate ownership on resources (
invoice.userId === session.user.id). - Use role-based access control (RBAC) for admin features; consider attribute-based (ABAC) for multi-tenant apps.
Next.js pattern (Route Handler + ownership check)
// app/api/invoices/[id]/route.ts
import { NextResponse } from "next/server";
export async function GET(req: Request, { params }: { params: { id: string } }) {
const session = await getSessionOrThrow(req); // your auth helper
const invoice = await db.invoice.findUnique({ where: { id: params.id } });
if (!invoice || invoice.userId !== session.user.id) {
return NextResponse.json({ error: "Not found" }, { status: 404 });
}
return NextResponse.json(invoice);
}Why it matters: Access control flaws are consistently among the most damaging issues because they lead directly to data exposure. Verizon DBIR regularly shows “basic web app attacks” and credential abuse as top patterns across breaches; access control failures amplify the impact once an attacker gets any foothold.
A02: Cryptographic Failures#
What it looks like
- Sending cookies over HTTP or without
Secure. - Storing sensitive data unencrypted (tokens, PII exports).
- Weak password hashing (anything other than bcrypt/Argon2/scrypt).
Checklist
- Enforce HTTPS everywhere + HSTS.
- Store passwords with Argon2id (preferred) or bcrypt with appropriate cost.
- Encrypt sensitive data at rest (DB-level encryption helps, but also consider application-layer encryption for high-value secrets).
- Never log secrets or tokens.
A03: Injection (SQL/NoSQL/Command/Template)#
What it looks like
- Interpolated SQL strings in Node.
- Unsafe query filters in Mongo/NoSQL.
- Passing untrusted input to shell commands for image processing, PDF generation, etc.
Checklist
- Parameterize queries (ORM or prepared statements).
- Validate and normalize inputs before they reach the DB layer.
- For search/filter endpoints, allowlist fields and operators.
- Avoid shell execution; if unavoidable, strict allowlists and sandboxing.
⚠️ Warning: React escaping does not prevent SQL/NoSQL injection. It only helps for HTML rendering. Treat DB and command execution as separate trust boundaries.
A04: Insecure Design#
What it looks like
- “We’ll add rate limiting later.”
- Multi-tenant logic without explicit tenant isolation.
- No threat modeling for new features (e.g., password reset flow).
Checklist
- Define abuse cases per feature (login, signup, password reset, file upload, payments).
- Add “security acceptance criteria” to tickets (rate limits, logs, permissions).
- Threat model quarterly or before major releases.
A05: Security Misconfiguration#
What it looks like
- Missing CSP/HSTS.
Access-Control-Allow-Origin: *with credentials.- Verbose errors in production, leaking stack traces.
- Unprotected admin routes.
Checklist
- Lock down headers and CORS (explicit allowlists).
- Disable debug logs in production.
- Ensure proper reverse proxy headers (
X-Forwarded-Proto) so auth/cookies behave correctly.
A06: Vulnerable and Outdated Components#
What it looks like
- Old Next.js or React version with known CVEs.
- Abandoned packages included for small utilities.
- Unpinned dependencies or missing lockfile.
Checklist
- Use lockfiles (
package-lock.json/pnpm-lock.yaml). - Enable Dependabot/Renovate.
- Remove dependencies you don’t need.
- Run SCA and produce an SBOM for regulated environments.
A07: Identification and Authentication Failures#
What it looks like
- Weak password reset implementation (token reuse, long expiry).
- Sessions that never expire.
- No brute-force protection on login.
Checklist
- MFA for privileged roles.
- Rate limit login + password reset endpoints.
- Use secure session cookies; rotate session on login and privilege changes.
- Short token expiry, one-time-use reset tokens.
A08: Software and Data Integrity Failures#
What it looks like
- Remote scripts without SRI or integrity controls.
- CI/CD pipeline pushing unreviewed builds.
- Unsigned container images.
Checklist
- Prefer first-party scripts; minimize third-party tags.
- Use subresource integrity (SRI) when possible.
- Require code reviews and branch protection.
- Use CI provenance where feasible.
A09: Security Logging and Monitoring Failures#
What it looks like
- No logs for failed logins or permission checks.
- No alerts on spikes in 401/403/500 or unusual traffic.
- No audit trail for admin actions.
Checklist
- Log auth events and admin actions with request IDs.
- Alert on anomalies (login failures per IP/user, sudden 403 spikes).
- Retain logs with secure access controls.
A10: Server-Side Request Forgery (SSRF)#
What it looks like
- URL fetch endpoints (image proxy, webhook tester, PDF generator) that can reach internal IPs or metadata services.
- Webhook handlers that follow redirects to internal addresses.
Checklist
- Block private IP ranges and metadata endpoints.
- Enforce allowlists for outbound requests where possible.
- Timeouts and size limits on server fetch.
# Authentication Best Practices (2026 Baseline)#
Authentication is where automated attacks concentrate. Credential stuffing is cheap, and leaked password databases are widely available. Your job is to make automated abuse expensive and noisy.
Passwords, MFA, and Account Lifecycle#
Checklist
- Require passwords with length ≥ 12 (passphrases encouraged).
- Use breached password checks (e.g., k-anonymity with Have I Been Pwned API) for consumer apps.
- Enforce MFA for admins and staff accounts; offer MFA for all users where feasible.
- Implement email/phone verification only if it reduces fraud for your domain (avoid extra PII collection without benefit).
- Support account deletion and data export flows securely (re-auth required).
Sessions and Cookies (Next.js-Specific)#
If you use cookie-based sessions (common with Auth.js/NextAuth or custom), configure cookie flags correctly.
Checklist
HttpOnly: true(prevents JS access)Secure: true(HTTPS only)SameSite: Laxfor most apps;Strictfor high-security portals;Noneonly when cross-site usage is required (must beSecure)- Short session TTL + rolling sessions for active users
- Rotate session identifiers on login and privilege change
Cookie example
const sessionCookie = {
name: "__Secure-session",
options: {
httpOnly: true,
secure: true,
sameSite: "lax" as const,
path: "/",
maxAge: 60 * 60 * 24 * 7, // 7 days
},
};ℹ️ Note: If you terminate TLS at a proxy/CDN, ensure your platform forwards the correct scheme. Mis-set
X-Forwarded-Protocan cause cookies to be issued withoutSecureor cause redirect loops.
Rate Limiting and Lockouts#
Baseline controls
- Rate limit by IP and by identifier (email/username).
- Add progressive delays after repeated failures.
- Lockouts should be short and reversible to avoid trivial DoS on user accounts.
In Next.js, you can implement rate limiting in:
- Middleware (edge) for coarse throttling.
- API routes / route handlers for fine-grained logic.
Minimal rate limit (in-memory placeholder; use Redis/Upstash in production)
const attempts = new Map<string, { count: number; resetAt: number }>();
export function rateLimit(key: string, limit = 10, windowMs = 60_000) {
const now = Date.now();
const entry = attempts.get(key);
if (!entry || entry.resetAt < now) {
attempts.set(key, { count: 1, resetAt: now + windowMs });
return { allowed: true };
}
if (entry.count >= limit) return { allowed: false };
entry.count += 1;
return { allowed: true };
}Why it matters: Any in-memory limit breaks on serverless horizontal scaling, so treat this as a local dev pattern only. For production, use a shared store (Redis) or edge rate limiting.
# Input Validation and Output Encoding (Practical Next.js Patterns)#
The fastest way to reduce real risk is to validate inputs at the boundary and keep internal code working with typed, normalized values.
Server-Side Validation with Schemas#
Checklist
- Validate every request body, query string, and route param on the server.
- Reject unknown fields (prevents mass assignment issues).
- Normalize formats (trim strings, lowercase emails).
- Use consistent error shapes (avoid leaking internals).
Zod example for a route handler
import { z } from "zod";
import { NextResponse } from "next/server";
const CreateProject = z.object({
name: z.string().min(2).max(80).trim(),
clientEmail: z.string().email().toLowerCase(),
}).strict();
export async function POST(req: Request) {
const json = await req.json().catch(() => null);
const parsed = CreateProject.safeParse(json);
if (!parsed.success) {
return NextResponse.json({ error: "Invalid input" }, { status: 400 });
}
const project = await db.project.create({ data: parsed.data });
return NextResponse.json({ id: project.id }, { status: 201 });
}Preventing XSS in Next.js#
React escapes string output by default, but XSS still happens through:
dangerouslySetInnerHTML- rendering user-controlled HTML/Markdown without sanitization
- injecting into script contexts (JSON-in-script)
- DOM-based XSS via unsafe client-side sinks
Checklist
- Avoid
dangerouslySetInnerHTML. If unavoidable, sanitize with a well-maintained HTML sanitizer and allowlist tags/attributes. - Don’t build inline scripts from user input.
- Use CSP with nonces to reduce blast radius.
# Content Security Policy (CSP) That Actually Works in Next.js#
A CSP that’s too permissive is theater; a CSP that’s too strict breaks production. The right approach in 2026 is nonce-based CSP for scripts, minimizing third-party scripts, and avoiding unsafe-inline.
Recommended CSP Baseline#
Start strict, then open only what you need. Example baseline (adjust domains):
default-src 'self'script-src 'self' 'nonce-<nonce>' https://trusted-analytics.examplestyle-src 'self' 'unsafe-inline'(many apps still need inline styles; aim to remove over time)img-src 'self' data: https:connect-src 'self' https://api.yourdomain.com https://sentry.ioframe-ancestors 'none'(or allow specific)base-uri 'self'object-src 'none'
Add Security Headers in next.config.js#
Use headers for site-wide defaults. This doesn’t handle per-request nonces by itself, but it sets everything else consistently.
// next.config.js
const securityHeaders = [
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
// Prefer CSP frame-ancestors over X-Frame-Options if you can
{ key: "X-Frame-Options", value: "DENY" },
];
module.exports = {
async headers() {
return [{ source: "/(.*)", headers: securityHeaders }];
},
};Nonce-Based CSP with Middleware (Practical Pattern)#
Middleware can generate a nonce and attach a CSP header. Then your server-rendered pages can read the nonce and apply it to inline scripts you control.
// middleware.ts
import { NextResponse } from "next/server";
import crypto from "crypto";
import type { NextRequest } from "next/server";
export function middleware(req: NextRequest) {
const res = NextResponse.next();
const nonce = crypto.randomBytes(16).toString("base64");
const csp = [
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}'`,
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
].join("; ");
res.headers.set("Content-Security-Policy", csp);
res.headers.set("x-nonce", nonce); // internal use (don’t expose to client JS)
return res;
}Why it matters: CSP is one of the few controls that can still block whole classes of XSS even when a bug slips in. It also reduces impact from compromised third-party scripts.
💡 Tip: Keep a list of every third-party domain your app needs (analytics, error tracking, payments). If you can’t justify it, remove it. Each extra domain expands your
script-src/connect-srcand your attack surface.
# HTTPS, HSTS, and Secure Cookies (Deployment Reality)#
HTTPS is non-negotiable. The most common “HTTPS issues” in Next.js aren’t about certificates; they’re about redirects, proxy headers, and cookie security.
HTTPS Checklist#
At the edge (CDN / load balancer)
- TLS 1.2+ enabled; prefer TLS 1.3.
- Redirect HTTP→HTTPS.
- Use HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains(addpreloadonly when you’re confident).
In the app
- Issue cookies with
Secure. - Avoid mixed content (HTTP images/scripts).
Verify
- Run Qualys SSL Labs. Aim for A.
- Check headers:
curl -I https://yourdomain.comHSTS Example (Edge/Header)#
If you add HSTS in Next.js headers, ensure all traffic is behind HTTPS first.
// next.config.js (add only if HTTPS is guaranteed)
{ key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains" }Why it matters: Without HSTS, users can be downgraded to HTTP in certain hostile network conditions. With HSTS, browsers refuse insecure connections after the first secure visit.
# Secure API Routes and Server Actions (Hardening Patterns)#
Next.js makes it easy to ship endpoints quickly. That speed is exactly how teams ship insecure defaults.
CORS: Be Explicit#
Checklist
- If your API is same-origin only, don’t add CORS at all.
- If you need CORS, use an allowlist of origins.
- Never use
Access-Control-Allow-Origin: *withAccess-Control-Allow-Credentials: true.
CORS allowlist example
const allowed = new Set(["https://app.example.com", "https://admin.example.com"]);
export function corsHeaders(origin: string | null) {
if (!origin || !allowed.has(origin)) return {};
return {
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Credentials": "true",
"Vary": "Origin",
};
}CSRF: Know When It Applies#
If you use cookie-based auth and accept state-changing requests (POST/PUT/DELETE), CSRF matters.
Checklist
- Prefer SameSite=Lax cookies as a baseline.
- Use CSRF tokens for sensitive operations.
- Require re-authentication for critical actions (change email, payout details).
Error Handling: Don’t Leak Internals#
Checklist
- Return generic errors to clients.
- Log detailed errors server-side with a request ID.
- Disable verbose stack traces in production.
# Data Layer Security: SQL/ORM, Multi-Tenancy, and Migrations#
Many “web security” incidents are actually data access bugs.
Parameterized Queries and ORM Safety#
Checklist
- Use ORM query builders (Prisma, Drizzle, etc.) or prepared statements.
- Avoid raw queries unless you can parameterize and centralize them.
- For filters/sorting, allowlist fields to prevent abuse.
Multi-Tenant Isolation (If You Have Workspaces)#
Checklist
- Every query includes
tenantIdfrom the session, not from the client. - Add DB constraints where possible (composite keys, RLS).
- Test tenant escape scenarios.
A practical option for Postgres is Row-Level Security (RLS), which can prevent entire classes of mistakes. If RLS is too heavy, enforce tenant scoping in your data access layer and write automated tests that attempt cross-tenant reads.
# Security Headers Checklist (Beyond CSP)#
Headers are cheap controls with measurable benefits. They also reduce the impact of user mistakes (opening links, embedding your app in iframes, etc.).
Recommended baseline
X-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: ...(deny what you don’t use)Content-Security-Policy: ...(nonce-based when possible)Strict-Transport-Security: ...(HTTPS only)X-Frame-Options: DENY(or use CSPframe-ancestors)- Avoid deprecated
X-XSS-Protection(modern browsers ignore it)
# Dependency and Supply Chain Security (The Stuff That Bites Later)#
In modern React/Next ecosystems, you likely ship hundreds of transitive dependencies. That’s not inherently bad, but it requires automation.
Checklist#
- Enable Dependabot/Renovate; patch weekly at minimum.
- Fail CI on critical vulnerabilities when a fix exists.
- Keep Next.js and Node.js on supported versions.
- Use
npm ci(orpnpm install --frozen-lockfile) in CI to guarantee reproducible builds. - Run secret scanning in CI (GitHub Advanced Security or alternatives).
- Consider generating an SBOM for enterprise customers.
CI dependency audit (minimal)
npm ci
npm audit --audit-level=highWhy it matters: Supply-chain attacks often don’t look like “hacking your app.” They look like a compromised dependency or build pipeline that injects malicious code into your production bundle.
# Logging, Monitoring, and Incident Readiness#
A secure app that you can’t observe is insecure in practice. You need enough telemetry to detect abuse quickly and enough discipline to respond.
What to Log (Minimum)#
| Event type | Fields to include | Why it matters |
|---|---|---|
| Login success/failure | userId/email hash, IP, user-agent, timestamp | Detect credential stuffing and account takeover |
| Password reset requested/completed | userId, IP, timestamp | Detect abuse and fraud |
| Permission denied (403) | userId, route, tenantId, requestId | Finds access-control probing |
| Admin actions | actorId, targetId, action, before/after, requestId | Audit trail for internal abuse or mistakes |
| Webhooks received | provider, eventId, signature status | Detect spoofed events and replay |
Alerts That Catch Real Incidents#
- Spike in failed logins per IP or per account.
- Spike in 403s on a single endpoint.
- Unusual outbound traffic volume (SSRF/data exfil signals).
- Error rate increase after deployment (signals broken auth or security header misconfig).
# Practical “Release Gate” Checklist for Next.js (Copy/Paste)#
Use this as your pre-release checklist. It’s short enough to run every sprint, but covers the highest ROI items.
- 1HTTPS and HSTS
- HTTP→HTTPS redirect at the edge
- HSTS enabled (after confirming HTTPS everywhere)
- 2Headers
- CSP present (nonce-based if you have inline scripts)
- nosniff, referrer-policy, permissions-policy set
- 3Auth and Sessions
- Rate limit login/reset
- Secure cookies (HttpOnly/Secure/SameSite)
- MFA for admins
- 4Input Validation
- Server-side schema validation on every endpoint
- Strict mode (unknown fields rejected)
- 5Access Control
- Server-side authorization on every data read/write
- Multi-tenant scope enforced
- 6Injection
- No string-built SQL
- Allowlisted sorting/filtering
- 7Uploads
- Size/type limits
- Private storage + signed URLs
- 8Dependencies
- Automated patching enabled
- CI blocks critical vulns with fixes
- 9Monitoring
- Auth and admin audit logs
- Alerts for login/403 spikes
- 10Backups
- Restore drill completed and documented
# Common Pitfalls We See in Next.js Apps#
- 1Relying on client-side route protection — attackers call APIs directly.
- 2Assuming CSP is “too hard” — teams skip it, then add multiple third-party scripts.
- 3Cookie misconfiguration behind proxies — missing
Secureor wrong domain/path. - 4CORS misconfiguration —
*origins plus credentials, exposing authenticated data. - 5Validation only on the client — the server accepts invalid or malicious payloads.
- 6No rate limiting — bot attacks hit login/reset endpoints until something breaks.
# Key Takeaways#
- Enforce server-side access control and tenant scoping on every API/route handler; UI checks don’t count.
- Implement secure authentication: MFA for admins, rate-limited login/reset, short-lived sessions, and correctly flagged cookies.
- Use server-side schema validation (strict allowlists) to kill entire classes of injection and mass-assignment bugs.
- Ship real security headers: CSP (prefer nonce-based), HSTS, and modern baseline headers set globally.
- Treat HTTPS + proxy correctness as an app feature: secure cookies and redirects depend on it.
- Automate dependency patching and security checks in CI so security doesn’t rely on memory.
# Conclusion#
A 2026-grade web application security checklist is not about adding one security tool; it’s about making secure defaults repeatable in your Next.js stack—headers, cookies, validation, access control, and monitoring. If you want a team to implement this checklist end-to-end (including CSP tuning, auth hardening, and CI security gates) as part of a reliable delivery process, start with our Web Development Process Step-by-Step and reach out via Samioda Mobile & Web Development to review your app’s security posture and ship improvements safely.
FAQ
More in Web Development
All →Tailwind CSS Best Practices: Building Maintainable UIs (Production Patterns for 2026)
A production-focused guide to tailwind CSS best practices: component patterns, custom config, dark mode, responsive strategy, and copy‑pasteable examples for maintainable UIs.
React Server Components (RSC): What They Are and How to Use Them in Next.js App Router
A practical 2026 guide to React Server Components: what they are, why they matter, and how to use server vs client components correctly in Next.js App Router with copy-pasteable examples.
Technical SEO for Developers (Next.js): Everything You Need to Know in 2026
A practical, developer-focused guide to technical SEO in Next.js: meta tags, structured data, Core Web Vitals, sitemaps, robots.txt, canonical URLs, and production-ready examples.
Need help with your project?
We build custom solutions using the technologies discussed in this article. Senior team, fixed prices.
Related Articles
React Server Components (RSC): What They Are and How to Use Them in Next.js App Router
A practical 2026 guide to React Server Components: what they are, why they matter, and how to use server vs client components correctly in Next.js App Router with copy-pasteable examples.
API Integration Guide: Best Practices for 2026
A practical API integration guide for 2026: REST vs GraphQL, authentication, error handling, retries, rate limiting, and production-ready Next.js API route examples.
Progressive Web Apps (PWA): Complete Guide for 2026
A practical progressive web app PWA guide for 2026: concepts, business benefits vs native apps, and a step-by-step Next.js implementation with manifest and service worker code.