security-headers

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Security Headers - Defense Against Multiple Attack Types

安全头——抵御多种攻击类型

Why Security Headers Are Critical

为什么安全头至关重要

Think of security headers as the walls and moat around your castle. Even if attackers get past the gate (your authentication), the walls (headers) prevent them from moving freely or exfiltrating data.
你可以把安全头看作是城堡周围的城墙和护城河。即便攻击者突破了城门(你的身份验证体系),城墙(安全头)也能阻止他们肆意行动或窃取数据。

The Browser Security Model

浏览器安全模型

Modern browsers have built-in security features, but they're opt-in. Without the right headers, browsers allow:
  • Your site to be embedded in malicious iframes (clickjacking)
  • Scripts from any origin (XSS amplification)
  • Insecure HTTP connections (man-in-the-middle attacks)
  • MIME type confusion (executing images as scripts)
Security headers tell the browser: "Enable all your security features for my site."
现代浏览器内置了安全特性,但这些特性是需要主动启用的。如果没有配置正确的请求头,浏览器会允许以下行为:
  • 你的站点被嵌入到恶意iframe中(点击劫持)
  • 任意来源的脚本加载运行(放大XSS攻击危害)
  • 不安全的HTTP连接(中间人攻击)
  • MIME类型混淆(将图片当作脚本执行)
安全头会告知浏览器:「为我的站点启用你所有的安全特性。」

Real-World Consequences of Missing Headers

缺失安全头的实际后果

According to a 2023 security audit of top 10,000 websites by Scott Helme, only 2.8% properly implement all recommended security headers. The remaining 97.2% are vulnerable to attacks that headers would prevent.
Magecart Attacks (2018-2020): Hundreds of e-commerce sites were compromised by injected payment-stealing JavaScript. Content-Security-Policy headers would have prevented these scripts from executing. Sites without CSP lost millions in fraudulent transactions.
根据Scott Helme在2023年对Top 10,000网站的安全审计结果,仅有2.8%的网站正确配置了所有推荐的安全头。剩余97.2%的网站都面临可被安全头拦截的攻击风险。
Magecart攻击(2018-2020年): 数百家电商网站被注入窃取支付信息的JavaScript脚本攻破。Content-Security-Policy头本可以阻止这些脚本执行,没有配置CSP的站点因欺诈交易损失了数百万美元。

Our Security Headers Architecture

我们的安全头架构

All headers are applied automatically via
middleware.ts
on every request. You don't need to manually set them—they're already protecting you.
所有安全头都会通过
middleware.ts
自动应用到每一个请求上。你不需要手动设置,它们已经在为你提供防护。

Headers We Apply

我们配置的安全头

  1. Content-Security-Policy (CSP) - Controls resource loading
  2. X-Frame-Options: DENY - Prevents clickjacking
  3. X-Content-Type-Options: nosniff - Prevents MIME confusion
  4. Strict-Transport-Security (HSTS) - Forces HTTPS (production only)
  5. X-Robots-Tag: noindex, nofollow - Protects private routes
  1. Content-Security-Policy (CSP) - 控制资源加载规则
  2. X-Frame-Options: DENY - 阻止点击劫持
  3. X-Content-Type-Options: nosniff - 阻止MIME混淆
  4. Strict-Transport-Security (HSTS) - 强制使用HTTPS(仅生产环境生效)
  5. X-Robots-Tag: noindex, nofollow - 保护私有路由

Header Descriptions & Implementation

安全头说明与实现

1. Content-Security-Policy (CSP)

1. Content-Security-Policy (CSP)

What it does: Controls what resources (scripts, styles, images) can load and from where.
Our configuration:
typescript
// Dynamic CSP based on environment variables
const clerkDomain = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL
  ? new URL(process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL).origin
  : '';

const convexDomain = process.env.NEXT_PUBLIC_CONVEX_URL
  ? new URL(process.env.NEXT_PUBLIC_CONVEX_URL).origin
  : '';

const csp = [
  "default-src 'self'",
  `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${clerkDomain} https://js.stripe.com`,
  `style-src 'self' 'unsafe-inline' ${clerkDomain}`,
  `connect-src 'self' ${clerkDomain} ${convexDomain} https://api.stripe.com`,
  `frame-src 'self' ${clerkDomain} https://js.stripe.com https://hooks.stripe.com`,
  "img-src 'self' data: https: blob:",
  "font-src 'self' data:",
  "object-src 'none'",
  "base-uri 'self'",
  "form-action 'self'",
  "frame-ancestors 'none'"
].join('; ');

