← Guías por stack

Vercel

Seguridad al desplegar en Vercel

Vercel despliega con un git push, y cada facilidad esconde una decisión de seguridad: variables de entorno, deploys de preview accesibles, source maps y cabeceras. Escanea tu despliegue y confírmalo.

Riesgos típicos en Vercel

Variables públicas mal usadas

Las variables con prefijo público se incrustan en el bundle. Guarda ahí solo lo que no te importe enseñar; las claves privadas van sin prefijo, solo en servidor.

Deploys de preview públicos

Cada rama genera una URL de preview accesible para quien la conozca, a menudo conectada a datos o claves reales. No las trates como privadas.

Source maps y cabeceras

Vercel sirve HTTPS pero no añade cabeceras defensivas. Y si publicas source maps, cualquiera reconstruye tu código. Ambas se configuran en el proyecto.

"Seguridad en Vercel" se divide en dos mitades que conviene no mezclar. La primera es lo que Vercel gestiona por ti y lo que te deja configurar en el panel: protección de despliegues, variables por entorno, cortafuegos, roles del equipo. La segunda es lo que Cursor, v0 o Bolt escriben en tu proyecto y que Vercel despliega tal cual, con sus claves públicas y sus rutas sin proteger. Esta guía explica el modelo de Vercel, repasa lo que la IA hace mal al desplegar ahí, te deja un vercel.json listo para pegar, un prompt de revisión y cuatro comprobaciones desde fuera. El detalle de cada fallo está en seguridad al desplegar en Vercel.

Parte 1: cómo funciona la seguridad en Vercel

Lo que Vercel hace solo

Certificado TLS emitido y renovado automáticamente, redirección de HTTP a HTTPS, funciones serverless aisladas entre proyectos, red global con mitigación de DDoS y un registro de cada despliegue con quién lo hizo y desde qué commit. Nada de esto lo tienes que configurar. También pone una cabecera para que los buscadores no indexen las vistas previas.

Lo que es configuración tuya

  • Deployment Protection. Por defecto las previews son públicas para quien tenga la URL. En Settings puedes exigir cuenta de Vercel del equipo (gratis), contraseña (planes de pago) o limitar por IP. Es la primera casilla que activar.
  • Variables de entorno por entorno. Cada variable se asigna a Production, Preview o Development. Las claves de producción no deberían existir en Preview, y las sensibles se marcan como Sensitive para que no se puedan volver a leer desde el panel.
  • Cortafuegos. Vercel Firewall trae reglas gestionadas, un modo de desafío contra bots y reglas propias de límite de peticiones por IP o por ruta. Se activan desde el panel sin tocar código y son la respuesta a "alguien está gastando mi cuota de OpenAI".
  • Git y despliegues. Cada push a cualquier rama despliega. Protege la rama de producción en GitHub, revisa qué cuentas tienen acceso al proyecto y activa la autenticación en dos pasos en el equipo.
  • Tokens y servicios. Los tokens de la CLI, los de Vercel Blob o los de las bases de datos integradas son secretos de servidor. Un token de Vercel con alcance de equipo despliega lo que quiera en todos tus proyectos.

Lo que depende del framework, no de Vercel

Qué variables llegan al navegador (NEXT_PUBLIC_, VITE_, NUXT_PUBLIC_), si se publican source maps y qué cabeceras se envían lo decide tu framework y tu configuración, no la plataforma. Vercel solo ejecuta el build. Por eso dos apps en Vercel pueden tener una A y una F en cabeceras.

Parte 2: lo que la IA hace mal al desplegar en Vercel

  • Claves secretas con prefijo público porque el componente "las necesitaba". Viajan en el bundle a todos los visitantes. Busca sk_live, service_role o AKIA en tus chunks.
  • Rutas de prueba desplegadas. /api/debug, /api/seed o /api/admin creadas para probar y subidas a producción sin autenticación.
  • Cron sin secreto. Vercel llama a tu ruta de cron con una cabecera Authorization que lleva tu CRON_SECRET; si el handler no la comprueba, cualquiera lanza la tarea.
  • Funciones sin límite. Un endpoint que llama a un modelo o envía correos sin tope por usuario ni regla de cortafuegos.
  • Archivos en public/. Un .env copiado, un backup.json, un config.yml. Todo lo de esa carpeta es público.
  • CORS con comodín y credenciales en las rutas de API, porque "así funcionaba desde localhost".
  • Sin vercel.json. Ni cabeceras de seguridad ni redirección del dominio .vercel.app al dominio propio, que queda duplicado en Google.

