← Blog
·9 min read

Headers de Seguridad en Next.js: Guía Completa (2026)

Configura los headers de seguridad en Next.js — CSP, HSTS, X-Frame-Options y más. Ejemplos reales en next.config.ts y por qué cada header importa.

Rod

Founder & Developer

Los headers de seguridad en Next.js son la primera línea de defensa de tu app — y la más ignorada. No requieren cambiar tu lógica de negocio. No requieren librerías adicionales. Son 30 líneas en next.config.ts que bloquean categorías enteras de ataques.

Cuando escaneamos URLs de apps en producción, más del 60% no tienen CSP configurado. La mayoría tampoco tiene HSTS ni X-Frame-Options. Esto no es un problema de Next.js — es que nadie lo enseña cuando aprendes el framework.

Este post lo arregla.


Qué son los security headers y por qué importan

Los HTTP security headers son instrucciones que tu servidor envía al browser con cada respuesta. No son código que corre en tu app — son directivas que el browser sigue para proteger al usuario.

Ejemplo: sin el header X-Frame-Options, cualquier sitio puede cargar tu app dentro de un <iframe>. Eso permite ataques de clickjacking — donde alguien pone un iframe invisible de tu app sobre un botón falso y el usuario cree que hace clic en algo inocente, pero en realidad está ejecutando una acción en tu app (transferir dinero, cambiar email, etc.).

Con X-Frame-Options: DENY, el browser rechaza cargar tu app en un iframe. El ataque no funciona.

El OWASP Top 10 incluye "Misconfiguration" como categoría — headers faltantes o mal configurados entran aquí. No es el vector de ataque más sofisticado, pero es consistentemente explotado porque es fácil de detectar y la corrección es trivial.


Cómo configurar headers en Next.js

En Next.js, los headers se configuran en next.config.ts con la función headers(). Se aplican a todas las respuestas del servidor — páginas, API routes, assets.

// next.config.ts
import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        // Aplicar a todas las rutas
        source: "/(.*)",
        headers: securityHeaders,
      },
    ];
  },
};
 
export default nextConfig;

Defines los headers como un array de objetos { key, value } que asignas a securityHeaders. Veamos cada uno.


Los 6 headers de seguridad esenciales para Next.js

1. Content-Security-Policy (CSP)

CSP es el más poderoso y el más complicado. Le dice al browser exactamente de dónde puede cargar scripts, estilos, imágenes, fonts y otros recursos. Si alguien inyecta un script malicioso en tu página, CSP evita que el browser lo ejecute si viene de un origen no autorizado.

// CSP básico para una app Next.js con Vercel Analytics y Google Fonts
{
  key: "Content-Security-Policy",
  value: [
    "default-src 'self'",
    "script-src 'self' 'unsafe-eval' 'unsafe-inline' https://vercel.live",
    "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
    "font-src 'self' https://fonts.gstatic.com",
    "img-src 'self' data: blob: https:",
    "connect-src 'self' https://*.supabase.co wss://*.supabase.co",
    "frame-ancestors 'none'",
  ].join("; "),
},

'unsafe-inline' y 'unsafe-eval' son necesarios si usas algunas librerías de terceros o Tailwind en runtime. El objetivo es restringir lo que puedes — incluso un CSP con 'unsafe-inline' que restringe connect-src ya protege contra exfiltración de datos.

Si quieres el CSP más estricto posible, usa nonces. Next.js tiene soporte nativo para eso con middleware:

// middleware.ts — generar un nonce por request para CSP estricto
import { NextResponse } from "next/server";
 
export function middleware(request: Request) {
  const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
  const csp = `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}';
    style-src 'self' 'nonce-${nonce}';
  `.replace(/\n/g, "");
 
  const response = NextResponse.next();
  response.headers.set("Content-Security-Policy", csp);
  response.headers.set("x-nonce", nonce); // para pasarlo al layout
  return response;
}

2. Strict-Transport-Security (HSTS)

Le dice al browser que solo se conecte a tu sitio por HTTPS. Después de la primera visita, el browser rechaza conexiones HTTP automáticamente — incluso antes de que el servidor responda.

{
  key: "Strict-Transport-Security",
  value: "max-age=63072000; includeSubDomains; preload",
},

max-age=63072000 es 2 años en segundos — el valor recomendado por hstspreload.org. includeSubDomains aplica el header a todos los subdominios. preload permite que tu dominio entre en la lista de preload de browsers — una vez ahí, el browser usa HTTPS incluso en la primera visita, antes de recibir el header.

Vercel habilita HTTPS automáticamente. Solo necesitas el header para aprovechar HSTS.

3. X-Frame-Options

Controla si tu app puede cargarse en un iframe. Previene clickjacking.

{
  key: "X-Frame-Options",
  value: "DENY",
},

DENY bloquea iframes completamente. SAMEORIGIN permite iframes solo desde tu propio dominio. Si no tienes un caso de uso legítimo para iframes, usa DENY.

