Security headers web com Claude Code: CSP, nonce, HSTS e deploy sem quebrar anúncios
Configure CSP, nonce, HSTS, frame-ancestors e security headers com Claude Code em Next.js, Astro, Express e Cloudflare.
Security headers parecem apenas detalhes de resposta HTTP, mas definem regras importantes para o navegador. Eles controlam quais scripts podem executar, se uma área administrativa pode ser carregada dentro de um iframe, quanta informação de referrer vai para sites externos e se recursos como câmera, microfone, localização e pagamento ficam disponíveis.
Claude Code acelera esse trabalho porque consegue ler o repositório, identificar scripts de terceiros, ajustar configurações de framework e sugerir comandos de verificação. O problema é que um pedido vago pode gerar uma política perigosa. Se você pedir apenas “corrija os erros de CSP”, a resposta pode ser algo como script-src * 'unsafe-inline' 'unsafe-eval'. Os erros somem, mas a proteção também.
A abordagem correta é inventariar recursos, publicar Content-Security-Policy-Report-Only, analisar relatórios, ajustar por rota e só então aplicar a CSP em modo enforcement. Este guia cobre CSP, nonce, HSTS, X-Frame-Options, frame-ancestors, Referrer-Policy e Permissions-Policy, com exemplos para Next.js, Astro, Express e Cloudflare Pages. Também inclui CSP report, Security Headers, CSP Evaluator e conflitos comuns com Google Tag Manager, Google Analytics, AdSense, imagens e CDNs. Para a segurança do próprio fluxo com Claude Code, veja também boas práticas de segurança e auditoria de segurança com Claude Code.
Use a documentação oficial como referência: MDN Content-Security-Policy, Next.js CSP guide, MDN Strict-Transport-Security, hstspreload.org, Cloudflare Pages Headers, Helmet, Google Tag Manager CSP e AdSense com CSP.
Comece pelo inventário de recursos
Não copie uma política pronta antes de entender o que o site realmente carrega. Peça ao Claude Code um levantamento explícito.
Projete security headers para este repositório.
Regras:
- Primeiro liste origens externas de script, style, image, font, frame e connect.
- Comece CSP com Report-Only.
- Evite * e unsafe-inline permanente.
- Se Next.js precisar de nonce, explique impacto em renderização dinâmica e cache.
- Verifique Google Analytics, GTM, AdSense, CDN de imagens e YouTube iframe.
- Entregue passos de teste com curl, Security Headers e CSP Evaluator.
Essa etapa evita confusões comuns. frame-src controla quais iframes sua página pode carregar. frame-ancestors controla quem pode colocar sua página dentro de um iframe. Problemas de GA4 geralmente aparecem em connect-src; imagens de CDN em img-src; clickjacking em frame-ancestors.
| Header | Ponto de partida | Cuidado |
|---|---|---|
Content-Security-Policy | Primeiro Content-Security-Policy-Report-Only | Não esconda problemas com * |
Strict-Transport-Security | Comece com max-age=300; includeSubDomains | preload só após todos os subdomínios estarem em HTTPS |
X-Frame-Options | DENY ou SAMEORIGIN | frame-ancestors é mais moderno e granular |
Referrer-Policy | strict-origin-when-cross-origin | Não coloque dados sensíveis em URLs |
Permissions-Policy | Desative recursos não usados | Libere apenas o que o produto usa |
X-Content-Type-Options | nosniff | Normalmente vale para o site inteiro |
HSTS preload não deve ser ativado no primeiro dia. O site oficial recomenda aumentar max-age em etapas e alerta que desfazer preload pode demorar. Um subdomínio antigo sem HTTPS ou um domínio de testes esquecido pode virar uma interrupção real.
CSP como rollout controlado
O fluxo mais seguro é observar, classificar e depois aplicar.
flowchart LR
A["Inventariar recursos"] --> B["Enviar CSP Report-Only"]
B --> C["Coletar console e reports"]
C --> D["Separar anúncios, analytics, CDN, iframes e ruído"]
D --> E["Ativar CSP com nonce ou hash"]
E --> F["Validar com Security Headers e CSP Evaluator"]
Não adicione todos os domínios relatados. Extensões de navegador, proxies corporativos, tags antigas e tentativas de ataque podem gerar reports. Claude Code ajuda a agrupar, mas a decisão final deve ser baseada no que o produto realmente precisa.
Next.js com nonce
Na documentação atual do App Router, Next.js usa proxy.ts para gerar um nonce por requisição. Projetos antigos podem usar middleware.ts, mas a lógica é a mesma: gerar um valor imprevisível, colocá-lo no request header e usar o mesmo valor em script-src.
Nonce por requisição tem impacto em renderização. Uma página que precisa de nonce novo não combina bem com HTML totalmente estático. Para blogs e landing pages públicas, hashes CSP ou scripts externos podem ser melhores. Para checkout, painel, conta e administração, nonce costuma compensar.
// proxy.ts
import { NextRequest, NextResponse } from "next/server";
export function proxy(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
const isDev = process.env.NODE_ENV !== "production";
const csp = [
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic' ${isDev ? "'unsafe-eval'" : ""} https: http:`,
`style-src 'self' ${isDev ? "'unsafe-inline'" : `'nonce-${nonce}'`} https://fonts.googleapis.com`,
"font-src 'self' https://fonts.gstatic.com",
"img-src 'self' data: blob: https:",
"connect-src 'self' https://www.google-analytics.com https://analytics.google.com",
"frame-src 'self' https://www.youtube-nocookie.com",
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
"frame-ancestors 'none'",
"upgrade-insecure-requests",
"report-uri /api/csp-report",
].join("; ").replace(/\s{2,}/g, " ").trim();
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-nonce", nonce);
const response = NextResponse.next({ request: { headers: requestHeaders } });
response.headers.set("Content-Security-Policy", csp);
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
response.headers.set("Permissions-Policy", "camera=(), microphone=(), geolocation=(), payment=(self)");
response.headers.set("Strict-Transport-Security", "max-age=300; includeSubDomains");
return response;
}
export const config = {
matcher: ["/((?!api/csp-report|_next/static|_next/image|favicon.ico).*)"],
};
Se você usa Google Tag Manager, passe o nonce para o snippet ou componente. O bootstrap do GTM é JavaScript inline, e o próprio Google recomenda o uso de nonce. O AdSense também documenta strict CSP, pois os domínios de anúncios podem mudar com o tempo. Uma allowlist fixa pode quebrar receita depois.
Recebendo CSP reports
Report-Only precisa de um endpoint. Em Next.js, um Route Handler mínimo pode ser assim:
// app/api/csp-report/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const contentType = request.headers.get("content-type") ?? "";
const body = await request.text();
const isReport =
contentType.includes("application/csp-report") ||
contentType.includes("application/reports+json") ||
body.includes("violated-directive");
if (!isReport) {
return NextResponse.json({ ok: false }, { status: 415 });
}
console.warn("csp-report", body.slice(0, 4000));
return new NextResponse(null, { status: 204 });
}
Em produção, não grave URLs completas se parâmetros podem conter dados pessoais. Registre rota, diretiva violada, URI bloqueada, user agent, horário e contagem. report-uri é antigo, mas ainda é útil por compatibilidade. report-to pode ser complemento, não a única base.
Astro, Express e Cloudflare
Astro permite inserir headers em src/middleware.ts. Em sites majoritariamente estáticos, reduzir scripts inline costuma ser mais simples do que gerar nonces.
// src/middleware.ts
import { defineMiddleware } from "astro:middleware";
const securityHeaders: Record<string, string> = {
"Content-Security-Policy-Report-Only": "default-src 'self'; script-src 'self' https://www.googletagmanager.com; img-src 'self' data: blob: https:; connect-src 'self' https://www.google-analytics.com; frame-src 'self' https://www.youtube-nocookie.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; report-uri /api/csp-report",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Referrer-Policy": "strict-origin-when-cross-origin",
"Permissions-Policy": "camera=(), microphone=(), geolocation=(), payment=(self)",
};
export const onRequest = defineMiddleware(async (_context, next) => {
const response = await next();
for (const [name, value] of Object.entries(securityHeaders)) {
response.headers.set(name, value);
}
return response;
});
No Express, Helmet é a opção prática, mas CSP continua sendo específica da aplicação.
import crypto from "node:crypto";
import express from "express";
import helmet from "helmet";
const app = express();
app.use((req, res, next) => {
res.locals.cspNonce = crypto.randomBytes(16).toString("base64");
next();
});
app.use(helmet({
contentSecurityPolicy: {
useDefaults: false,
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`, "'strict-dynamic'", "https:", "http:"],
imgSrc: ["'self'", "data:", "blob:", "https:"],
connectSrc: ["'self'", "https://www.google-analytics.com"],
objectSrc: ["'none'"],
baseUri: ["'self'"],
frameAncestors: ["'none'"],
reportUri: ["/csp-report"],
},
},
strictTransportSecurity: { maxAge: 300, includeSubDomains: true },
referrerPolicy: { policy: "strict-origin-when-cross-origin" },
xFrameOptions: { action: "deny" },
}));
Cloudflare Pages usa _headers para headers estáticos. Esse arquivo não cria nonces por request.
/*
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self)
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://www.googletagmanager.com; img-src 'self' data: blob: https:; connect-src 'self' https://www.google-analytics.com; frame-src 'self' https://www.youtube-nocookie.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; report-uri /csp-report
Casos de uso e armadilhas
Um site de conteúdo com GA4, GTM, AdSense, fontes externas, YouTube e CDN de imagens precisa equilibrar segurança e receita. Use Report-Only antes de enforcement, valide se anúncios aparecem, se eventos de Analytics chegam e se imagens continuam carregando.
Uma área administrativa de SaaS deve ser mais restritiva. Menos scripts de terceiros, frame-ancestors 'none', object-src 'none', base-uri 'self' e form-action estreito são boas bases. SDKs de pagamento e chat devem existir apenas nas rotas necessárias.
Um widget incorporável precisa de política própria. Se clientes precisam inserir a página em iframe, X-Frame-Options: DENY quebra a funcionalidade. Separe headers por rota e defina origens confiáveis em frame-ancestors.
Verificação e resultado prático
Confira pelo menos home, login ou formulário, e uma rota de embed ou checkout.
curl -I https://example.com/
curl -I https://example.com/login
curl -I https://example.com/embed/widget
Depois use Security Headers para visão geral e CSP Evaluator para fragilidades da CSP. Nota alta é útil, mas não substitui teste de anúncio, Analytics, pagamento, imagem e iframe.
No teste preparado para este artigo, Report-Only foi o maior ganho. Ele separou problemas de nonce no GTM, connect-src no GA4, frame-src no YouTube e img-src no CDN. Com esses reports, Claude Code conseguiu propor política por rota, remoção de tags desnecessárias e aumento gradual de HSTS. Para aplicar isso em um projeto real, veja treinamento e consultoria Claude Code ou os templates e produtos ClaudeCodeLab e transforme a lista em regra de revisão no CLAUDE.md.
Artigos relacionados
Guia completo de CORS com Claude Code: APIs cross-origin seguras
Configure CORS com Claude Code: preflight, credentials, allowlist de origin, comandos de teste e prompts de revisão.
Auditoria de Permissões Claude Code antes do Deploy: Checklist Seguro
Checklist de permissões, comandos, arquivos, provas e handoff antes de deploy real com Claude Code.
Propostas e briefings de agência web em metade do tempo com o Claude Code
Diretor de agência web: monte propostas e briefings em metade do tempo com o Claude Code. Com prompt, checklist e script prontos.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.