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.
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.
Artigos relacionados
Workflow Obsidian para CLAUDE.md com Claude Code
Transforme notas de trabalho do Obsidian em notas operacionais CLAUDE.md para preservar contexto.
Claude Code Revenue CTA Routing: artigos para PDF, Gumroad e consultoria
Um fluxo com Claude Code para levar leitores ao PDF grátis, Gumroad ou consultoria conforme intenção.
Regras de handoff para equipes com Claude Code: evidências, permissões, rollback e receita
Formato prático para entregar trabalho do Claude Code com prova, permissões, rollback, PDF grátis, Gumroad e consultoria.