Redireccionamiento

Existen varias formas de manejar redirecciones en Next.js. Esta página cubrirá cada opción disponible, casos de uso y cómo gestionar grandes cantidades de redirecciones.

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

Hook useRouter()

Si necesita redirigir dentro de un componente, puede 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')}>
      Dashboard
    </button>
  )
}
import { useRouter } from 'next/router'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

Nota importante:

  • Si no necesita navegar programáticamente a un usuario, debería usar un componente <Link>.

Consulte la referencia de API de 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 cambia la estructura de URL de páginas o tiene una lista de redirecciones conocidas de antemano.

redirects soporta coincidencia de rutas, cabeceras, cookies y consultas, brindando flexibilidad para redirigir usuarios basado en solicitudes entrantes.

Para usar redirects, agregue la opción a su archivo next.config.js:

next.config.js
module.exports = {
  async redirects() {
    return [
      // Redirección básica
      {
        source: '/about',
        destination: '/',
        permanent: true,
      },
      // Coincidencia de ruta con comodín
      {
        source: '/blog/:slug',
        destination: '/news/:slug',
        permanent: true,
      },
    ]
  },
}

Consulte la referencia de API de redirects para más información.

Nota importante:

  • redirects puede retornar códigos 307 (Redirección Temporal) o 308 (Redirección Permanente) con la opción permanent.
  • redirects puede tener límites en plataformas. Por ejemplo, en Vercel hay un límite de 1,024 redirecciones. Para manejar grandes cantidades (1000+), considere crear una solución personalizada con Middleware. Vea gestión de redirecciones a escala para más.
  • redirects se ejecuta antes que Middleware.

NextResponse.redirect en Middleware

Middleware permite ejecutar código antes de completar una solicitud. Luego, basado en la solicitud entrante, redirigir a otra URL usando NextResponse.redirect. Esto es útil para redirigir usuarios basado en condiciones (ej. autenticación, gestión de sesión) o tener grandes cantidades de redirecciones.

Por ejemplo, para redirigir al usuario a /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 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 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.

Consulte la documentación de Middleware para más información.

Gestión de redirecciones a escala (avanzado)

Para manejar grandes cantidades de redirecciones (1000+), puede considerar crear una solución personalizada con Middleware. Esto permite gestionar redirecciones programáticamente sin necesidad de redeployar su aplicación.

Para esto, necesitará considerar:

  1. Crear y almacenar un mapa de redirecciones.
  2. Optimizar el rendimiento de búsqueda de datos.

Ejemplo Next.js: Vea nuestro ejemplo 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 puede almacenar en una base de datos (usualmente clave-valor) o archivo JSON.

Considere la siguiente estructura de datos:

{
  "/old": {
    "destination": "/new",
    "permanent": true
  },
  "/blog/post-old": {
    "destination": "/blog/post-new",
    "permanent": true
  }
}

En Middleware, puede 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, como Vercel Edge Config o Redis.
  • Emplear 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, puede importar un archivo generado de filtro Bloom en el Middleware y luego verificar si la ruta de la solicitud entrante existe en el filtro Bloom.

Si existe, reenvíe la solicitud a un Rutas API (API Routes) que verificará el archivo real y redirigirá al usuario a la URL correspondiente. 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 desde el 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 con 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 desde el 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 con la solicitud sin redirigir
  return NextResponse.next()
}

Luego, en la Ruta API:

import { 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 desde el 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)
}
import redirects from '@/app/redirects/redirects.json'

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

  // Obtener la entrada de redirección desde el archivo redirects.json
  const redirect = redirects[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)
}

Nota importante:

  • Para generar un filtro Bloom, puede usar una biblioteca como bloom-filters.
  • Debe validar las solicitudes hechas a su Manejador de Ruta para prevenir solicitudes maliciosas.