Cómo manejar redirecciones en Next.js
Existen varias formas de manejar redirecciones en Next.js. Esta página cubrirá cada opción disponible, casos de uso y cómo gestionar un gran número de redirecciones.
API | Propósito | Dónde | Código de Estado |
---|---|---|---|
redirect | Redirigir al usuario después de una mutación o evento | Componentes del Servidor, Acciones del Servidor, Manejadores de Ruta | 307 (Temporal) o 303 (Acción del Servidor) |
permanentRedirect | Redirigir al usuario después de una mutación o evento | Componentes del Servidor, Acciones del Servidor, Manejadores de Ruta | 308 (Permanente) |
useRouter | Realizar una navegación del lado del cliente | Manejadores de Eventos en Componentes del Cliente | N/A |
redirects en next.config.js | Redirigir una solicitud entrante basada en una ruta | Archivo next.config.js | 307 (Temporal) o 308 (Permanente) |
NextResponse.redirect | Redirigir una solicitud entrante basada en una condición | Middleware | Cualquiera |
Función redirect
La función redirect
permite redirigir al usuario a otra URL. Puedes llamar a redirect
en Componentes del Servidor, Manejadores de Ruta y Acciones del Servidor.
redirect
se usa frecuentemente después de una mutación o evento. Por ejemplo, al crear una publicación:
'use server'
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
export async function createPost(id: string) {
try {
// Llamar a la base de datos
} catch (error) {
// Manejar errores
}
revalidatePath('/posts') // Actualizar publicaciones en caché
redirect(`/post/${id}`) // Navegar a la página de la nueva publicación
}
'use server'
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
export async function createPost(id) {
try {
// Llamar a la base de datos
} catch (error) {
// Manejar errores
}
revalidatePath('/posts') // Actualizar publicaciones en caché
redirect(`/post/${id}`) // Navegar a la página de la nueva publicación
}
Nota importante:
redirect
devuelve por defecto un código de estado 307 (Redirección Temporal). Cuando se usa en una Acción del Servidor, devuelve un 303 (Ver Otro), que se usa comúnmente para redirigir a una página de éxito como resultado de una solicitud POST.redirect
internamente lanza un error, por lo que debe llamarse fuera de bloquestry/catch
.redirect
puede llamarse en Componentes del Cliente durante el proceso de renderizado pero no en manejadores de eventos. Puedes usar el hookuseRouter
en su lugar.redirect
también acepta URLs absolutas y puede usarse para redirigir a enlaces externos.- Si deseas redirigir antes del proceso de renderizado, usa
next.config.js
o Middleware.
Consulta la referencia de la API redirect
para más información.
Función permanentRedirect
La función permanentRedirect
permite redirigir permanentemente al usuario a otra URL. Puedes llamar a permanentRedirect
en Componentes del Servidor, Manejadores de Ruta y Acciones del Servidor.
permanentRedirect
se usa frecuentemente después de una mutación o evento que cambia la URL canónica de una entidad, como actualizar la URL del perfil de un usuario después de cambiar su nombre de usuario:
'use server'
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export async function updateUsername(username: string, formData: FormData) {
try {
// Llamar a la base de datos
} catch (error) {
// Manejar errores
}
revalidateTag('username') // Actualizar todas las referencias al nombre de usuario
permanentRedirect(`/profile/${username}`) // Navegar al nuevo perfil de usuario
}
'use server'
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export async function updateUsername(username, formData) {
try {
// Llamar a la base de datos
} catch (error) {
// Manejar errores
}
revalidateTag('username') // Actualizar todas las referencias al nombre de usuario
permanentRedirect(`/profile/${username}`) // Navegar al nuevo perfil de usuario
}
Nota importante:
permanentRedirect
devuelve por defecto un código de estado 308 (Redirección Permanente).permanentRedirect
también acepta URLs absolutas y puede usarse para redirigir a enlaces externos.- Si deseas redirigir antes del proceso de renderizado, usa
next.config.js
o Middleware.
Consulta la referencia de la API permanentRedirect
para más información.
Hook useRouter()
Si necesitas redirigir dentro de un manejador de eventos en un Componente del Cliente, puedes usar el método push
del hook useRouter
. Por ejemplo:
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Panel
</button>
)
}
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Panel
</button>
)
}
Nota importante:
- Si no necesitas navegar programáticamente a un usuario, deberías usar un componente
<Link>
.
Consulta la referencia de la API useRouter
para más información.
redirects
en next.config.js
La opción redirects
en el archivo next.config.js
permite redirigir una ruta de solicitud entrante a un destino diferente. Esto es útil cuando cambias la estructura de URLs de las páginas o tienes una lista de redirecciones conocidas de antemano.
redirects
soporta coincidencia de rutas, coincidencia de encabezados, cookies y consultas, dándote flexibilidad para redirigir usuarios basado en una solicitud entrante.
Para usar redirects
, añade la opción a tu archivo next.config.js
:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
async redirects() {
return [
// Redirección básica
{
source: '/about',
destination: '/',
permanent: true,
},
// Coincidencia de rutas con comodines
{
source: '/blog/:slug',
destination: '/news/:slug',
permanent: true,
},
]
},
}
export default nextConfig
module.exports = {
async redirects() {
return [
// Redirección básica
{
source: '/about',
destination: '/',
permanent: true,
},
// Coincidencia de rutas con comodines
{
source: '/blog/:slug',
destination: '/news/:slug',
permanent: true,
},
]
},
}
Consulta la referencia de la API redirects
para más información.
Nota importante:
redirects
puede devolver un código de estado 307 (Redirección Temporal) o 308 (Redirección Permanente) con la opciónpermanent
.redirects
puede tener un límite en algunas plataformas. Por ejemplo, en Vercel hay un límite de 1,024 redirecciones. Para manejar un gran número de redirecciones (1000+), considera crear una solución personalizada usando Middleware. Consulta manejo de redirecciones a escala para más información.redirects
se ejecuta antes que Middleware.
NextResponse.redirect
en Middleware
Middleware te permite ejecutar código antes de que se complete una solicitud. Luego, basado en la solicitud entrante, redirigir a una URL diferente usando NextResponse.redirect
. Esto es útil si quieres redirigir usuarios basado en una condición (ej. autenticación, gestión de sesiones, etc) o tener un gran número de redirecciones.
Por ejemplo, para redirigir al usuario a una página /login
si no está autenticado:
import { NextResponse, NextRequest } from 'next/server'
import { authenticate } from 'auth-provider'
export function middleware(request: NextRequest) {
const isAuthenticated = authenticate(request)
// Si el usuario está autenticado, continuar normalmente
if (isAuthenticated) {
return NextResponse.next()
}
// Redirigir a la página de login si no está autenticado
return NextResponse.redirect(new URL('/login', request.url))
}
export const config = {
matcher: '/dashboard/:path*',
}
import { NextResponse } from 'next/server'
import { authenticate } from 'auth-provider'
export function middleware(request) {
const isAuthenticated = authenticate(request)
// Si el usuario está autenticado, continuar normalmente
if (isAuthenticated) {
return NextResponse.next()
}
// Redirigir a la página de login si no está autenticado
return NextResponse.redirect(new URL('/login', request.url))
}
export const config = {
matcher: '/dashboard/:path*',
}
Nota importante:
- Middleware se ejecuta después de
redirects
ennext.config.js
y antes del renderizado.
Consulta la documentación de Middleware para más información.
Manejo de redirecciones a escala (avanzado)
Para manejar un gran número de redirecciones (1000+), puedes considerar crear una solución personalizada usando Middleware. Esto te permite manejar redirecciones programáticamente sin tener que redeployar tu aplicación.
Para hacer esto, necesitarás considerar:
- Crear y almacenar un mapa de redirecciones.
- Optimizar el rendimiento de búsqueda de datos.
Ejemplo de Next.js: Consulta nuestro ejemplo de Middleware con filtro Bloom para una implementación de las recomendaciones siguientes.
1. Crear y almacenar un mapa de redirecciones
Un mapa de redirecciones es una lista de redirecciones que puedes almacenar en una base de datos (usualmente un almacén clave-valor) o archivo JSON.
Considera la siguiente estructura de datos:
{
"/old": {
"destination": "/new",
"permanent": true
},
"/blog/post-old": {
"destination": "/blog/post-new",
"permanent": true
}
}
En Middleware, puedes leer desde una base de datos como Edge Config de Vercel o Redis, y redirigir al usuario basado en la solicitud entrante:
import { NextResponse, NextRequest } from 'next/server'
import { get } from '@vercel/edge-config'
type RedirectEntry = {
destination: string
permanent: boolean
}
export async function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname
const redirectData = await get(pathname)
if (redirectData && typeof redirectData === 'string') {
const redirectEntry: RedirectEntry = JSON.parse(redirectData)
const statusCode = redirectEntry.permanent ? 308 : 307
return NextResponse.redirect(redirectEntry.destination, statusCode)
}
// No se encontró redirección, continuar sin redirigir
return NextResponse.next()
}
import { NextResponse } from 'next/server'
import { get } from '@vercel/edge-config'
export async function middleware(request) {
const pathname = request.nextUrl.pathname
const redirectData = await get(pathname)
if (redirectData) {
const redirectEntry = JSON.parse(redirectData)
const statusCode = redirectEntry.permanent ? 308 : 307
return NextResponse.redirect(redirectEntry.destination, statusCode)
}
// No se encontró redirección, continuar sin redirigir
return NextResponse.next()
}
2. Optimización del rendimiento en la búsqueda de datos
Leer un conjunto de datos grande para cada solicitud entrante puede ser lento y costoso. Existen dos formas de optimizar el rendimiento en la búsqueda de datos:
- Utilizar una base de datos optimizada para lecturas rápidas
- Usar una estrategia de búsqueda de datos como un filtro Bloom (Bloom filter) para verificar eficientemente si existe una redirección antes de leer el archivo o base de datos de redirecciones más grande.
Considerando el ejemplo anterior, puedes importar un archivo de filtro Bloom generado en el Middleware y luego verificar si la ruta (pathname) de la solicitud entrante existe en el filtro Bloom.
Si existe, reenvía la solicitud a un Manejador de Ruta (Route Handler) que verificará el archivo real y redirigirá al usuario a la URL adecuada. Esto evita importar un archivo grande de redirecciones en el Middleware, lo cual puede ralentizar cada solicitud entrante.
import { NextResponse, NextRequest } from 'next/server'
import { ScalableBloomFilter } from 'bloom-filters'
import GeneratedBloomFilter from './redirects/bloom-filter.json'
type RedirectEntry = {
destination: string
permanent: boolean
}
// Inicializar el filtro Bloom desde un archivo JSON generado
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any)
export async function middleware(request: NextRequest) {
// Obtener la ruta de la solicitud entrante
const pathname = request.nextUrl.pathname
// Verificar si la ruta está en el filtro Bloom
if (bloomFilter.has(pathname)) {
// Reenviar la ruta al Manejador de Ruta
const api = new URL(
`/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`,
request.nextUrl.origin
)
try {
// Obtener datos de redirección del Manejador de Ruta
const redirectData = await fetch(api)
if (redirectData.ok) {
const redirectEntry: RedirectEntry | undefined =
await redirectData.json()
if (redirectEntry) {
// Determinar el código de estado
const statusCode = redirectEntry.permanent ? 308 : 307
// Redirigir al destino
return NextResponse.redirect(redirectEntry.destination, statusCode)
}
}
} catch (error) {
console.error(error)
}
}
// No se encontró redirección, continuar la solicitud sin redirigir
return NextResponse.next()
}
import { NextResponse } from 'next/server'
import { ScalableBloomFilter } from 'bloom-filters'
import GeneratedBloomFilter from './redirects/bloom-filter.json'
// Inicializar el filtro Bloom desde un archivo JSON generado
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter)
export async function middleware(request) {
// Obtener la ruta de la solicitud entrante
const pathname = request.nextUrl.pathname
// Verificar si la ruta está en el filtro Bloom
if (bloomFilter.has(pathname)) {
// Reenviar la ruta al Manejador de Ruta
const api = new URL(
`/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`,
request.nextUrl.origin
)
try {
// Obtener datos de redirección del Manejador de Ruta
const redirectData = await fetch(api)
if (redirectData.ok) {
const redirectEntry = await redirectData.json()
if (redirectEntry) {
// Determinar el código de estado
const statusCode = redirectEntry.permanent ? 308 : 307
// Redirigir al destino
return NextResponse.redirect(redirectEntry.destination, statusCode)
}
}
} catch (error) {
console.error(error)
}
}
// No se encontró redirección, continuar la solicitud sin redirigir
return NextResponse.next()
}
Luego, en el Manejador de Ruta (Route Handler):
import { NextRequest, NextResponse } from 'next/server'
import redirects from '@/app/redirects/redirects.json'
type RedirectEntry = {
destination: string
permanent: boolean
}
export function GET(request: NextRequest) {
const pathname = request.nextUrl.searchParams.get('pathname')
if (!pathname) {
return new Response('Solicitud incorrecta', { status: 400 })
}
// Obtener la entrada de redirección del archivo redirects.json
const redirect = (redirects as Record<string, RedirectEntry>)[pathname]
// Manejar falsos positivos del filtro Bloom
if (!redirect) {
return new Response('No hay redirección', { status: 400 })
}
// Devolver la entrada de redirección
return NextResponse.json(redirect)
}
import { NextResponse } from 'next/server'
import redirects from '@/app/redirects/redirects.json'
export function GET(request) {
const pathname = request.nextUrl.searchParams.get('pathname')
if (!pathname) {
return new Response('Solicitud incorrecta', { status: 400 })
}
// Obtener la entrada de redirección del archivo redirects.json
const redirect = redirects[pathname]
// Manejar falsos positivos del filtro Bloom
if (!redirect) {
return new Response('No hay redirección', { status: 400 })
}
// Devolver la entrada de redirección
return NextResponse.json(redirect)
}
Dato útil:
- Para generar un filtro Bloom, puedes usar una librería como
bloom-filters
.- Debes validar las solicitudes hechas a tu Manejador de Ruta para prevenir solicitudes maliciosas.