Nota: frame-ancestors en CSP es la versión moderna de este header y tiene precedencia en browsers modernos. Incluir ambos garantiza compatibilidad con browsers que no soportan CSP.

4. X-Content-Type-Options

Previene que el browser intente "adivinar" el tipo de contenido de un archivo. Sin este header, un archivo subido como imagen que en realidad contiene JavaScript puede ejecutarse como script.

{
  key: "X-Content-Type-Options",
  value: "nosniff",
},

Siempre nosniff. No hay razón para no incluir este header.

5. Referrer-Policy

Controla cuánta información de la URL de origen se incluye en el header Referer cuando el usuario navega a otro sitio.

{
  key: "Referrer-Policy",
  value: "strict-origin-when-cross-origin",
},

strict-origin-when-cross-origin envía el origen completo en requests del mismo sitio, solo el origen (sin path) en requests cross-origin sobre HTTPS, y nada en requests cross-origin sobre HTTP. Es el balance correcto entre funcionalidad y privacidad.

6. Permissions-Policy

Le dice al browser qué APIs del dispositivo puede usar tu app. Si tu app no usa la cámara ni el micrófono, deshabílilalos — así un script inyectado no puede acceder a ellos.

{
  key: "Permissions-Policy",
  value: "camera=(), microphone=(), geolocation=(self), interest-cohort=()",
},

Ajusta según lo que tu app necesita. interest-cohort=() desactiva FLoC (la API de Google para tracking de cohortes).


Configuración completa lista para copiar

// next.config.ts
import type { NextConfig } from "next";
 
const securityHeaders = [
  {
    key: "Content-Security-Policy",
    value: [
      "default-src 'self'",
      "script-src 'self' 'unsafe-eval' 'unsafe-inline'",
      "style-src 'self' 'unsafe-inline'",
      "img-src 'self' data: blob: https:",
      "font-src 'self'",
      "connect-src 'self'",
      "frame-ancestors 'none'",
    ].join("; "),
  },
  {
    key: "Strict-Transport-Security",
    value: "max-age=63072000; includeSubDomains; preload",
  },
  {
    key: "X-Frame-Options",
    value: "DENY",
  },
  {
    key: "X-Content-Type-Options",
    value: "nosniff",
  },
  {
    key: "Referrer-Policy",
    value: "strict-origin-when-cross-origin",
  },
  {
    key: "Permissions-Policy",
    value: "camera=(), microphone=(), geolocation=(self), interest-cohort=()",
  },
];
 
const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        source: "/(.*)",
        headers: securityHeaders,
      },
    ];
  },
};
 
export default nextConfig;

Copia esto como punto de partida. Ajusta el CSP según los servicios de terceros que uses (Stripe, Google Analytics, Supabase, etc.).


Cómo verificar tus headers después de deployar

El verificador de cabeceras de seguridad de Data Hogo analiza tu URL de producción y te dice exactamente qué headers tienes, cuáles faltan, y cómo están configurados.

Verifica los headers de tu app gratis →

También puedes verlos en DevTools: Network tab > selecciona cualquier request > pestaña Headers > Response Headers.

Si quieres verificar contra los estándares, securityheaders.com da un score de A a F basado en los headers presentes.


Preguntas frecuentes

¿Qué son los security headers en Next.js?

Son headers HTTP que tu servidor envía con cada respuesta y que le dicen al browser cómo comportarse para proteger al usuario. No son código que corre en tu app — son instrucciones que el browser sigue. Configurarlos en Next.js tarda menos de 10 minutos y bloquea categorías enteras de ataques como XSS y clickjacking.

¿Cómo agrego security headers en Next.js?

En next.config.ts, exporta una función headers() dentro de la configuración de Next.js. Cada entrada tiene un source (qué rutas aplica) y un array de headers con key y value. Los headers se aplican a todas las respuestas que coincidan con el patrón source.

¿Qué es CSP y por qué es el header más importante?

Content Security Policy (CSP) le dice al browser exactamente de dónde puede cargar scripts, estilos, imágenes y otros recursos. Sin CSP, si alguien logra inyectar un script malicioso en tu página, el browser lo ejecuta sin restricciones. CSP es la defensa más efectiva contra ataques XSS.

¿HSTS afecta el desarrollo local en Next.js?

HSTS solo aplica en conexiones HTTPS. En desarrollo local con HTTP, el header se envía pero el browser lo ignora para localhost. No afecta tu flujo de desarrollo. En producción con HTTPS, el browser guarda la instrucción y rechaza conexiones HTTP en futuras visitas.

¿Cómo verifico que mis security headers están correctamente configurados?

Puedes verificarlos con el verificador de cabeceras de seguridad de Data Hogo — conecta tu URL de producción y te dice qué headers tienes, qué falta, y cómo están configurados. También puedes usar las DevTools del browser (Network tab > selecciona cualquier request > Headers).

nextjssecurity headersCSPHSTSseguridadnext.configtutoriales