Introducción/Guías/Redirecciones

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.

APIPropósitoDóndeCódigo de Estado
useRouterRealizar una navegación del lado del clienteComponentesN/A
redirects en next.config.jsRedirigir una solicitud entrante basada en una rutaArchivo next.config.js307 (Temporal) o 308 (Permanente)
NextResponse.redirectRedirigir una solicitud entrante basada en una condiciónMiddlewareCualquiera

Hook useRouter()

Si necesitas redirigir dentro de un componente, puedes usar el método push del hook useRouter. Por ejemplo:

import { useRouter } from 'next/router'

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

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ón permanent.
  • 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*',
}

Nota importante:

  • Middleware se ejecuta después de redirects en next.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:

  1. Crear y almacenar un mapa de redirecciones.
  2. 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()
}

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 Rutas API (API Routes) 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()
}

Luego, en la Ruta API (API Route):

import type { NextApiRequest, NextApiResponse } from 'next'
import redirects from '@/app/redirects/redirects.json'

type RedirectEntry = {
  destination: string
  permanent: boolean
}

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const pathname = req.query.pathname
  if (!pathname) {
    return res.status(400).json({ message: 'Solicitud incorrecta' })
  }

  // 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 res.status(400).json({ message: 'No hay redirección' })
  }

  // Devolver la entrada de redirección
  return res.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.

On this page