Next.jsReact启动器

nextjs-security-starter — Next.js 启动器模板

作者 calliarc

基于 Next.js App Router 的安全起步模板,内置 CSP nonce、安全响应头、Auth.js 会话认证、CSRF 防护、令牌桶限流与 Zod 输入校验,附带 Vitest 测试和 GitHub Actions 流水线。

看看你的网站会是什么样

浏览真实页面截图,比较内容排版与手机效果。点击下方缩略图切换,放大查看整页细节。

3 个预览视图
桌面端首页1 / 3
桌面端截图首屏预览

由服务器浏览器实际访问页面后截取。

全部页面与视图 3

截图用于了解视觉与布局,交互效果请以实际项目为准。

采集于 2026-10-14查看来源

值得关注的模板亮点

从已有项目资料中,快速了解它的特点。

01

每个请求生成 128 位随机 nonce 的严格 CSP,script-src 使用 'strict-dynamic' 且不含 'unsafe-inline','unsafe-eval' 仅在开发环境添加

02

安全响应头集中定义:HSTS、nosniff、Referrer-Policy、Permissions-Policy、COOP/CORP、frame-ancestors,并移除 X-Powered-By

03

Auth.js v5 认证:JWT 会话 8 小时、生产 Cookie 带 __Secure- 前缀,scrypt 密码哈希,登录按账号限流,回调 URL 仅接受同站地址

04

CSRF 防护:Server Actions 依赖 Origin 校验,Route Handlers 通过 secureRoute() 执行 Origin 检查与签名双提交令牌验证

05

