Skip to Content
FrontendSecuritySecurity checklist

Security checklist

⏱️ ETA: 5h 15 min

🎯 Goal: Enhance the security of your Next.js applications by following best practices across various areas.

Details about each point of the checklist

Refs :

This checklist is a collection of security best practices for Next.js applications. It covers a wide range of topics :

  • Dependency management
  • Data validation
  • Environment variables
  • Data exposure
  • Security headers
  • Server-side security
  • Centralized security functions
  • Data handling
  • Development and Security tools
  • Self-hosted deployments
  • Content Security Policy
  • Search parameters and user inputs
  • Cross-Site Scripting (XSS) protection
  • Cross-Site Request Forgery (CSRF) protection
  • Error handling

By following these guidelines, you can enhance the security of your Next.js projects and protect them from common vulnerabilities.

1. Keep Dependencies Updated

⏱️ ETA: 40 min

🎯 Goal: Prevent vulnerabilities, bugs, and incompatibilities by maintaining up-to-date packages.

A. Regularly audit dependencies for vulnerabilities.

  • Run npm audit to identify vulnerabilities.
  • Run npm audit fix regularly.
  • If vulnerabilities are found, and are not fixed by npm audit fix, update manually dependencies with known vulnerabilities.

B. Regularly update all dependencies.

  • Check for outdated packages using npm outdated or pnpm outdated.
  • Update dependencies with npm update or pnpm update. (Test upgrades on presentation project first.)

C. Identify and remove unused or outdated dependencies to improve security and performance.

  • Review package.json for unused dependencies.
  • Can also use tools like Depcheck  to identify unused packages.
  • Remove any unused dependencies.

D. Commit package-lock.json to maintain consistent versions across environments.

  • Verify that package-lock.json is committed.

2. Protect Secrets and Environment Variables

⏱️ ETA: 40 min

🎯 Goal: Avoid exposing sensitive information to the client or version control.

Follow our standards to manage environment variables

Environment Variables Security

A. Use proper environment variable management.

  • Store sensitive information only in environments/.env.local or in the github’s secret manager.

  • Never expose secrets/sensitive information with the NEXT_PUBLIC_ prefix unless required

  • Add environments/.env.local to .gitignore.

  • Search the project for hardcoded keys or credentials or secrets/sensitive data in general.

B. Validate environment variables.

  • Use zod to validate variable presence and types.
  • Provide fallback values for non-critical configs.

C. Document environment variables.

  • Maintain a list of all environment variables and their purposes.
  • Include examples in the project documentation.

3. Secure Authentication and Authorization

⏱️ ETA: 1h 30 min

🎯 Goal: Ensure only valid users access private routes and APIs.

A. Centralize authentication logic.

  • Make sure all auth logic (login, logout, session checks) is in one module/bundle.
  • Review imports — ensure only one source of auth logic is used.

B. Enforce authorization rules.

  • Make sure protected areas check user roles and restrict access accordingly.
  • Use middleware for route protection (middleware.ts or API middleware).
  • API routes validate tokens server-side on every request.
  • Ensure the token is validated on every request to protected resources.

C. Handle cookies sessions securely.

  • Use flags on cookies :
    • Secure: true
    • HttpOnly: true
    • SameSite: ‘Strict’ or ‘Lax’
  • Rotate session tokens regularly.

4. Prevent Data Exposure (Secure Server Actions & APIs)

⏱️ ETA: 2h

🎯 Goal: Keep sensitive or unnecessary data from being sent to the client.

A. Protect backend logic.

  • Sensitive logic goes in:

    • Server components (“use server”)
    • API routes (/api/…)
    • Middleware
  • Don’t embed sensitive info in client-rendered props or pages.

  • Inspect built Next.js output (.next/) to ensure sensitive files aren’t included. --- Script

B. Limit what your API returns.

  • Return only necessary fields.
  • Exclude tokens, passwords, and internal references.

C. Test for exposure.

  • Inspect the Network tab to ensure no sensitive data leaks in requests/responses.
  • Use JSON-serializable-safe data (no classes, prototypes, or functions).
    • Use JSON.stringify() on API responses to confirm no serialization errors occur.
  • Test API responses in Postman or browser to verify output format.
  • Send malformed requests manually — ensure they return proper error responses.
  • Run automated tests for invalid input handling.

5. Apply Security Headers

⏱️ ETA: 1h

🎯 Goal: Use HTTP headers to strengthen browser-level security.