response.headers.set('Content-Security-Policy', csp);
What This Means:
  • Scripts: Only from our domain, Clerk, and Stripe
  • Styles: Only from our domain and Clerk
  • Connections: Only to Clerk, Convex, Stripe APIs
  • Frames: Only Clerk and Stripe
  • Images: Any HTTPS source (for user avatars, external images)
Why Dynamic Configuration:
typescript
const clerkDomain = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL
  ? new URL(process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL).origin
  : ''
We don't hardcode Clerk's domain. It comes from environment variables. This means:
  • ✅ Different domains for dev/staging/prod (automatic)
  • ✅ Easy to change without code modifications
  • ✅ No security secrets in codebase
Trade-off: We allow
unsafe-inline
and
unsafe-eval
for scripts. This is required for Next.js and Clerk to function. We mitigate this risk through input sanitization and validation.
Prevent Data Exfiltration: The
connect-src
directive prevents malicious scripts from sending data to unauthorized domains. Even if XSS bypasses sanitization, the browser blocks unauthorized network requests.
作用: 控制可以加载的资源类型(脚本、样式、图片等)以及允许的来源。
我们的配置:
typescript
// Dynamic CSP based on environment variables
const clerkDomain = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL
  ? new URL(process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL).origin
  : '';

const convexDomain = process.env.NEXT_PUBLIC_CONVEX_URL
  ? new URL(process.env.NEXT_PUBLIC_CONVEX_URL).origin
  : '';

const csp = [
  "default-src 'self'",
  `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${clerkDomain} https://js.stripe.com`,
  `style-src 'self' 'unsafe-inline' ${clerkDomain}`,
  `connect-src 'self' ${clerkDomain} ${convexDomain} https://api.stripe.com`,
  `frame-src 'self' ${clerkDomain} https://js.stripe.com https://hooks.stripe.com`,
  "img-src 'self' data: https: blob:",
  "font-src 'self' data:",
  "object-src 'none'",
  "base-uri 'self'",
  "form-action 'self'",
  "frame-ancestors 'none'"
].join('; ');

response.headers.set('Content-Security-Policy', csp);
配置含义:
  • 脚本: 仅允许来自本站域名、Clerk和Stripe的脚本加载
  • 样式: 仅允许来自本站域名和Clerk的样式加载
  • 网络请求: 仅允许向Clerk、Convex、Stripe API发起请求
  • 框架: 仅允许加载Clerk和Stripe的iframe
  • 图片: 允许任意HTTPS来源的图片(用于用户头像、外部图片等场景)
为什么用动态配置:
typescript
const clerkDomain = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL
  ? new URL(process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL).origin
  : ''
我们没有硬编码Clerk的域名,而是从环境变量中读取,这意味着:
  • ✅ 开发/预发/生产环境自动适配不同域名
  • ✅ 无需修改代码即可轻松变更域名
  • ✅ 代码库中不会留存安全敏感信息
权衡: 我们允许脚本使用
unsafe-inline
unsafe-eval
,这是Next.js和Clerk正常运行的必要配置。我们通过输入 sanitization 和校验来降低该配置带来的风险。
阻止数据窃取:
connect-src
指令可以阻止恶意脚本向未授权的域名发送数据。即便XSS绕过了输入校验,浏览器也会拦截未授权的网络请求。

2. X-Frame-Options: DENY

2. X-Frame-Options: DENY

What it prevents: Clickjacking attacks where your site is embedded in invisible iframe on attacker's site.
Attack scenario: Attacker embeds your "Delete Account" button in an invisible iframe overlay on a game site. Users think they're clicking "Play Game" but actually click "Delete Account."
Our protection:
typescript
response.headers.set('X-Frame-Options', 'DENY');
DENY means browsers refuse to embed our site in ANY iframe, even our own. Maximum security.
Alternative values:
  • DENY
    - No iframes at all (most secure, what we use)
  • SAMEORIGIN
    - Only our own site can iframe us
  • ALLOW-FROM uri
    - Deprecated, don't use
