En el desarrollo de software a escala de producción, resulta crítico inspeccionar y filtrar el tráfico entrante antes de que los servidores de la aplicación consuman ciclos de CPU renderizando componentes o ejecutando costosas consultas a la base de datos:
x-vercel-ip-country) con redirección al prefijo de idioma pertinente.Next.js da respuesta a estos requisitos mediante una pasarela especializada: Next.js Middleware. Se trata de código ejecutado en nodos de borde de la CDN (Edge Network) para cada petición HTTP entrante antes de la resolución del enrutador y la comprobación de la caché del servidor.
El archivo middleware.ts se declara estrictamente en la raíz del proyecto (al mismo nivel que app o src).
El rasgo arquitectónico primordial de Middleware en Next.js es su ejecución en el Edge Runtime. Es una sandbox basada en V8 con acceso a APIs web estándar (fetch, Request, Response, Web Crypto, Headers), pero sin librerías nativas del sistema operativo de Node.js (fs, child_process, sockets TCP).
Al operar en el perímetro de la red, Middleware resuelve solicitudes en menos de 10 ms, descartando peticiones no autorizadas antes de que alcancen el centro de datos principal.
La petición HTTP entrante es interceptada en el nodo CDN de borde geográficamente más cercano al usuario.
El motor valida la ruta frente a la expresión regular de config.matcher, descartando archivos estáticos e imágenes.
Validación inmediata del token JWT o cookie de sesión mediante Web Crypto API sin consultar la base de datos.
Generación de nonce criptográfico para CSP y asignación de cabeceras x-user-id en NextResponse.next().
Decisión de despacho: permitir paso (NextResponse.next), redirección (redirect) o reescritura interna de ruta (rewrite).
¿Cuál es la diferencia fundamental entre NextResponse.redirect() y NextResponse.rewrite()?
El archivo middleware.ts exporta una función middleware y un objeto de configuración config.matcher:
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('session_token')?.value;
// Guardia de autenticación para secciones privadas
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
const loginUrl = new URL('/login', request.url);
loginUrl.searchParams.set('from', request.nextUrl.pathname);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
}
// Configuración de rutas donde debe intervenir el Middleware
export const config = {
matcher: [
/*
* Excluir recursos internos y archivos estáticos para optimizar el rendimiento:
* - api (salvo que se requiera middleware global explícito)
* - _next/static (archivos estáticos JS/CSS)
* - _next/image (imágenes optimizadas)
* - favicon.ico, sitemap.xml, robots.txt
*/
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
};Next.js permite inyectar cabeceras adicionales en la solicitud (por ejemplo, identificadores de usuario o valores nonce para CSP):
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Clonar las cabeceras de la petición entrante
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-gateway-timestamp', Date.now().toString());
requestHeaders.set('x-client-ip', request.ip ?? 'unknown');
// Propagar las cabeceras modificadas a los Server Components
return NextResponse.next({
request: {
headers: requestHeaders,
},
});
}¿Por qué se desaconseja categóricamente ejecutar consultas pesadas a bases de datos relacionales con Prisma o pg en Next.js Middleware?
Patrón estructural para plataformas SaaS donde el usuario accede a company.platform.com y el servidor redirige de forma transparente la solicitud hacia app/tenants/[tenant]/page.tsx:
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const hostname = request.headers.get('host') ?? '';
const pathname = request.nextUrl.pathname;
// Extraer el subdominio del inquilino (por ejemplo, acme.platform.com -> acme)
const currentHost = hostname.replace(`.${process.env.NEXT_PUBLIC_ROOT_DOMAIN}`, '');
// Si se trata de un subdominio de cliente, reescribir internamente
if (currentHost && currentHost !== 'www' && currentHost !== 'localhost:3000') {
return NextResponse.rewrite(
new URL(`/tenants/${currentHost}${pathname}`, request.url)
);
}
return NextResponse.next();
}Si en la lógica del Middleware se redirige a los usuarios no autenticados a /login, pero la propia ruta /login no está excluida de config.matcher, el Middleware se interceptará a sí mismo infinitamente hasta emitir el error ERR_TOO_MANY_REDIRECTS.
Solución: Comprueba siempre que la ruta solicitada no coincida ya con el destino de redirección: if (!token && !request.nextUrl.pathname.startsWith('/login')).
Un error habitual es mutar request.headers.set(...) y posteriormente retornar un simple NextResponse.next(). En dicho escenario, las modificaciones se descartan y nunca alcanzan a los Server Components.
Solución: Envía siempre el objeto request: { headers: requestHeaders } como argumento dentro de NextResponse.next().