X-Content-Type-Options、X-Frame-Options、Content-Security-Policy、Referrer-Policy 四者在几乎所有生产场景下都必须设置;Strict-Transport-Security 仅限 HTTPS 环境启用,HTTP 下硬加会导致重定向循环;X-XSS-Protection 已被现代浏览器弃用,无需配置。

gin.SecurityMiddleware 里哪些头必须设,哪些要看环境
直接给结论:X-Content-Type-Options、X-Frame-Options、Content-Security-Policy、Referrer-Policy 这四个在几乎所有生产场景下都该开;Strict-Transport-Security 只在 HTTPS 下生效,HTTP 环境硬加反而可能被中间人篡改;X-XSS-Protection 已被现代浏览器弃用,Chrome 89+ 完全忽略,留着纯属占位。
常见错误是把 Strict-Transport-Security 写死在所有响应里——HTTP 请求返回这个头,浏览器会记住并强制跳转 HTTPS,但若后端没配 HTTPS,用户就卡死在重定向循环里。正确做法是只在 c.Request.TLS != nil 或 X-Forwarded-Proto: https 时才写入。
-
X-Frame-Options: DENY和Content-Security-Policy: frame-ancestors 'none'功能重叠,但后者更现代、支持细粒度控制(比如只允许某域名嵌入),建议两者都设,兼容老浏览器 -
Referrer-Policy推荐用strict-origin-when-cross-origin,比no-referrer更平衡:同站传完整 referer,跨站只传源站,不泄露路径参数 - 如果用了前端微前端架构(如 qiankun),
frame-ancestors得改成具体白名单,不能写'none',否则子应用 iframe 加载失败
为什么 gin-contrib/csrf 不适合直接套用,而要用 gorilla/csrf
gin-contrib/csrf 库已三年未更新,不支持 Gin v2 的 Context 取值方式,且默认把 token 存在 Cookie 里但没设 SameSite=Strict,导致 CSRF 防护形同虚设——攻击者用恶意网站发起 POST 请求时,浏览器仍会自动带上 Cookie。
真正可用的是 gorilla/csrf + utrack/gin-csrf 适配器组合。它默认生成双 token(Cookie + 表单字段),且 Cookie 自动带 HttpOnly、SameSite=Strict、Secure(HTTPS 下)等关键属性。
- 密钥必须严格 32 字节,少一字节都会 panic,别用字符串字面量硬编码,从环境变量读取再做
sha256.Sum256转换 - CSRF token 必须显式注入到 HTML 模板里,比如
{{.CSRFToken}},不能靠 JS 动态 fetch,否则绕过 SameSite 限制 - API 场景(前后端分离)不用这套——JWT 或 session token 本身已含防重放能力,CSRF 对无 Cookie 的 API 请求无效
CORS 白名单配置容易漏掉的三个细节
很多团队在 server/middleware/cors.go 里写了白名单,但线上仍报跨域错误,问题往往出在协议、端口、路径这三处。
比如前端地址是 https://admin.example.com:8080,后端白名单只写了 https://admin.example.com,漏了 :8080 端口,浏览器就判定不匹配;或者前端用 http://localhost:3000 调试,白名单没包含 http://localhost:3000(注意协议是 http,不是 https)。
-
Access-Control-Allow-Origin不能设多个值,只能写一个域名或*(但*和Access-Control-Allow-Credentials: true冲突) -
Access-Control-Allow-Headers必须包含前端实际发送的所有自定义头,比如用了X-User-Id,就必须显式列出,少一个就会预检失败 - 开发环境可临时用
origin == "http://localhost:*"模糊匹配,但上线前必须收窄为精确域名+协议+端口
安全头和 Nginx 的协作边界在哪
安全头既能在 Gin 中间件里设,也能在 Nginx 配置里加,但混用时容易冲突。比如 Gin 设了 Content-Security-Policy,Nginx 又加了一次,浏览器收到两个同名头,只认最后一个(通常是 Nginx 的),Gin 的配置就失效了。
推荐分工:Gin 负责动态头(比如根据登录态决定是否暴露 Set-Cookie),Nginx 负责静态头(所有请求都一样的)。典型分法是:
- Gin 写:
X-Frame-Options、Referrer-Policy、Content-Security-Policy(若需根据用户角色动态调整 script-src) - Nginx 写:
Strict-Transport-Security、X-Content-Type-Options、X-XSS-Protection(虽然已废弃,但某些旧 IE 仍依赖) - 绝对不要在两边重复设同一头,尤其
Content-Security-Policy,拼错一个引号整个策略就失效
最麻烦的是 Content-Security-Policy 的调试——浏览器控制台只报“violated directive”,不告诉你哪条规则触发的。上线前务必用 Content-Security-Policy-Report-Only 头跑几天,收集真实 violation report 再调策略。


