防护目标: 点击劫持攻击,这类攻击会将你的站点嵌入到攻击者站点的不可见iframe中。
攻击场景: 攻击者将你的「删除账户」按钮嵌入到游戏站点的不可见iframe浮层中,用户以为自己点击的是「开始游戏」,实际点击的是「删除账户」。
我们的防护方案:
typescript
response.headers.set('X-Frame-Options', 'DENY');
DENY表示浏览器会拒绝将我们的站点嵌入到任何iframe中,即便是我们自己的站点也不行,实现最高级别的安全防护。
可选值说明:
  • DENY
    - 完全禁止iframe嵌入(最安全,我们使用的配置)
  • SAMEORIGIN
    - 仅允许本站点iframe嵌入
  • ALLOW-FROM uri
    - 已废弃,不要使用

3. X-Content-Type-Options: nosniff

3. X-Content-Type-Options: nosniff

What it prevents: MIME confusion attacks where browsers execute images as JavaScript.
Attack scenario: Attacker uploads file "avatar.jpg" that contains JavaScript. Old browsers try to be "helpful" and "sniff" the file type, detecting JavaScript, and execute it.
Our protection:
typescript
response.headers.set('X-Content-Type-Options', 'nosniff');
nosniff
tells browsers to strictly follow Content-Type headers, never guess.
Why This Matters: Without this header, an attacker could:
  1. Upload "image.jpg" containing
    <script>evil()</script>
  2. Link to it:
    <img src="/uploads/image.jpg">
  3. Browser sniffs file, detects script, executes it
  4. XSS achieved despite upload validation
With
nosniff
, browser sees Content-Type: image/jpeg and refuses to execute as script.
防护目标: MIME混淆攻击,这类攻击会让浏览器将图片当作JavaScript执行。
攻击场景: 攻击者上传包含JavaScript代码的文件「avatar.jpg」,旧版本浏览器会自作主张地「嗅探」文件类型,识别出JavaScript代码并执行。
我们的防护方案:
typescript
response.headers.set('X-Content-Type-Options', 'nosniff');
nosniff
告知浏览器严格遵循Content-Type头的定义,绝对不要自行猜测文件类型
重要性: 如果没有这个头,攻击者可以:
  1. 上传包含
    <script>evil()</script>
    的「image.jpg」文件
  2. 插入引用链接:
    <img src="/uploads/image.jpg">
  3. 浏览器嗅探文件识别出脚本并执行
  4. 即便上传校验通过也会出现XSS漏洞
配置
nosniff
后,浏览器识别到Content-Type为image/jpeg时,会拒绝将其作为脚本执行

4. Strict-Transport-Security (HSTS) - Production Only

4. Strict-Transport-Security (HSTS) - 仅生产环境生效

