Middleware

Middleware le permite ejecutar código antes de que se complete una solicitud. Luego, según la solicitud entrante, puede modificar la respuesta reescribiendo, redirigiendo, modificando los encabezados de solicitud o respuesta, o respondiendo directamente.

Middleware se ejecuta antes de que se cargue el contenido en caché y se hagan coincidir las rutas. Consulte Coincidencia de Rutas para más detalles.

Casos de Uso

Algunos escenarios comunes donde Middleware es efectivo incluyen:

  • Redirecciones rápidas después de leer partes de la solicitud entrante
  • Reescribir a diferentes páginas basado en pruebas A/B o experimentos
  • Modificar encabezados para todas las páginas o un subconjunto de páginas

Middleware no es adecuado para:

  • Obtención lenta de datos
  • Gestión de sesiones

Convención

Utilice el archivo middleware.ts (o .js) en la raíz de su proyecto para definir Middleware. Por ejemplo, al mismo nivel que pages o app, o dentro de src si es aplicable.

Nota: Aunque solo se admite un archivo middleware.ts por proyecto, aún puede organizar su lógica de middleware de forma modular. Separe las funcionalidades del middleware en archivos .ts o .js separados e impórtelos en su archivo principal middleware.ts. Esto permite una gestión más limpia del middleware específico de ruta, agrupado en middleware.ts para un control centralizado. Al imponer un único archivo de middleware, se simplifica la configuración, se evitan posibles conflictos y se optimiza el rendimiento al evitar múltiples capas de middleware.

Ejemplo

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

// Esta función puede marcarse como `async` si se usa `await` dentro
export function middleware(request: NextRequest) {
  return NextResponse.redirect(new URL('/home', request.url))
}

// Consulte "Coincidencia de Rutas" a continuación para más información
export const config = {
  matcher: '/about/:path*',
}

Coincidencia de Rutas

Middleware se invocará para cada ruta en su proyecto. Dado esto, es crucial usar matchers para apuntar o excluir rutas específicas con precisión. El siguiente es el orden de ejecución:

  1. headers desde next.config.js
  2. redirects desde next.config.js
  3. Middleware (rewrites, redirects, etc.)
  4. beforeFiles (rewrites) desde next.config.js
  5. Rutas del sistema de archivos (public/, _next/static/, pages/, app/, etc.)
  6. afterFiles (rewrites) desde next.config.js
  7. Rutas Dinámicas (/blog/[slug])
  8. fallback (rewrites) desde next.config.js

Hay dos formas de definir en qué rutas se ejecutará Middleware:

  1. Configuración personalizada de matcher
  2. Declaraciones condicionales

Matcher

matcher le permite filtrar Middleware para que se ejecute en rutas específicas.

middleware.js
export const config = {
  matcher: '/about/:path*',
}

Puede hacer coincidir una sola ruta o múltiples rutas con una sintaxis de array:

middleware.js
export const config = {
  matcher: ['/about/:path*', '/dashboard/:path*'],
}

La configuración matcher permite regex completo, por lo que se admite la coincidencia como búsquedas negativas o coincidencia de caracteres. Aquí se puede ver un ejemplo de búsqueda negativa para hacer coincidir todas las rutas excepto las específicas:

middleware.js
export const config = {
  matcher: [
    /*
     * Coincide con todas las rutas de solicitud excepto las que comienzan con:
     * - api (rutas API)
     * - _next/static (archivos estáticos)
     * - _next/image (archivos de optimización de imágenes)
     * - favicon.ico, sitemap.xml, robots.txt (archivos de metadatos)
     */
    '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
  ],
}

También puede omitir Middleware para ciertas solicitudes usando los arrays missing o has, o una combinación de ambos:

middleware.js
export const config = {
  matcher: [
    /*
     * Coincide con todas las rutas de solicitud excepto las que comienzan con:
     * - api (rutas API)
     * - _next/static (archivos estáticos)
     * - _next/image (archivos de optimización de imágenes)
     * - favicon.ico, sitemap.xml, robots.txt (archivos de metadatos)
     */
    {
      source:
        '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
      missing: [
        { type: 'header', key: 'next-router-prefetch' },
        { type: 'header', key: 'purpose', value: 'prefetch' },
      ],
    },

    {
      source:
        '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
      has: [
        { type: 'header', key: 'next-router-prefetch' },
        { type: 'header', key: 'purpose', value: 'prefetch' },
      ],
    },

    {
      source:
        '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
      has: [{ type: 'header', key: 'x-present' }],
      missing: [{ type: 'header', key: 'x-missing', value: 'prefetch' }],
    },
  ],
}

Bueno saber: Los valores de matcher deben ser constantes para que puedan analizarse estáticamente en tiempo de compilación. Los valores dinámicos como variables serán ignorados.

Matchers configurados:

  1. DEBEN comenzar con /
  2. Pueden incluir parámetros con nombre: /about/:path coincide con /about/a y /about/b pero no con /about/a/c
  3. Pueden tener modificadores en parámetros con nombre (que comienzan con :): /about/:path* coincide con /about/a/b/c porque * es cero o más. ? es cero o uno y + uno o más
  4. Pueden usar expresiones regulares entre paréntesis: /about/(.*) es lo mismo que /about/:path*

