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 :
- https://blog.arcjet.com/next-js-security-checklist/
- https://nextjs.org/blog/security-nextjs-server-components-actions
- https://github.com/PHPCreation/phpreaction-frontend-crud-react-v2/issues/256
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 auditto identify vulnerabilities. - Run
npm audit fixregularly. - 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 outdatedorpnpm outdated. - Update dependencies with
npm updateorpnpm update. (Test upgrades on presentation project first.)
C. Identify and remove unused or outdated dependencies to improve security and performance.
- Review
package.jsonfor 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.jsonis 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 SecurityA. Use proper environment variable management.
-
Store sensitive information only in
environments/.env.localor in the github’s secret manager. -
Never expose secrets/sensitive information with the
NEXT_PUBLIC_prefix unless required -
Add
environments/.env.localto.gitignore. -
Search the project for hardcoded keys or credentials or secrets/sensitive data in general.
B. Validate environment variables.
- Use
zodto 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.tsor 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.
- Use
- 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.tsornext.config.js. - Use
next-secure-headersfor 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
DOMPurifybefore rendering.
7. Mitigate Cross-Site Request Forgery (CSRF)
⏱️ ETA: 50 min
🎯 Goal: Protect against unauthorized requests made on behalf of users.
Security ChecklistA. 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=LaxorStrict. - Add
SecureandHttpOnlyflags. - 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 auditregularly. - 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.
---