What it prevents: SSL stripping attacks where man-in-the-middle downgrades HTTPS to HTTP.
Attack scenario: User types "yourapp.com" (no https://). Browser initially requests HTTP. Attacker intercepts, serves fake HTTP version, steals credentials.
Our protection:
typescript
if (process.env.NODE_ENV === 'production') {
  response.headers.set(
    'Strict-Transport-Security',
    'max-age=31536000; includeSubDomains'
  );
}
Configuration breakdown:
  • max-age=31536000
    - 1 year duration
  • includeSubDomains
    - Applies to all subdomains
  • Once set, browsers ONLY use HTTPS for your domain
Why production only: In development, you're on localhost (HTTP). HSTS would break local development. Our middleware detects environment and enables HSTS only in production.
Important: Once HSTS is set for a domain, browsers remember it. If you need to remove it, you must:
  1. Set
    max-age=0
  2. Wait for all users' browsers to receive the new header
  3. This can take months if max-age was long
防护目标: SSL剥离攻击,这类中间人攻击会将HTTPS连接降级为HTTP连接。
攻击场景: 用户输入「yourapp.com」(不带https://),浏览器首先发起HTTP请求,攻击者拦截请求后返回伪造的HTTP版本站点,窃取用户凭证。
我们的防护方案:
typescript
if (process.env.NODE_ENV === 'production') {
  response.headers.set(
    'Strict-Transport-Security',
    'max-age=31536000; includeSubDomains'
  );
}
配置解析:
  • max-age=31536000
    - 有效期为1年
  • includeSubDomains
    - 规则对所有子域名生效
  • 配置生效后,浏览器只会通过HTTPS访问你的域名
为什么仅生产环境生效: 开发环境你使用的是localhost(HTTP协议),HSTS会破坏本地开发流程。我们的中间件会自动识别环境,仅在生产环境启用HSTS。
注意: 一旦为某个域名设置了HSTS,浏览器会记住该配置。如果你需要移除HSTS,必须:
  1. 设置
    max-age=0
  2. 等待所有用户的浏览器接收到新的头配置
  3. 如果之前的max-age设置得很长,这个过程可能需要数月

5. X-Robots-Tag: noindex, nofollow (Protected Routes)

5. X-Robots-Tag: noindex, nofollow(受保护路由)

What it prevents: Search engines indexing private content.
Why it matters: You don't want
/dashboard/payment-details
showing up in Google search results.
Our implementation:
typescript
if (req.nextUrl.pathname.startsWith('/dashboard')) {
  response.headers.set('X-Robots-Tag', 'noindex, nofollow');
}
Applied to:
/dashboard/*
routes only (public pages should be indexed)
What this tells search engines:
  • noindex
    - Don't add this page to search results
  • nofollow
    - Don't follow links on this page
防护目标: 搜索引擎索引私有内容。
重要性: 你肯定不希望
/dashboard/payment-details
这类页面出现在谷歌搜索结果中。
我们的实现:
typescript
if (req.nextUrl.pathname.startsWith('/dashboard')) {
  response.headers.set('X-Robots-Tag', 'noindex, nofollow');
}
生效范围:
/dashboard/*
路由(公开页面应该被搜索引擎索引)
告知搜索引擎的规则:
  • noindex
    - 不要将该页面加入搜索结果
  • nofollow
    - 不要爬取该页面上的链接

Implementation in Middleware

中间件实现

middleware.ts Pattern

middleware.ts 模板

typescript
import { clerkMiddleware } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';

export default clerkMiddleware((auth, req) => {
  const response = NextResponse.next();

  // Get dynamic domains from environment
  const clerkDomain = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL
    ? new URL(process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL).origin
    : '';

  const convexDomain = process.env.NEXT_PUBLIC_CONVEX_URL
    ? new URL(process.env.NEXT_PUBLIC_CONVEX_URL).origin
    : '';

  // Build CSP dynamically
  const csp = [
    "default-src 'self'",
    `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${clerkDomain} https://js.stripe.com`,
    `style-src 'self' 'unsafe-inline' ${clerkDomain}`,
    `connect-src 'self' ${clerkDomain} ${convexDomain} https://api.stripe.com`,
    `frame-src 'self' ${clerkDomain} https://js.stripe.com https://hooks.stripe.com`,
    "img-src 'self' data: https: blob:",
    "font-src 'self' data:",
    "object-src 'none'",
    "base-uri 'self'",
    "form-action 'self'",
    "frame-ancestors 'none'"
  ].join('; ');

  // Apply security headers
  response.headers.set('Content-Security-Policy', csp);
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('X-Frame-Options', 'DENY');

  // HSTS only in production
  if (process.env.NODE_ENV === 'production') {
    response.headers.set(
      'Strict-Transport-Security',
      'max-age=31536000; includeSubDomains'
    );
  }

  // Prevent indexing of protected routes
  if (req.nextUrl.pathname.startsWith('/dashboard')) {
    response.headers.set('X-Robots-Tag', 'noindex, nofollow');
  }

  return response;
});

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
};
typescript
import { clerkMiddleware } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';

export default clerkMiddleware((auth, req) => {
  const response = NextResponse.next();

  // Get dynamic domains from environment
  const clerkDomain = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL
    ? new URL(process.env.NEXT_PUBLIC_CLERK_FRONTEND_API_URL).origin
    : '';

  const convexDomain = process.env.NEXT_PUBLIC_CONVEX_URL
    ? new URL(process.env.NEXT_PUBLIC_CONVEX_URL).origin
    : '';

  // Build CSP dynamically
  const csp = [
    "default-src 'self'",
    `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${clerkDomain} https://js.stripe.com`,
    `style-src 'self' 'unsafe-inline' ${clerkDomain}`,
    `connect-src 'self' ${clerkDomain} ${convexDomain} https://api.stripe.com`,
    `frame-src 'self' ${clerkDomain} https://js.stripe.com https://hooks.stripe.com`,
    "img-src 'self' data: https: blob:",
    "font-src 'self' data:",
    "object-src 'none'",
    "base-uri 'self'",
    "form-action 'self'",
    "frame-ancestors 'none'"
  ].join('; ');

  // Apply security headers
  response.headers.set('Content-Security-Policy', csp);
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('X-Frame-Options', 'DENY');

  // HSTS only in production
  if (process.env.NODE_ENV === 'production') {
    response.headers.set(
      'Strict-Transport-Security',
      'max-age=31536000; includeSubDomains'
    );
  }

  // Prevent indexing of protected routes
  if (req.nextUrl.pathname.startsWith('/dashboard')) {
    response.headers.set('X-Robots-Tag', 'noindex, nofollow');
  }

  return response;
});

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
};

Testing Security Headers

安全头测试

Test All Headers

测试所有安全头

bash
curl -I http://localhost:3000
bash
curl -I http://localhost:3000

Expected headers:

Expected headers:

X-Frame-Options: DENY

X-Frame-Options: DENY

X-Content-Type-Options: nosniff

X-Content-Type-Options: nosniff

Content-Security-Policy: default-src 'self'; ...

Content-Security-Policy: default-src 'self'; ...

undefined
undefined

Test Production HSTS

测试生产环境HSTS

bash
undefined
bash
undefined

In production

In production

Should include:

Should include:

Strict-Transport-Security: max-age=31536000; includeSubDomains

Strict-Transport-Security: max-age=31536000; includeSubDomains

undefined
undefined

Test Protected Route Headers

测试受保护路由的头

bash
curl -I http://localhost:3000/dashboard
bash
curl -I http://localhost:3000/dashboard

Should include:

Should include:

X-Robots-Tag: noindex, nofollow

X-Robots-Tag: noindex, nofollow

undefined
undefined

Online Header Testing

在线安全头测试工具

Use these online tools to test your deployed site:
你可以使用以下在线工具测试已部署站点的安全头:

Customizing CSP for New Integrations

为新集成自定义CSP

When adding new third-party services:
当你接入新的第三方服务时:

Example: Adding Google Analytics

示例:接入谷歌分析

typescript
// middleware.ts

// Add Google Analytics domain to CSP
const csp = [
  "default-src 'self'",
  `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${clerkDomain} https://js.stripe.com https://www.googletagmanager.com https://www.google-analytics.com`,
  `style-src 'self' 'unsafe-inline' ${clerkDomain}`,
  `connect-src 'self' ${clerkDomain} ${convexDomain} https://api.stripe.com https://www.google-analytics.com https://analytics.google.com`,
  // ... rest of CSP
].join('; ');
typescript
// middleware.ts

// Add Google Analytics domain to CSP
const csp = [
  "default-src 'self'",
  `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${clerkDomain} https://js.stripe.com https://www.googletagmanager.com https://www.google-analytics.com`,
  `style-src 'self' 'unsafe-inline' ${clerkDomain}`,
  `connect-src 'self' ${clerkDomain} ${convexDomain} https://api.stripe.com https://www.google-analytics.com https://analytics.google.com`,
  // ... rest of CSP
].join('; ');

Example: Adding Custom CDN

示例:接入自定义CDN

typescript
const cdnDomain = process.env.NEXT_PUBLIC_CDN_URL
  ? new URL(process.env.NEXT_PUBLIC_CDN_URL).origin
  : '';

const csp = [
  "default-src 'self'",
  `script-src 'self' ${cdnDomain}`,
  `style-src 'self' ${cdnDomain}`,
  `img-src 'self' ${cdnDomain} https:`,
  // ... rest of CSP
].join('; ');
typescript
const cdnDomain = process.env.NEXT_PUBLIC_CDN_URL
  ? new URL(process.env.NEXT_PUBLIC_CDN_URL).origin
  : '';

const csp = [
  "default-src 'self'",
  `script-src 'self' ${cdnDomain}`,
  `style-src 'self' ${cdnDomain}`,
  `img-src 'self' ${cdnDomain} https:`,
  // ... rest of CSP
].join('; ');

Common CSP Issues & Solutions

常见CSP问题与解决方案

Issue 1: Inline Scripts Blocked

问题1:内联脚本被拦截

Symptom: Scripts in
<script>
tags don't execute
Solution: Next.js requires
unsafe-inline
and
unsafe-eval
. Already configured in our CSP.
Better alternative: Use nonces (requires SSR changes):
typescript
const nonce = crypto.randomBytes(16).toString('base64');
script-src 'self' 'nonce-${nonce}'
症状:
<script>
标签中的脚本无法执行
解决方案: Next.js需要
unsafe-inline
unsafe-eval
配置,我们的CSP已经默认配置了这两个选项。
更优方案: 使用nonce(需要修改SSR配置):
typescript
const nonce = crypto.randomBytes(16).toString('base64');
script-src 'self' 'nonce-${nonce}'

Issue 2: External Images Not Loading

问题2:外部图片无法加载

Symptom: User avatars from Gravatar/etc don't show
Current solution:
img-src 'self' data: https: blob:
allows all HTTPS images
Stricter alternative:
typescript
`img-src 'self' data: https://gravatar.com https://images.yourapp.com`
症状: Gravatar等来源的用户头像无法显示
当前解决方案:
img-src 'self' data: https: blob:
配置允许所有HTTPS来源的图片加载
更严格的替代方案:
typescript
`img-src 'self' data: https://gravatar.com https://images.yourapp.com`

Issue 3: WebSocket Connections Failing

问题3:WebSocket连接失败

Symptom: Convex real-time updates don't work
Solution: Ensure
connect-src
includes Convex domain:
typescript
`connect-src 'self' ${convexDomain}`
症状: Convex实时更新不生效
解决方案: 确保
connect-src
包含Convex域名:
typescript
`connect-src 'self' ${convexDomain}`

What Security Headers Prevent

安全头的防护范围

Clickjacking (X-Frame-Options) ✅ XSS amplification (CSP) ✅ MIME confusion (X-Content-Type-Options) ✅ SSL stripping (HSTS) ✅ Search engine exposure of private data (X-Robots-Tag) ✅ Data exfiltration (CSP connect-src) ✅ Unauthorized resource loading (CSP default-src)
点击劫持(X-Frame-Options) ✅ XSS攻击放大(CSP) ✅ MIME混淆(X-Content-Type-Options) ✅ SSL剥离(HSTS) ✅ 私有数据被搜索引擎收录(X-Robots-Tag) ✅ 数据窃取(CSP connect-src) ✅ 未授权资源加载(CSP default-src)

Common Mistakes to Avoid

需要避免的常见错误

DON'T hardcode domains in CSP - Use environment variables ❌ DON'T enable HSTS in development - Breaks localhost ❌ DON'T use X-Frame-Options: ALLOW-FROM - Deprecated ❌ DON'T forget to test headers after deploymentDON'T set overly permissive CSP (like
*
wildcards)
DO use dynamic CSP with environment variablesDO test headers with online tools after deploymentDO update CSP when adding new third-party servicesDO keep HSTS production-onlyDO protect dashboard routes from indexing
不要在CSP中硬编码域名 - 使用环境变量 ❌ 不要在开发环境启用HSTS - 会破坏localhost访问 ❌ 不要使用X-Frame-Options: ALLOW-FROM - 已废弃 ❌ 部署后不要忘记测试安全头不要配置过于宽松的CSP(比如使用
*
通配符)
务必使用基于环境变量的动态CSP部署后务必使用在线工具测试安全头接入新的第三方服务时务必更新CSP务必仅在生产环境启用HSTS务必保护dashboard路由不被搜索引擎索引

References

参考资料

Next Steps

后续步骤

  • For XSS prevention: Use
    input-validation
    skill
  • For clickjacking tests: Use
    security-testing
    skill
  • Headers are automatic - check
    middleware.ts:1
    for implementation
  • For adding new integrations: Update CSP in middleware.ts
  • XSS防护:使用
    input-validation
    技能
  • 点击劫持测试:使用
    security-testing
    技能
  • 安全头已自动配置:查看
    middleware.ts:1
    了解实现
  • 接入新集成:更新middleware.ts中的CSP配置