Lea más detalles en la documentación de path-to-regexp.

Bueno saber: Por compatibilidad con versiones anteriores, Next.js siempre considera /public como /public/index. Por lo tanto, un matcher de /public/:path coincidirá.

Declaraciones Condicionales

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/about')) {
    return NextResponse.rewrite(new URL('/about-2', request.url))
  }

  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.rewrite(new URL('/dashboard/user', request.url))
  }
}

NextResponse

La API NextResponse le permite:

  • redirect la solicitud entrante a una URL diferente
  • rewrite la respuesta mostrando una URL dada
  • Establecer encabezados de solicitud para Rutas API, getServerSideProps y destinos de rewrite
  • Establecer cookies de respuesta
  • Establecer encabezados de respuesta

Para producir una respuesta desde Middleware, puede:

  1. rewrite a una ruta (Página o Manejador de Ruta) que produzca una respuesta
  2. devolver un NextResponse directamente. Consulte Producir una Respuesta

Uso de Cookies

Las cookies son encabezados regulares. En una Request, se almacenan en el encabezado Cookie. En una Response están en el encabezado Set-Cookie. Next.js proporciona una forma conveniente de acceder y manipular estas cookies a través de la extensión cookies en NextRequest y NextResponse.

  1. Para solicitudes entrantes, cookies viene con los siguientes métodos: get, getAll, set y delete cookies. Puede verificar la existencia de una cookie con has o eliminar todas las cookies con clear.
  2. Para respuestas salientes, cookies tiene los siguientes métodos get, getAll, set y delete.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Suponga que hay un encabezado "Cookie:nextjs=fast" en la solicitud entrante
  // Obteniendo cookies de la solicitud usando la API `RequestCookies`
  let cookie = request.cookies.get('nextjs')
  console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
  const allCookies = request.cookies.getAll()
  console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]

  request.cookies.has('nextjs') // => true
  request.cookies.delete('nextjs')
  request.cookies.has('nextjs') // => false

  // Estableciendo cookies en la respuesta usando la API `ResponseCookies`
  const response = NextResponse.next()
  response.cookies.set('vercel', 'fast')
  response.cookies.set({
    name: 'vercel',
    value: 'fast',
    path: '/',
  })
  cookie = response.cookies.get('vercel')
  console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
  // La respuesta saliente tendrá un encabezado `Set-Cookie:vercel=fast;path=/`.

  return response
}

Establecer Encabezados

Puede establecer encabezados de solicitud y respuesta utilizando la API NextResponse (establecer encabezados de solicitud está disponible desde Next.js v13.0.0).

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Clone los encabezados de solicitud y establezca un nuevo encabezado `x-hello-from-middleware1`
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-hello-from-middleware1', 'hello')

  // También puede establecer encabezados de solicitud en NextResponse.next
  const response = NextResponse.next({
    request: {
      // Nuevos encabezados de solicitud
      headers: requestHeaders,
    },
  })

  // Establezca un nuevo encabezado de respuesta `x-hello-from-middleware2`
  response.headers.set('x-hello-from-middleware2', 'hello')
  return response
}

Bueno saber: Evite establecer encabezados grandes ya que podría causar un error 431 Campos de Encabezado de Solicitud Demasiado Grandes dependiendo de la configuración de su servidor web backend.

CORS

Puede establecer encabezados CORS en Middleware para permitir solicitudes entre orígenes, incluyendo solicitudes simples y preflight.

import { NextRequest, NextResponse } from 'next/server'

const allowedOrigins = ['https://acme.com', 'https://my-app.org']

const corsOptions = {
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}

export function middleware(request: NextRequest) {
  // Verifique el origen de la solicitud
  const origin = request.headers.get('origin') ?? ''
  const isAllowedOrigin = allowedOrigins.includes(origin)

  // Maneje solicitudes preflight
  const isPreflight = request.method === 'OPTIONS'

  if (isPreflight) {
    const preflightHeaders = {
      ...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
      ...corsOptions,
    }
    return NextResponse.json({}, { headers: preflightHeaders })
  }

  // Maneje solicitudes simples
  const response = NextResponse.next()

  if (isAllowedOrigin) {
    response.headers.set('Access-Control-Allow-Origin', origin)
  }

  Object.entries(corsOptions).forEach(([key, value]) => {
    response.headers.set(key, value)
  })

  return response
}

export const config = {
  matcher: '/api/:path*',
}

Bueno saber: Puede configurar encabezados CORS para rutas individuales en Manejadores de Ruta.

Producción de una Respuesta

Puede responder directamente desde el Middleware devolviendo una instancia de Response o NextResponse. (Esta funcionalidad está disponible desde Next.js v13.1.0)