A. Core headers to enable.

  • CSP (Content-Security-Policy): restricts allowed sources of content.
  • HSTS (Strict-Transport-Security): enforces HTTPS (HSTS).
  • X-Frame-Options: DENY: prevents clickjacking.
  • X-Content-Type-Options: nosniff: avoids MIME-type confusion.

B. Implementation and testing.

  • Add them via Next.js middleware.ts or next.config.js.
  • Use next-secure-headers for easier setup.
  • Open DevTools → Network tab → select any response → verify headers are applied.
  • Should not be using Iframes unless absolutely necessary. If Iframe is necessary :
    • Always use rel=“noopener noreferrer” with target=“_blank” on links that open in new tabs to prevent reverse tabnabbing.
    • Never allow user-generated content to be displayed in an iframe without proper sanitization.
    • Should not permit data transfer from tabs when redirecting to another tab.
    • Restrict to domain
    • Secure as much as possible
    • Should be very limited, sometimes only the render and nothing else. We can limit what we can do with an iframe.

6. Prevent Cross-Site Scripting (XSS)

⏱️ ETA: 50 min 🎯 Goal: Avoid rendering untrusted user input without sanitization.

A. Input handling.

  • Escape all user-provided data by default (React does this automatically).
  • Implement schema validation like Zod  + React Hook Form  for all form inputs.
  • Test with a variety of input scenarios.
  • Attempt edge cases manually or via unit tests.
  • Never insert raw HTML with dangerouslySetInnerHTML.

B. If rendering HTML is necessary.

  • Use sanitizers like DOMPurify before rendering.

7. Mitigate Cross-Site Request Forgery (CSRF)

⏱️ ETA: 50 min

🎯 Goal: Protect against unauthorized requests made on behalf of users.

Security Checklist

A. Use CSRF tokens.

  • Implement CSRF protection in authenticated requests.
  • Validate them server-side on all state-changing actions (POST, PUT, DELETE, …).
  • Verify CSRF tokens rotate.

B. Strengthen cookies.

  • Inspect cookies in DevTools → verify SameSite=Lax or Strict.
  • Add Secure and HttpOnly flags.
  • Attempt cross-origin request — cookies shouldn’t be sent.

C. Restrict allowed origins.

  • Configure allowed origins in your CORS setup.
  • Test from unauthorized origin — expect CORS or 403 error.
  • Verify trusted domains still have full access.

8. Implement Rate Limiting and Bot Protection

⏱️ ETA: 1h

**🎯 Goal:**Prevent brute-force attacks and request abuse.

A. Rate limiting.

  • Apply limits per IP or user on API routes (or middleware ?).
  • Simulate rapid requests — ensure server responds with HTTP 429 after limit.

B. Bot protection.

  • Add CAPTCHA or hCaptcha on login, signup, and password reset forms.
  • Return a 429 (Too Many Requests) response when limits are exceeded.

9. Handle Errors Safely

⏱️ ETA: 1h

🎯 Goal: Prevent information leaks through error messages.

A. Standardize error handling.

  • Wrap server code in try/catch blocks.
  • Display generic error messages to users.
  • Verify toast or error message appears and is user-friendly.
  • Trigger known errors — ensure app doesn’t crash.
  • Check logs (Sentry) to confirm errors are recorded.

B. Control exposure.

  • Review production logs for sensitive output.
  • Never return stack traces or internal info to the client.
  • Log detailed errors securely on the server or a monitoring tool.
  • Force 500 errors in production — ensure generic response (“Server error”) only.
  • Check logs are restricted to developers/admins.

10. Use Automated Security Tools

⏱️ ETA: 45 min

🎯 Goal: Identify and fix issues early in your pipeline.

A. During development.

  • Use security-focused ESLint plugins (eslint-plugin-security).
  • Run npm audit regularly.
  • Check that warnings appear for unsafe patterns.
  • Verify that no vulnerabilities are present in the codebase.
  • Verify IDE enforces linting rules on save or commit.

B. In CI/CD.

  • Integrate Snyk, Dependabot, ESLint, npm audit scripts, or GitHub Advanced Security.
  • Scan dependencies and report vulnerabilities automatically.

Code

You can copy paste this code in a file in the root of your project to keep track of the security checklist.

  • Filename : SECURITY_CHECKLIST.md
  • Header of the file :
Last verification : YYYY-MM-DD By : Name
  • Code :

Show Code (Md)

