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, redireccionando, modificando los encabezados de solicitud o respuesta, o respondiendo directamente.
Middleware se ejecuta antes de que se empareje el contenido en caché y las rutas. Consulte Coincidencia de rutas para más detalles.
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.
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*',
}
import { NextResponse } from 'next/server'
// Esta función puede marcarse como `async` si se usa `await` dentro
export function middleware(request) {
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. El siguiente es el orden de ejecución:
headers
denext.config.js
redirects
denext.config.js
- Middleware (
rewrites
,redirects
, etc.) beforeFiles
(rewrites
) denext.config.js
- Rutas del sistema de archivos (
public/
,_next/static/
,pages/
,app/
, etc.) afterFiles
(rewrites
) denext.config.js
- Rutas dinámicas (
/blog/[slug]
) fallback
(rewrites
) denext.config.js
Hay dos formas de definir en qué rutas se ejecutará Middleware:
Matcher
matcher
le permite filtrar Middleware para que se ejecute en rutas específicas.
export const config = {
matcher: '/about/:path*',
}
Puede coincidir con una sola ruta o múltiples rutas con sintaxis de array:
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 coincidir con todo excepto rutas específicas:
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 (archivo favicon)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}
Nota importante: Los valores de
matcher
deben ser constantes para que puedan analizarse estáticamente en el momento de compilación. Los valores dinámicos como variables serán ignorados.
Matchers configurados:
- DEBEN comenzar con
/
- Pueden incluir parámetros con nombre:
/about/:path
coincide con/about/a
y/about/b
pero no con/about/a/c
- Pueden tener modificadores en parámetros con nombre (comenzando con
:
):/about/:path*
coincide con/about/a/b/c
porque*
es cero o más.?
es cero o uno y+
uno o más - 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.
Nota importante: 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))
}
}
import { NextResponse } from 'next/server'
export function middleware(request) {
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 diferenterewrite
la respuesta mostrando una URL dada- Establecer encabezados de solicitud para Rutas API,
getServerSideProps
y destinos derewrite
- Establecer cookies de respuesta
- Establecer encabezados de respuesta
Para producir una respuesta desde Middleware, puede:
rewrite
a una ruta (Página o Ruta API Edge) que produzca una respuesta- devolver una
NextResponse
directamente. Consulte Produciendo una respuesta
Usando 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
.
- Para solicitudes entrantes,
cookies
viene con los siguientes métodos:get
,getAll
,set
ydelete
cookies. Puede verificar la existencia de una cookie conhas
o eliminar todas las cookies conclear
. - Para respuestas salientes,
cookies
tienen los siguientes métodosget
,getAll
,set
ydelete
.
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=/test`.
return response
}
import { NextResponse } from 'next/server'
export function middleware(request) {
// 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=/test`.
return response
}
Estableciendo 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) {
// Clonar los encabezados de solicitud y establecer 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.rewrite
const response = NextResponse.next({
request: {
// Nuevos encabezados de solicitud
headers: requestHeaders,
},
})
// Establecer un nuevo encabezado de respuesta `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
import { NextResponse } from 'next/server'
export function middleware(request) {
// Clonar los encabezados de solicitud y establecer 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.rewrite
const response = NextResponse.next({
request: {
// Nuevos encabezados de solicitud
headers: requestHeaders,
},
})
// Establecer un nuevo encabezado de respuesta `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
Nota importante: 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.
Produciendo una respuesta
Puede responder desde Middleware directamente devolviendo una instancia de Response
o NextResponse
. (Esto está disponible desde Next.js v13.1.0)
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Limitar el middleware a rutas que comienzan 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 }
)
}
}
import { isAuthenticated } from '@lib/auth'
// Limitar el middleware a rutas que comienzan con `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request) {
// 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 }
)
}
}
Banderas avanzadas de Middleware
En v13.1
de Next.js se introdujeron dos banderas adicionales para middleware, skipMiddlewareUrlNormalize
y skipTrailingSlashRedirect
para manejar casos de uso avanzados.
skipTrailingSlashRedirect
permite deshabilitar las redirecciones predeterminadas de Next.js para agregar o eliminar barras diagonales finales, lo que permite un manejo personalizado dentro del middleware que puede mantener la barra diagonal final para algunas rutas pero no para otras, permitiendo migraciones incrementales más fáciles.
module.exports = {
skipTrailingSlashRedirect: true,
}
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 diagonal final
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
req.nextUrl.pathname += '/'
return NextResponse.redirect(req.nextUrl)
}
}
skipMiddlewareUrlNormalize
permite deshabilitar la normalización de URL que hace Next.js para hacer que el manejo de visitas directas y transiciones del cliente sea el mismo. Hay algunos casos avanzados donde necesita control total usando la URL original que esto desbloquea.
module.exports = {
skipMiddlewareUrlNormalize: true,
}
export default async function middleware(req) {
const { pathname } = req.nextUrl
// GET /_next/data/build-id/hello.json
console.log(pathname)
// con la bandera esto ahora es /_next/data/build-id/hello.json
// sin la bandera esto se normalizaría a /hello
}
Historial de versiones
Versión | Cambios |
---|---|
v13.1.0 | Se agregaron banderas avanzadas de Middleware |
v13.0.0 | Middleware puede modificar encabezados de solicitud, encabezados de respuesta y enviar respuestas |
v12.2.0 | Middleware es estable, consulte la guía de actualización |
v12.0.9 | Se aplicaron URLs absolutas en Edge Runtime (PR) |
v12.0.0 | Se agregó Middleware (Beta) |