Un vercel.json listo para pegar

Cabeceras de seguridad para todas las rutas y redirección del subdominio de Vercel a tu dominio. Sustituye los dos nombres y, si usas Next.js, puedes poner las cabeceras en next.config en su lugar:

{
  "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=()" }
      ]
    }
  ],
  "redirects": [
    {
      "source": "/(.*)",
      "has": [{ "type": "host", "value": "mi-proyecto.vercel.app" }],
      "destination": "https://tudominio.com/$1",
      "permanent": true
    }
  ]
}

La Content-Security-Policy va aparte porque depende de tus scripts; empieza en modo informe como explica cabeceras de seguridad HTTP.

Un prompt de revisión antes de desplegar

Revisa la seguridad de este proyecto desplegado en Vercel y no cambies nada todavía. Responde con una lista:
1. Variables con prefijo público (NEXT_PUBLIC_, VITE_, NUXT_PUBLIC_) que contengan claves secretas.
2. Rutas de API sin comprobación de sesión, rutas de prueba o depuración, y rutas de cron que no verifiquen CRON_SECRET.
3. Endpoints que llamen a servicios de pago sin límite por usuario.
4. Archivos en public/ que no deban ser públicos.
5. Rutas de API con Access-Control-Allow-Origin: * y credenciales.
6. Si existe vercel.json o next.config con cabeceras de seguridad y si los source maps están desactivados en producción.
Para cada punto, propón el cambio exacto y espera mi confirmación.

Comprueba el resultado desde fuera

curl -sI https://tudominio.com | grep -iE "strict-transport|x-frame|x-content-type|content-security"
curl -sI https://mi-proyecto.vercel.app | grep -iE "^HTTP|^location"
curl -sI https://mi-proyecto-git-main-equipo.vercel.app | grep -iE "^HTTP"
for p in .env .env.local config.json backup.zip api/debug api/seed; do printf "%-14s " "$p"; curl -s -o /dev/null -w "%{http_code}\n" "https://tudominio.com/$p"; done

Lo esperado: cabeceras presentes, un 301 hacia tu dominio en la URL .vercel.app, un 401 en la URL de preview si la protección está activa, y 404 en todas las rutas de la lista. Scanaris hace estas comprobaciones y unas cuarenta más en 30 segundos: detecta que la app está en Vercel y con qué framework, busca secretos en el bundle, comprueba source maps, cabeceras, CSP, cookies, CORS, TLS y archivos expuestos, y te da un fix prompt por cada fallo para pegar en Cursor, v0 o Bolt. Si tu app es Next.js, la guía de Next.js explica qué código corre en el servidor y qué llega al navegador.

Preguntas frecuentes

¿Las vistas previas de Vercel son privadas?

No por defecto: cualquiera con la URL las abre, aunque no se indexen. Activa Vercel Authentication en Deployment Protection; está disponible en el plan gratuito.

¿Vercel protege mis rutas de API?

No. Una ruta de API es una URL pública; la sesión la compruebas tú en el código. El cortafuegos puede limitar peticiones y frenar bots, pero no decide quién está autorizado.

¿Necesito vercel.json si uso Next.js?

Para las cabeceras no: next.config tiene la función headers(). Para la redirección del dominio .vercel.app al tuyo puedes usar cualquiera de los dos. Lo importante es que exista en alguno.

Checks relacionados

¿Tu app Vercel pasa estos checks?

Escanéala gratis y recibe un prompt de arreglo por cada fallo que encontremos.

Escanear mi web gratis