令牌桶限流按客户端 IP 生效,登录突发 5 次后每分钟 5 次,/api/* 突发 30 次后每秒 1 次,响应带 RateLimit-Limit 与 RateLimit-Remaining

06

Zod 输入校验与安全错误处理,未知错误仅返回 requestId 和通用消息,不向客户端暴露堆栈

模板详细介绍

这是一个 Next.js 安全起步模板,README 标注状态为 working v0.1.0,模板可构建、测试通过,所列功能均已就位。它把常见 Web 安全机制集中到可复用的代码结构中:src/proxy.ts 为每个请求生成 128 位随机 nonce 并设置严格 CSP(script-src 使用 'strict-dynamic',不含 'unsafe-inline';'unsafe-eval' 仅在开发环境添加),同时负责安全响应头、按客户端 IP 的限流和乐观鉴权重定向。安全头统一在 src/lib/security/headers.ts 定义,由 next.config.ts 应用到所有路径。认证基于 Auth.js v5,使用 JWT 会话(8 小时有效期、每小时最多续签一次),生产环境 Cookie 带 __Secure- 前缀并设置 Secure、HttpOnly、SameSite=Lax;凭据提供方用 Zod 校验输入、scrypt 哈希密码,对未知邮箱也执行哈希以降低时序差异,登录按账号限流。CSRF 方面,Server Actions 依赖 Next.js 的 Origin 校验,Route Handlers 需用 secureRoute() 包装,执行 Origin 检查与签名双提交令牌校验。限流采用令牌桶,登录接口突发 5 次后每分钟 5 次,/api/* 突发 30 次后每秒 1 次。输入校验使用 Zod,错误处理通过 handleRouteError() 区分 Zod 错误、ApiError 与未知错误,不向客户端返回堆栈。模板还提供 /.well-known/security.txt(RFC 9116,Expires 始终为 180 天后)、CI 中的 npm audit、gitleaks 与 Dependabot。

限制与注意事项:README 明确说明 nonce 仅适用于动态渲染,根布局调用 connection(),因此没有页面会被静态预渲染。默认 MemoryStore 按进程工作,部署后重置且不跨实例或 Serverless 调用共享;多实例部署需自行基于 Redis 实现 RateLimitStore 接口,并用 Lua 脚本或 Upstash 的 @upstash/ratelimit 保持 consume 原子性。客户端 IP 取自 X-Real-IP / X-Forwarded-For,仅在会覆盖这些头的代理(如 Vercel、Cloudflare 或正确配置的 nginx / 负载均衡)之后可信。演示用户从环境变量读取,README 建议将 findUser() 替换为数据库查询,并考虑改用 argon2id 或 bcrypt。路线图中 Redis / Upstash 适配器、CSP 违规上报端点和 Playwright 端到端测试尚未完成。README 未提供性能数据、生产环境部署清单或托管平台的具体配置说明。

Next.js启动器

适合你的项目吗?

适用场景

  • 需要为 Next.js App Router 项目快速搭建 CSP nonce、安全响应头与 CSRF 防护基础结构
  • 希望参考 Auth.js v5 的凭据登录、会话 Cookie 配置与受保护路由实现
  • 需要为登录和 API 端点添加按 IP 的令牌桶限流示例
  • 想了解在 CI 中集成 npm audit、gitleaks 与 Dependabot 的配置方式

适合人群

  • 使用 Next.js App Router 与 TypeScript 的开发者
  • 需要可运行的安全机制参考实现而非生产成品的团队
  • 准备自行替换演示用户存储并接入数据库的项目
  • 关注 CSP、CSRF、限流与安全响应头配置细节的工程人员

上手与定制指南

结合仓库当前版本的说明,按下面的资料检查内容、配置与运行要求。

  1. 1

    需要 Node.js 20.9 或更高版本(CI 使用 Node 22)与 npm;克隆仓库后执行 npm ci,并复制 .env.example 为 .env.local

  2. 2

    使用 npx auth secret 生成 AUTH_SECRET,使用 npm run hash-password 生成 scrypt 哈希填入 DEMO_USER_PASSWORD_HASH

  3. 3

    运行 npm run dev 后访问 /login,用 DEMO_USER_EMAIL 和已哈希的密码登录;/dashboard 展示 Server Action 表单与使用 CSRF 令牌的 Route Handler 表单

  4. 4

    启用 GitHub 登录需创建 OAuth 应用,回调地址为 http://localhost:3000/api/auth/callback/github,并设置 AUTH_GITHUB_ID 与 AUTH_GITHUB_SECRET

  5. 5

    多实例部署时默认 MemoryStore 不共享,需按 README 说明基于 Redis 实现 RateLimitStore 接口

查看仓库使用说明

授权与来源资料

使用条件与限制

免费使用 · 保留许可声明

本次收录的是公开仓库中采用 MIT 许可的免费版本;免费范围为该版本代码,不代表外部服务或第三方素材免费。

使用时需要遵守

  • 允许免费使用、修改、商用和分发该版本代码。
  • 分发代码副本或实质性部分时,保留原作者版权声明和完整 MIT 许可声明。
  • 代码按现状提供;担保与责任限制以原许可证及适用法律为准。

额外费用与待核对事项

  • 演示图片、字体、图标及其他第三方素材需按各自许可核对,不能仅凭代码的 MIT 许可判断。
  • 域名、托管、数据库、支付或 AI 等外部服务可能另外收费。
  • Nonce 仅适用于动态渲染,根布局调用 connection(),因此没有任何页面会被静态预渲染。
  • 默认 MemoryStore 按进程工作,部署后重置且不跨实例或 Serverless 调用共享;多实例部署需自行基于 Redis 实现 RateLimitStore 接口。
  • 客户端 IP 取自 X-Real-IP / X-Forwarded-For,仅在会覆盖这些头的代理(如 Vercel、Cloudflare 或正确配置的 nginx / 负载均衡)之后可信。
  • 演示用户从环境变量读取,需将 findUser() 替换为数据库查询,并考虑改用 argon2id 或 bcrypt。
  • 启用 GitHub 登录需自行创建 OAuth 应用并设置 AUTH_GITHUB_ID / AUTH_GITHUB_SECRET。
  • Redis / Upstash RateLimitStore 适配器尚未实现,仍处于路线图待办状态。
  • CSP 违规上报端点(report-to)尚未实现,仍处于路线图待办状态。
  • 针对认证与 CSRF 流程的 Playwright 端到端测试尚未实现,仍处于路线图待办状态。
查看原文依据

Permission is hereby granted, free of charge

许可证原文

- Nonces only work with dynamic rendering, so the root layout calls `connection()`. This means no page is statically prerendered.

README 原文

**Rate limiting in production:** the default `MemoryStore` works per process. It resets on deploy and is not shared across instances or serverless invocations. For multi-instance deployments, implement the `RateLimitStore` interface on top of Redis. Use a Lua script (or Upstash's `@upstash/ratelimit`) so that consume stays atomic, then pass that store to the `RateLimiter` instances.

README 原文

Client IPs come from `X-Real-IP` / `X-Forwarded-For`. These headers can only be trusted behind a proxy that overwrites them, such as Vercel, Cloudflare, or a correctly configured nginx or load balancer.

README 原文

The demo user is read from env vars. Replace `findUser()` with a database lookup, and consider argon2id or bcrypt through your user store.

README 原文

To enable GitHub sign-in, create an OAuth app with the callback `http://localhost:3000/api/auth/callback/github` and set `AUTH_GITHUB_ID` / `AUTH_GITHUB_SECRET`.

README 原文

- [ ] Redis / Upstash `RateLimitStore` adapter

README 原文

- [ ] CSP violation reporting endpoint (`report-to`)

README 原文

- [ ] End-to-end tests (Playwright) for auth and CSRF flows

README 原文
https://github.com/calliarc/nextjs-security-starter/blob/main/LICENSEhttps://github.com/calliarc/nextjs-security-starter/blob/main/README.md

DeepSeek 根据仓库许可证和 README 整理 ·2026-10-14。结果仅覆盖已读取资料,依赖、图片、字体及后续条款变更以各自原文为准。

项目核对记录与介绍依据

模板资料

来源平台
GitHub
资料与维护参考分
资料较完整 · 80 / 100
技术栈
Next.js · React
网站类型
启动器
这个分数如何计算

本站按许可资料(最高30分)、演示链接(12分)、预览图(10分)、Star数(15分)、记录的维护时间(15分)、描述完整度(8分)、功能记录(5分)和分类资料(5分)计算。它不是代码、安全或性能测试,也不是 Google 评分;有链接不代表已实测可用。

这个项目需要核对什么

记录的仓库更新时间
2026-10-14
截图检查日期
2026-10-14

已收录演示链接。请用你的内容与屏幕尺寸检查;有演示不代表表单、认证或支付已经可用。

目录记录的主要技术栈是 Next.js。请通过依赖清单和锁文件核对实际版本与托管要求。这些说明基于收录资料,不代表代码审计或部署实测。

查看具体选型方法 →

介绍的制作与核对状态

介绍由 AI 辅助整理目录与仓库资料。仓库作者不等于这篇介绍的审核者。

审核声明: codex-user-requested-batch-review-20261015 · 2026-10-15

查看当时使用的来源资料

SHA-256: 00d6bef6c891102781b7dd0866b6d4819a8bb93d434b4bab63286dcbfe32e26a

2026-10-14T23:37:38.851Z

来源文件 / 仓库
# Next.js Security Starter

Next.js template with secure headers, rate limiting, CSRF protection and auth set up correctly.

[![CI](https://github.com/calliarc/nextjs-security-starter/actions/workflows/ci.yml/badge.svg)](https://github.com/calliarc/nextjs-security-starter/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Release](https://img.shields.io/github/v/release/calliarc/nextjs-security-starter?include_prereleases&sort=semver)](https://github.com/calliarc/nextjs-security-starter/releases)
[![Built by CalliArc](https://img.shields.io/badge/built%20by-CalliArc-0a66c2)](https://www.calliarc.com/)

> **Status:** working v0.1.0. The template builds, the tests pass, and every feature below is in place. Feedback and issues are welcome.

## Features

- Strict Content-Security-Policy with a fresh nonce per request (`'strict-dynamic'`, no `'unsafe-inline'`)
- Security headers configured in one place (HSTS, nosniff, Referrer-Policy, Permissions-Policy, COOP/CORP, frame-ancestors)
- Authentication with secure session cookies (Auth.js v5, credentials provider plus optional GitHub OAuth, protected `/dashboard`)
- CSRF protection for server actions and API routes (Origin check plus a signed double-submit token for Route Handlers)
- Rate limiting for login and API endpoints (token bucket with a pluggable store)
- Input validation with Zod and safe error handling (no stack traces reach clients)
- `/.well-known/security.txt` (RFC 9116)
- Dependency and secret scanning in CI (`npm audit`, gitleaks, Dependabot)

## Tech stack

- Next.js 16 (App Router, `proxy.ts`)
- TypeScript (strict)
- Auth.js (`next-auth` v5)
- Zod 4
- Proxy-based (formerly "middleware") rate limiting
- Vitest, ESLint, GitHub Actions

## Getting started

Requirements: Node.js 20.9 or later (CI uses Node 22) and npm.

```bash
git clone https://github.com/calliarc/nextjs-security-starter.git
cd nextjs-security-starter
npm ci
cp .env.example .env.local
```

Fill in `.env.local`:

```bash
# 1. Session signing secret
npx auth secret            # or: openssl rand -base64 32  -> AUTH_SECRET=

# 2. Demo user password hash (the prompt hides your input)
npm run hash-password      # paste the output into DEMO_USER_PASSWORD_HASH=
```

Then run the app:

```bash
npm run dev                # http://localhost:3000
```

Sign in at `/login` with `DEMO_USER_EMAIL` and the password you hashed. The dashboard shows a Server Action form and a Route Handler form that uses a CSRF token.

To enable GitHub sign-in, create an OAuth app with the callback `http://localhost:3000/api/auth/callback/github` and set `AUTH_GITHUB_ID` / `AUTH_GITHUB_SECRET`.

### Scripts

| Command | What it does |
| --- | --- |
| `npm run dev` | Development server |
| `npm run build` / `npm start` | Production build and server |
| `npm run lint` | ESLint (`eslint-config-next` core-web-vitals + TypeScript) |
| `npm run typecheck` | `tsc --noEmit` |
| `npm test` | Vitest unit tests for the CSP, rate-limit, CSRF and validation helpers |
| `npm run hash-password` | Prints an scrypt hash for `DEMO_USER_PASSWORD_HASH` |

### Project layout

```
src/
  proxy.ts                      # nonce + CSP, security headers, rate limiting, optimistic auth redirect
  auth.ts                       # Auth.js config (providers, cookies, session, redirect guard)
  lib/security/
    csp.ts                      # nonce generation and CSP builder
    headers.ts                  # every other security header, in one place
    csrf.ts                     # Origin check and signed double-submit tokens
    rate-limit.ts               # token bucket, RateLimitStore interface, MemoryStore
    password.ts                 # scrypt hashing
    client-ip.ts                # client IP used as the rate-limit key
  lib/http/                     # safe error responses, secureRoute() wrapper
  lib/validation/               # Zod schemas and a size-limited JSON body parser
  app/
    .well-known/security.txt/   # RFC 9116 security.txt
    api/csrf, api/messages      # CSRF token endpoint and an example protected endpoint
    login, dashboard            # sign-in page and protected page with Server Actions
tests/                          # Vitest unit tests
```

## Security features explained

### Content-Security-Policy with nonces

`src/proxy.ts` creates a 128-bit random nonce for every request. It sets the CSP on the response, and on the request as well, which is how Next.js finds the nonce and adds it to its own scripts and styles. The policy looks like this:

```
default-src 'self'; script-src 'self' 'nonce-…' 'strict-dynamic'; style-src 'self' 'nonce-…';
object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests; …
```

- `'unsafe-eval'` is added **only** in development, because React's dev tooling needs it.
- Nonces only work with dynamic rendering, so the root layout calls `connection()`. This means no page is statically prerendered.
- To allow extra origins, use `cspDirectives({ extend: { "img-src": ["https://…"] } })`. The GitHub provider adds `https://github.com` to `form-action` automatically.
- API responses get `default-src 'none'; frame-ancestors 'none'`.

### Security headers in one place

All other headers are defined in `src/lib/security/headers.ts`. `next.config.ts` applies them to every path, and the proxy also applies them to the redirects and 429 responses it creates itself:

| Header | Value |
| --- | --- |
| `Strict-Transport-Security` | `max-age=63072000; includeSubDomains` (production only; `preload` is opt-in) |
| `X-Content-Type-Options` | `nosniff` |
| `Referrer-Policy` | `strict-origin-when-cross-origin` |
| `Permissions-Policy` | camera, microphone, geolocation, payment, usb, topics and others disabled |
| `X-Frame-Options` / CSP `frame-ancestors` | `DENY` / `'none'` |
| `Cross-Origin-Opener-Policy` / `-Resource-Policy` | `same-origin` |
| `X-Powered-By` | removed (`poweredByHeader: false`) |

### Authentication and session cookies

`src/auth.ts` configures Auth.js v5:

- JWT sessions last 8 hours and are re-issued at most once an hour.
- In production the cookies use the `__Secure-` prefix and are `Secure`, `HttpOnly` and `SameSite=Lax`.
- The credentials provider validates input with Zod and hashes passwords with scrypt. It runs the hash even for unknown emails, so response time does not reveal whether an account exists, and it returns the same error for a wrong email or a wrong password.
- The login is rate limited per account.
- The `redirect` callback and the login action only accept same-site callback URLs, which blocks open redirects.
- `/dashboard` is protected twice. The proxy does a quick cookie-presence redirect, and the page itself calls `auth()`. Server Actions and Route Handlers also call `auth()` themselves, because a proxy matcher is not a security boundary.

The demo user is read from env vars. Replace `findUser()` with a database lookup, and consider argon2id or bcrypt through your user store.

### CSRF protection

- **Server Actions:** Next.js only accepts `POST` for them and rejects any call whose `Origin` host differs from `Host` / `X-Forwarded-Host`. You don't need a token. If a reverse proxy rewrites the host, list the public host in `experimental.serverActions.allowedOrigins`. Note that a request with **no** `Origin` header is allowed through with a warning, so SameSite cookies are still your second layer.
- **Route Handlers:** wrap them in `secureRoute()` (`src/lib/http/secure-route.ts`). For unsafe methods it runs two checks:
  1. **Origin check.** `Origin` (falling back to `Referer`) must match the app's own origin or an allow-list. `Sec-Fetch-Site: cross-site` is always rejected. `X-Forwarded-*` headers are ignored unless `TRUST_PROXY_HEADERS=true`.
  2. **Signed double-submit token.** `GET /api/csrf` returns `<random>.<HMAC>`. It also sets the token in an `HttpOnly`, `SameSite=Strict` cookie, which is named `__Host-csrf` in production. The client sends the token back in `x-csrf-token`. The cookie and header must match, and the HMAC must verify against `CSRF_SECRET` (or `AUTH_SECRET` if `CSRF_SECRET` is not set).

  If either check fails, the response is a generic `403`.

### Rate limiting

`src/lib/security/rate-limit.ts` implements a token bucket. Each key has a bucket of `capacity` tokens that refills at `refillPerSecond`. `src/proxy.ts` applies the limits per client IP:

- **Login** (`POST /login`, `/api/auth/callback/*`, `/api/auth/signin/*`): a burst of 5, then 5 per minute. `authorize()` applies the same limit per email address.
- **API** (`/api/*`): a burst of 30, then 1 request per second.

Responses include `RateLimit-Limit` and `RateLimit-Remaining`. Blocked requests get a `429` with `Retry-After`.

**Rate limiting in production:** the default `MemoryStore` works per process. It resets on deploy and is not shared across instances or serverless invocations. For multi-instance deployments, implement the `RateLimitStore` interface on top of Redis. Use a Lua script (or Upstash's `@upstash/ratelimit`) so that consume stays atomic, then pass that store to the `RateLimiter` instances.

Client IPs come from `X-Real-IP` / `X-Forwarded-For`. These headers can only be trusted behind a proxy that overwrites them, such as Vercel, Cloudflare, or a correctly configured nginx or load balancer.

### Input validation and safe errors

- Zod schemas live in `src/lib/validation/schemas.ts`. `parseJsonBody()` enforces `Content-Type: application/json`, a byte limit and valid JSON before it validates.
- `handleRouteError()` handles three cases:
  - Zod errors: returns `400` with a list of field paths and messages.
  - `ApiError`: returns its own status and a public message.
  - Anything else: logs the full error server-side under a request id and returns `{ "error": { "code": "internal_error", "message": "Something went wrong.", "requestId": "…" } }`. Stack traces are never sent to clients.
- `error.tsx` / `global-error.tsx` show only the opaque `digest`. Server Action bodies are capped at 100 KB.

### security.txt

`/.well-known/security.txt` is served by a route handler. Its `Expires` field is always 180 days ahead. Set `SECURITY_CONTACT`, `SECURITY_POLICY_URL` and `APP_URL` to point it at your own disclosure process.

### CI and supply chain

`.github/workflows/ci.yml` runs these jobs on every push and pull request:

- lint, typecheck, test and build
- `npm audit --audit-level=high`
- a full-history gitleaks secret scan

Workflow permissions are read-only and checkout credentials are not persisted. Dependabot (`.github/dependabot.yml`) opens weekly updates for npm packages and GitHub Actions.

## Roadmap

- [x] Initial release
- [x] Documentation and examples
- [x] CI and automated tests
- [ ] Redis / Upstash `RateLimitStore` adapter
- [ ] CSP violation reporting endpoint (`report-to`)
- [ ] End-to-end tests (Playwright) for auth and CSRF flows

Have an idea? [Open an issue](https://github.com/calliarc/nextjs-security-starter/issues).

## Contributing

Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).

## Changelog

See [CHANGELOG.md](CHANGELOG.md).

## License

[MIT](LICENSE) © 2026 CalliArc

---

Built and maintained by [CalliArc](https://www.calliarc.com/). Need help with secure application development? [Talk to our team](https://www.calliarc.com/services/managed-cyber-security/).
反馈内容错误

看看同类模板

对比同技术栈与用途的模板,找到更适合的视觉与内容结构。