A Vercel torna o deploy tão fácil que nos esquecemos de que continua a ser um servidor na internet. A plataforma em si está bem protegida: TLS automático, rede global, isolamento entre projetos. Os buracos somos nós que os pomos na configuração, e são quase sempre os mesmos cinco ou seis. Este guia percorre-os um a um com o comando para os verificar e a alteração exata para os fechar, a pensar em apps geradas com Cursor, Bolt ou v0 que acabam na Vercel.
1. Variáveis de ambiente que acabam no navegador
Na Vercel defines as variáveis no painel do projeto, e a framework decide quais chegam ao cliente. Em Next.js, tudo o que começar por NEXT_PUBLIC_; em Vite, por VITE_; em Nuxt, o que estiver em runtimeConfig.public. É uma convenção útil até alguém pôr NEXT_PUBLIC_STRIPE_SECRET_KEY porque a IA precisava de lhe aceder a partir de um componente. Essa chave viaja no bundle para todos os visitantes.
Dois hábitos que o evitam. Primeiro, rever o prefixo: se uma variável tem o prefixo público, assume que é pública e pergunta-te se devia ser. Segundo, usar os ambientes da Vercel (Production, Preview, Development) para que as chaves de produção não existam nos ramos de teste, e marcar as sensíveis como Sensitive, o que impede vê-las depois a partir do painel. Para verificar o que chegou ao bundle:
curl -s https://oteudominio.com/ | grep -o '/_next/static/chunks/[^"]*\.js' | sort -u | head -20
Descarrega esses ficheiros e procura sk_live, service_role, AKIA ou o nome das tuas variáveis. Mais em segredos que a tua app deixa fugir no navegador.
2. Deploys de pré-visualização públicos
Cada ramo e cada pull request gera um URL do tipo projeto-git-ramo-equipa.vercel.app. É cómodo para mostrar alterações, e é público: qualquer pessoa com o URL vê a versão em desenvolvimento, com as suas funcionalidades a meio e, às vezes, os seus dados de teste. A Vercel acrescenta por defeito um cabeçalho para que os motores de busca não indexem as pré-visualizações, mas isso não impede que uma pessoa a abra.
A solução está em Settings, Deployment Protection: ativa Vercel Authentication para que só membros da equipa acedam às previews (está disponível no plano gratuito), ou proteção por palavra-passe nos planos pagos. E lembra-te de que o URL projeto.vercel.app de produção também existe ao lado do teu domínio: redireciona-o para o teu domínio ou põe uma canónica para que não compita contigo no Google.
3. Cabeçalhos de segurança que ninguém pôs
A Vercel serve a tua app com os cabeçalhos mínimos; os de segurança tens de os acrescentar tu. Sem eles, o teu site pode ser embutido num iframe alheio, o navegador adivinha tipos de conteúdo e não há política que limite que scripts se executam. No vercel.json acrescentam-se assim:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains; preload" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
]
}
]
}
Em Next.js também o podes fazer a partir do next.config.js com a função headers(). A Content-Security-Policy merece um passo à parte porque tem de ser ajustada aos teus scripts; a explicação de cada cabeçalho está em cabeçalhos de segurança HTTP explicados para humanos, e podes verificar os teus com o analisador de cabeçalhos.
4. Source maps publicados
Os source maps permitem ver o código original em vez do minificado. São úteis para depurar e uma má ideia em produção: oferecem a tua lógica, as tuas rotas internas e os comentários com "TODO: tirar esta chave". O Next.js desativa-os por defeito no navegador (productionBrowserSourceMaps: false); em Vite depende de build.sourcemap, que alguns templates põem a true. Verifica se existem:
curl -sI https://oteudominio.com/_next/static/chunks/main-abc123.js.map | head -1
Se responder 200, estão publicados. Se precisas de mapas para rastrear erros, envia-os para o teu serviço de monitorização no build e não os sirvas ao público.
5. Ficheiros e rotas que não deviam responder
Tudo o que está na pasta public/ é servido tal e qual. Um .env copiado para lá por engano, um backup.json, um config.yml com credenciais: públicos. As rotas de API também contam: /api/debug, /api/seed ou /api/admin que a IA criou para testar e que continuam em produção sem autenticação. E as rotas de cron: a Vercel chama-as com um cabeçalho Authorization que contém o teu CRON_SECRET; se não o verificas no código, qualquer pessoa pode disparar a tua tarefa agendada.
for p in .env .env.local .git/config config.json backup.zip api/debug api/seed; do
printf "%s -> " "$p"; curl -s -o /dev/null -w "%{http_code}\n" "https://oteudominio.com/$p"
done
Tudo o que responder 200 merece uma vista de olhos.
6. Funções sem limites
Uma rota de API que chama a OpenAI ou envia emails sem limitar pedidos é uma fatura à espera de ser descoberta. A Vercel tem uma firewall com regras de limite de pedidos por IP e um modo de desafio contra bots que se ativa a partir do painel sem tocar no código. Para endpoints com custo, acrescenta ainda um limite por utilizador na tua própria lógica e exige sessão.
7. CORS e cookies nas rotas de API
Se a tua API responde com Access-Control-Allow-Origin: * e além disso usa cookies, qualquer site pode fazer pedidos em nome dos teus utilizadores. Na Vercel configura-se por rota no vercel.json ou no próprio handler. Lista as origens permitidas de forma explícita e nunca combines o wildcard com credenciais. Detalhe em CORS mal configurado.
8. O que a Vercel já faz por ti
Para não perderes tempo: o certificado TLS é emitido e renovado sozinho, o HTTP redireciona para HTTPS automaticamente, as funções correm isoladas e os registos de deploy ficam auditados. O que não faz é decidir por ti o que é público, que cabeçalhos envias nem quem pode ver as tuas previews. Essa parte é tua.
Checklist de cinco minutos
- Nenhuma variável com prefixo público contém uma chave secreta.
- Deployment Protection ativada para as pré-visualizações.
- Cabeçalhos de segurança no
vercel.jsonou nonext.config.js. - Source maps não acessíveis em produção.
- Nada sensível em
public/; rotas de teste eliminadas; cron protegido com o seu segredo. - Limite de pedidos nas rotas com custo.
- CORS com origens explícitas.
- O URL
.vercel.appredireciona para o teu domínio.
O Scanaris verifica isto por ti
O Scanaris revê de fora o teu deploy na Vercel: cabeçalhos, segredos no bundle, source maps, ficheiros e rotas expostas, CORS, cookies e TLS, e dá-te um fix prompt por cada falha para colar no Cursor, no Bolt ou no v0 com a alteração exata no vercel.json ou no teu código. São verificações só de leitura sobre o que já é público; nunca toca no teu projeto. Os riscos específicos da plataforma estão no guia de segurança para Vercel, e se a tua app é Next.js, na checklist de Next.js.
Perguntas frequentes
As pré-visualizações da Vercel são privadas por defeito?
Não. São públicas para quem tiver o URL, embora não sejam indexadas pelos motores de busca. Ativa Vercel Authentication em Deployment Protection para as limitares à tua equipa.
A Vercel põe cabeçalhos de segurança automaticamente?
Só os básicos de funcionamento. HSTS, X-Frame-Options, Content-Security-Policy e os restantes tens de os configurar tu no vercel.json ou na framework.
O que acontece se uma chave secreta já foi publicada com prefixo público?
Dá-a por comprometida mesmo que a tires do código: o bundle anterior pode estar em cache ou descarregado. Roda a chave no serviço de origem, passa-a para uma variável sem prefixo e faz deploy de novo.