# Security checklist **⏱️ ETA:** 5h 15 min **🎯 Goal:** Enhance the security of your Next.js applications by following best practices across various areas. Ref : https://blog.arcjet.com/next-js-security-checklist/ Ref : https://nextjs.org/blog/security-nextjs-server-components-actions Ref : https://github.com/PHPCreation/phpreaction-frontend-crud-react-v2/issues/256 ## **1. Keep Dependencies Updated** **⏱️ ETA:** 40 min **🎯 Goal:** Prevent vulnerabilities, bugs, and incompatibilities by maintaining up-to-date packages. ### A. Regularly audit dependencies for vulnerabilities. - [ ] Run `npm audit` to identify vulnerabilities. - [ ] Run `npm audit fix` regularly. - [ ] If vulnerabilities are found, and are not fixed by `npm audit fix`, update manually dependencies with known vulnerabilities. ### B. Regularly update all dependencies. - [ ] Check for outdated packages using `npm outdated` or `pnpm outdated`. - [ ] Update dependencies with `npm update` or `pnpm update`. (Test upgrades on presentation project first.) ### C. Identify and remove unused or outdated dependencies to improve security and performance. - [ ] Review `package.json` for unused dependencies. - [ ] Can also use tools like [Depcheck](https://www.npmjs.com/package/depcheck) to identify unused packages. - [ ] Remove any unused dependencies. ### D. Commit `package-lock.json` to maintain consistent versions across environments. - [ ] Verify that `package-lock.json` is committed. --- ## **2. Protect Secrets and Environment Variables** **⏱️ ETA:** 40 min **🎯 Goal:** Avoid exposing sensitive information to the client or version control. Follow our standards to manage environment variables Ref : [Environment Variables Security](https://dev.react-doc.phpr.link/frontend/setup/env-setup-management) ### A. Use proper environment variable management. - [ ] Store sensitive information only in `environments/.env.local` or in the github’s secret manager. - [ ] Never expose secrets/sensitive information with the `NEXT_PUBLIC_` prefix unless required - [ ] Add `environments/.env.local` to `.gitignore`. - [ ] Search the project for hardcoded keys or credentials or secrets/sensitive data in general. ### B. Validate environment variables. - [ ] Use `zod` to validate variable presence and types. - [ ] Provide fallback values for non-critical configs. ### C. Document environment variables. - [ ] Maintain a list of all environment variables and their purposes. - [ ] Include examples in the project documentation. --- ## **3. Secure Authentication and Authorization** **⏱️ ETA:** 1h 30 min **🎯 Goal:** Ensure only valid users access private routes and APIs. ### A. Centralize authentication logic. - [ ] Make sure all auth logic (login, logout, session checks) is in one module/bundle. - [ ] Review imports — ensure only one source of auth logic is used. ### B. Enforce authorization rules. - [ ] Make sure protected areas check user roles and restrict access accordingly. - [ ] Use middleware for route protection (`middleware.ts` or API middleware). - [ ] API routes validate tokens server-side on every request. - [ ] Ensure the token is validated on every request to protected resources. ### C. Handle cookies sessions securely. - [ ] Use flags on cookies : - [ ] `Secure`: true - [ ] `HttpOnly`: true - [ ] `SameSite`: 'Strict' or 'Lax' - [ ] Rotate session tokens regularly. --- ## **4. Prevent Data Exposure (Secure Server Actions & APIs)** **⏱️ ETA:** 2h **🎯 Goal:** Keep sensitive or unnecessary data from being sent to the client. ### A. Protect backend logic. - [ ] Sensitive logic goes in: - Server components ("use server") - API routes (/api/...) - Middleware - [ ] Don’t embed sensitive info in client-rendered props or pages. - [ ] Inspect built Next.js output (`.next/`) to ensure sensitive files aren’t included. --- Script ### B. Limit what your API returns. - [ ] Return only necessary fields. - [ ] Exclude tokens, passwords, and internal references. ### C. Test for exposure. - [ ] Inspect the **Network tab** to ensure no sensitive data leaks in requests/responses. - [ ] Use JSON-serializable-safe data (no classes, prototypes, or functions). - Use `JSON.stringify()` on API responses to confirm no serialization errors occur. - [ ] Test API responses in Postman or browser to verify output format. - [ ] Send malformed requests manually — ensure they return proper error responses. - [ ] Run automated tests for invalid input handling. --- ## **5. Apply Security Headers** **⏱️ ETA:** 1h **🎯 Goal:** Use HTTP headers to strengthen browser-level security. ### A. Core headers to enable. - [ ] `CSP (Content-Security-Policy)`: restricts allowed sources of content. - [ ] `HSTS (Strict-Transport-Security)`: enforces HTTPS (HSTS). - [ ] `X-Frame-Options: DENY`: prevents clickjacking. - [ ] `X-Content-Type-Options: nosniff`: avoids MIME-type confusion. ### B. Implementation and testing. - [ ] Add them via Next.js `middleware.ts` or `next.config.js`. - Use [`next-secure-headers`](https://github.com/jagaapple/next-secure-headers) for easier setup. - [ ] Open DevTools → Network tab → select any response → verify headers are applied. - [ ] Should not be using Iframes unless absolutely necessary. If Iframe is necessary : - Always use rel="noopener noreferrer" with target="\_blank" on links that open in new tabs to prevent reverse tabnabbing. - Never allow user-generated content to be displayed in an iframe without proper sanitization. - Should not permit data transfer from tabs when redirecting to another tab. - Restrict to domain - Secure as much as possible - Should be very limited, sometimes only the render and nothing else. We can limit what we can do with an iframe. --- ## **6. Prevent Cross-Site Scripting (XSS)** **⏱️ ETA:** 50 min **🎯 Goal:** Avoid rendering untrusted user input without sanitization. ### A. Input handling. - [ ] Escape all user-provided data by default (React does this automatically). - [ ] Implement schema validation like [Zod](https://zod.dev/) + [React Hook Form](https://react-hook-form.com/) for all form inputs. - [ ] Test with a variety of input scenarios. - [ ] Attempt edge cases manually or via unit tests. - [ ] Never insert raw HTML with `dangerouslySetInnerHTML`. ### B. If rendering HTML is necessary. - [ ] Use sanitizers like `DOMPurify` before rendering. --- ## **7. Mitigate Cross-Site Request Forgery (CSRF)** **⏱️ ETA:** 50 min **🎯 Goal:** Protect against unauthorized requests made on behalf of users. Ref: [Implement CSRF protection](https://dev.react-doc.phpr.link/frontend/security/implement-CSRF-protection) ### A. Use CSRF tokens. - [ ] Implement CSRF protection in authenticated requests. - [ ] Validate them server-side on all state-changing actions (POST, PUT, DELETE, ...). - [ ] Verify CSRF tokens rotate. ### B. Strengthen cookies. - [ ] Inspect cookies in DevTools → verify `SameSite=Lax` or `Strict`. - [ ] Add `Secure` and `HttpOnly` flags. - [ ] Attempt cross-origin request — cookies shouldn’t be sent. ### C. Restrict allowed origins. - [ ] Configure allowed origins in your CORS setup. - [ ] Test from unauthorized origin — expect CORS or 403 error. - [ ] Verify trusted domains still have full access. --- ## **8. Implement Rate Limiting and Bot Protection** **⏱️ ETA:** 1h **🎯 Goal:**Prevent brute-force attacks and request abuse. ### A. Rate limiting. - [ ] Apply limits per IP or user on API routes (or middleware ?). - [ ] Simulate rapid requests — ensure server responds with HTTP 429 after limit. ### B. Bot protection. - [ ] Add CAPTCHA or hCaptcha on login, signup, and password reset forms. - [ ] Return a 429 (Too Many Requests) response when limits are exceeded. --- ## **9. Handle Errors Safely** **⏱️ ETA:** 1h **🎯 Goal:** Prevent information leaks through error messages. ### A. Standardize error handling. - [ ] Wrap server code in try/catch blocks. - [ ] Display generic error messages to users. - [ ] Verify toast or error message appears and is user-friendly. - [ ] Trigger known errors — ensure app doesn’t crash. - [ ] Check logs (Sentry) to confirm errors are recorded. ### B. Control exposure. - [ ] Review production logs for sensitive output. - [ ] Never return stack traces or internal info to the client. - [ ] Log detailed errors securely on the server or a monitoring tool. - [ ] Force 500 errors in production — ensure generic response (“Server error”) only. - [ ] Check logs are restricted to developers/admins. --- ## **10. Use Automated Security Tools** **⏱️ ETA:** 45 min **🎯 Goal:** Identify and fix issues early in your pipeline. ### A. During development. - [ ] Use security-focused ESLint plugins (`eslint-plugin-security`). - [ ] Run `npm audit` regularly. - [ ] Check that warnings appear for unsafe patterns. - [ ] Verify that no vulnerabilities are present in the codebase. - [ ] Verify IDE enforces linting rules on save or commit. ### B. In CI/CD. - [ ] Integrate **Snyk**, **Dependabot**, **ESLint**, **npm audit scripts**, or **GitHub Advanced Security**. - [ ] Scan dependencies and report vulnerabilities automatically. ---
Last updated on