import type { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'

// Limitar el middleware a rutas que comiencen con `/api/`
export const config = {
  matcher: '/api/:function*',
}

export function middleware(request: NextRequest) {
  // Llamar a nuestra función de autenticación para verificar la solicitud
  if (!isAuthenticated(request)) {
    // Responder con JSON indicando un mensaje de error
    return Response.json(
      { success: false, message: 'authentication failed' },
      { status: 401 }
    )
  }
}

waitUntil y NextFetchEvent

El objeto NextFetchEvent extiende el objeto nativo FetchEvent e incluye el método waitUntil().

El método waitUntil() toma una promesa como argumento y extiende la vida útil del Middleware hasta que la promesa se resuelva. Esto es útil para realizar trabajo en segundo plano.

middleware.ts
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'

export function middleware(req: NextRequest, event: NextFetchEvent) {
  event.waitUntil(
    fetch('https://my-analytics-platform.com', {
      method: 'POST',
      body: JSON.stringify({ pathname: req.nextUrl.pathname }),
    })
  )

  return NextResponse.next()
}

Banderas Avanzadas de Middleware

En la versión v13.1 de Next.js se introdujeron dos banderas adicionales para middleware: skipMiddlewareUrlNormalize y skipTrailingSlashRedirect para manejar casos de uso avanzados.

skipTrailingSlashRedirect desactiva las redirecciones de Next.js para agregar o eliminar barras finales. Esto permite un manejo personalizado dentro del middleware para mantener la barra final en algunas rutas pero no en otras, lo que puede facilitar migraciones incrementales.

next.config.js
module.exports = {
  skipTrailingSlashRedirect: true,
}
middleware.js
const legacyPrefixes = ['/docs', '/blog']

export default async function middleware(req) {
  const { pathname } = req.nextUrl

  if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
    return NextResponse.next()
  }

  // aplicar manejo de barra final
  if (
    !pathname.endsWith('/') &&
    !pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
  ) {
    return NextResponse.redirect(
      new URL(`${req.nextUrl.pathname}/`, req.nextUrl)
    )
  }
}

skipMiddlewareUrlNormalize permite desactivar la normalización de URL en Next.js para manejar visitas directas y transiciones del cliente de la misma manera. En algunos casos avanzados, esta opción proporciona control total utilizando la URL original.

next.config.js
module.exports = {
  skipMiddlewareUrlNormalize: true,
}
middleware.js
export default async function middleware(req) {
  const { pathname } = req.nextUrl

  // GET /_next/data/build-id/hello.json

  console.log(pathname)
  // con la bandera esto es /_next/data/build-id/hello.json
  // sin la bandera esto se normalizaría a /hello
}

Pruebas Unitarias (experimental)

A partir de Next.js 15.1, el paquete next/experimental/testing/server contiene utilidades para ayudar a probar unitariamente archivos de middleware. Las pruebas unitarias de middleware pueden ayudar a garantizar que solo se ejecute en las rutas deseadas y que la lógica de enrutamiento personalizado funcione como se espera antes de que el código llegue a producción.

La función unstable_doesMiddlewareMatch se puede utilizar para verificar si el middleware se ejecutará para la URL, encabezados y cookies proporcionados.

import { unstable_doesMiddlewareMatch } from 'next/experimental/testing/server'

expect(
  unstable_doesMiddlewareMatch({
    config,
    nextConfig,
    url: '/test',
  })
).toEqual(false)

También se puede probar toda la función de middleware.

import { isRewrite, getRewrittenUrl } from 'next/experimental/testing/server'

const request = new NextRequest('https://nextjs.org/docs')
const response = await middleware(request)
expect(isRewrite(response)).toEqual(true)
expect(getRewrittenUrl(response)).toEqual('https://other-domain.com/docs')
// getRedirectUrl también podría usarse si la respuesta fuera una redirección

Entorno de Ejecución (Runtime)

El middleware utiliza por defecto el entorno de ejecución Edge (Edge runtime). A partir de la versión v15.2 (canary), tenemos soporte experimental para usar el entorno de ejecución de Node.js (Node.js runtime). Para habilitarlo, agregue la bandera a su archivo next.config:

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    nodeMiddleware: true,
  },
}

export default nextConfig

Luego, en su archivo de middleware, establezca el entorno de ejecución como nodejs en el objeto config:

export const config = {
  runtime: 'nodejs',
}

Nota: Esta característica aún no se recomienda para uso en producción. Por lo tanto, Next.js generará un error a menos que esté utilizando la versión next@canary en lugar de la versión estable.

Soporte de Plataformas

Opción de DespliegueSoporte
Servidor Node.js
Contenedor Docker
Exportación estáticaNo
AdaptadoresDepende de la plataforma

Aprenda cómo configurar Middleware al autoalojar Next.js.

Historial de Versiones

VersiónCambios
v15.2.0El middleware ahora puede usar el entorno de ejecución de Node.js (experimental)
v13.1.0Se agregaron banderas avanzadas de middleware
v13.0.0El middleware puede modificar encabezados de solicitud, encabezados de respuesta y enviar respuestas
v12.2.0El middleware es estable, consulte la guía de actualización
v12.0.9Se aplican URLs absolutas en Edge Runtime (PR)
v12.0.0Se agregó Middleware (Beta)