Protect your app and your users. Common threats explained simply, with practical steps you can ask AI to implement.
An attacker injects malicious JavaScript into your website. When other users visit the page, the script runs in their browser — stealing cookies, passwords, or redirecting them. Happens when you display user input without sanitizing it. Fix: never insert raw HTML from users.
An attacker tricks a logged-in user into making a request they didn't intend — like changing their email or transferring money. The user visits a malicious site that sends a hidden request to your app using their session. Fix: use CSRF tokens on forms.
An attacker puts database commands into a form field. If your app builds SQL queries from raw user input, the attacker can read, modify, or delete your entire database. Fix: always use parameterized queries or an ORM like Prisma.
An attacker puts your website inside an invisible iframe on their page. Users think they're clicking on the attacker's site but are actually clicking buttons on yours. Fix: add the X-Frame-Options header to prevent framing.
Check that form data matches expected formats (email looks like email, numbers are numbers). Reject anything unexpected on both client and server.
Never render raw user input as HTML. Use text content instead of innerHTML. React does this automatically, but be careful with dangerouslySetInnerHTML.
Encrypt all traffic between your users and your server. Most hosting platforms (Vercel, Netlify) enable HTTPS automatically. Never serve your site over plain HTTP.
Set HttpOnly (no JavaScript access), Secure (HTTPS only), and SameSite=Strict flags on cookies. This prevents most session hijacking attacks.
Run npm audit regularly to check for known vulnerabilities in your packages. Update outdated packages, especially security-critical ones like auth libraries.
Tools like Prisma or Drizzle automatically prevent SQL injection by using parameterized queries. Never build SQL strings by concatenating user input.
HTTP headers you add to your server responses that tell the browser how to protect your users. Add these in your Next.js config or middleware.
Controls which resources (scripts, styles, images) your page can load. Blocks inline scripts by default, which stops most XSS attacks. The most powerful security header.
Blocks unauthorized scripts
Prevents inline JavaScript injection
Controls which domains can serve content
Prevents your site from being loaded inside an iframe on another website. Set to DENY or SAMEORIGIN. Stops clickjacking attacks completely.
DENY — never allow framing
SAMEORIGIN — only your own site
Prevents clickjacking attacks
Controls which other websites can make API requests to your server. Without CORS, any website could call your API. Set Access-Control-Allow-Origin to only trusted domains.
Restricts API access to trusted origins
Protects against unauthorized requests
Configure per-route for flexibility
A rule that limits how many requests a user or IP address can make in a time window. For example: max 10 API calls per minute. After hitting the limit, the server responds with a 429 'Too Many Requests' error until the window resets.
Without rate limiting, anyone can spam your API with thousands of requests per second. This can crash your server, rack up huge bills (especially for AI APIs), and allow brute-force password attacks. Rate limiting is your first line of defense.
A serverless rate limiting library that works with Vercel and Next.js. Uses Redis under the hood. Set up in minutes: define a limit (e.g., 10 requests/minute) and apply it to any API route.
Apply rate limits at the middleware level so every request is checked before reaching your API routes. Use Next.js middleware combined with Upstash or a simple in-memory store for development.
API keys, database URLs, and auth secrets must only be used in server-side code (API routes, server components). If a variable starts with NEXT_PUBLIC_, it's visible to everyone.
CriticalStore secrets in a .env.local file in your project root. This file is automatically ignored by git. Never commit .env files to your repository.
Best PracticeVariables like DATABASE_URL or OPENAI_API_KEY (no NEXT_PUBLIC_ prefix) are only available on the server. They can't be accessed from client components or the browser.
ImportantOn Vercel, Netlify, or Railway, add environment variables in the project settings. These are encrypted and injected at build/runtime. Never hardcode secrets in your code.
Best PracticeCopy-paste these prompts to quickly add security features to your project.
"Add rate limiting to my API routes — max 10 requests per minute per IP. Use Upstash Rate Limit. Return a 429 status with a friendly error message when the limit is exceeded."
"Add security headers to my Next.js app: Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy. Configure them in next.config.js."
"Audit my app for XSS vulnerabilities. Check all places where user input is rendered and make sure nothing uses dangerouslySetInnerHTML with unsanitized data. Add input validation to all forms."
"Add CSRF protection to my form submissions. Generate a CSRF token on the server, include it as a hidden field in forms, and validate it on submission."
"Set up environment variables properly. Move all API keys and secrets to .env.local, make sure nothing sensitive has the NEXT_PUBLIC_ prefix, and add .env.local to .gitignore."