Introducción/Guías/Autoalojamiento

Cómo autoalojar tu aplicación Next.js

Cuando despliegues tu aplicación Next.js, es posible que desees configurar cómo se manejan diferentes características según tu infraestructura.

🎥 Ver: Aprende más sobre autoalojar Next.js → YouTube (45 minutos).

Optimización de imágenes

La Optimización de imágenes a través de next/image funciona con autoalojamiento sin configuración cuando se despliega usando next start. Si prefieres tener un servicio separado para optimizar imágenes, puedes configurar un cargador de imágenes.

La Optimización de imágenes puede usarse con una exportación estática definiendo un cargador de imágenes personalizado en next.config.js. Ten en cuenta que las imágenes se optimizan en tiempo de ejecución, no durante la construcción.

Es bueno saber:

Middleware

El Middleware funciona con autoalojamiento sin configuración cuando se despliega usando next start. Como requiere acceso a la solicitud entrante, no es compatible cuando se usa una exportación estática.

El Middleware usa el runtime Edge, un subconjunto de todas las APIs de Node.js disponibles para ayudar a garantizar baja latencia, ya que puede ejecutarse antes de cada ruta o recurso en tu aplicación. Si no deseas esto, puedes usar el runtime completo de Node.js para ejecutar Middleware.

Si buscas añadir lógica (o usar un paquete externo) que requiera todas las APIs de Node.js, podrías mover esta lógica a un layout como un Componente de Servidor. Por ejemplo, verificando headers y redireccionando. También puedes usar headers, cookies o parámetros de consulta para redireccionar o reescribir a través de next.config.js. Si eso no funciona, también puedes usar un servidor personalizado.

Variables de entorno

Next.js puede soportar variables de entorno tanto en tiempo de construcción como en tiempo de ejecución.

Por defecto, las variables de entorno solo están disponibles en el servidor. Para exponer una variable de entorno al navegador, debe tener el prefijo NEXT_PUBLIC_. Sin embargo, estas variables públicas se incluirán en el paquete JavaScript durante next build.

Puedes leer variables de entorno de forma segura en el servidor durante el renderizado dinámico.

import { connection } from 'next/server'

export default async function Component() {
  await connection()
  // cookies, headers y otras APIs dinámicas
  // también optarán por renderizado dinámico, lo que significa
  // que esta variable de entorno se evalúa en tiempo de ejecución
  const value = process.env.MY_VALUE
  // ...
}
import { connection } from 'next/server'

export default async function Component() {
  await connection()
  // cookies, headers y otras APIs dinámicas
  // también optarán por renderizado dinámico, lo que significa
  // que esta variable de entorno se evalúa en tiempo de ejecución
  const value = process.env.MY_VALUE
  // ...
}

Esto te permite usar una única imagen Docker que puede promoverse a través de múltiples entornos con diferentes valores.

Es bueno saber:

Caché e ISR

Next.js puede almacenar en caché respuestas, páginas estáticas generadas, salidas de construcción y otros recursos estáticos como imágenes, fuentes y scripts.

El almacenamiento en caché y la revalidación de páginas (con Regeneración Estática Incremental) usan la misma caché compartida. Por defecto, esta caché se almacena en el sistema de archivos (en disco) en tu servidor Next.js. Esto funciona automáticamente con autoalojamiento usando tanto el Pages Router como el App Router.

Puedes configurar la ubicación de la caché de Next.js si deseas persistir páginas y datos en caché en almacenamiento duradero, o compartir la caché entre múltiples contenedores o instancias de tu aplicación Next.js.

Caché automática

  • Next.js establece el header Cache-Control como public, max-age=31536000, immutable para recursos verdaderamente inmutables. No puede ser sobrescrito. Estos archivos inmutables contienen un hash SHA en el nombre del archivo, por lo que pueden almacenarse en caché indefinidamente. Por ejemplo, Importaciones de imágenes estáticas. Puedes configurar el TTL para imágenes.
  • La Regeneración Estática Incremental (ISR) establece el header Cache-Control como s-maxage: <revalidate in getStaticProps>, stale-while-revalidate. Este tiempo de revalidación se define en tu función getStaticProps en segundos. Si estableces revalidate: false, por defecto tendrá una duración de caché de un año.
  • Las páginas renderizadas dinámicamente establecen un header Cache-Control como private, no-cache, no-store, max-age=0, must-revalidate para evitar que se almacenen en caché datos específicos del usuario. Esto aplica tanto al App Router como al Pages Router. Esto también incluye Modo Borrador.

Recursos estáticos

Si deseas alojar recursos estáticos en un dominio o CDN diferente, puedes usar la configuración assetPrefix en next.config.js. Next.js usará este prefijo de recurso al recuperar archivos JavaScript o CSS. Separar tus recursos a un dominio diferente tiene la desventaja de tiempo adicional en la resolución DNS y TLS.

Aprende más sobre assetPrefix.

Configuración de la caché

Por defecto, los recursos de caché generados se almacenarán en memoria (por defecto 50mb) y en disco. Si estás alojando Next.js usando una plataforma de orquestación de contenedores como Kubernetes, cada pod tendrá una copia de la caché. Para evitar que se muestren datos obsoletos ya que la caché no se comparte entre pods por defecto, puedes configurar la caché de Next.js para proporcionar un manejador de caché y desactivar el almacenamiento en memoria.

Para configurar la ubicación de la caché ISR/Data cuando autoalojas, puedes configurar un manejador personalizado en tu archivo next.config.js:

next.config.js
module.exports = {
  cacheHandler: require.resolve('./cache-handler.js'),
  cacheMaxMemorySize: 0, // desactivar caché en memoria por defecto
}

Luego, crea cache-handler.js en la raíz de tu proyecto, por ejemplo:

cache-handler.js
const cache = new Map()

module.exports = class CacheHandler {
  constructor(options) {
    this.options = options
  }

  async get(key) {
    // Esto podría almacenarse en cualquier lugar, como almacenamiento duradero
    return cache.get(key)
  }

  async set(key, data, ctx) {
    // Esto podría almacenarse en cualquier lugar, como almacenamiento duradero
    cache.set(key, {
      value: data,
      lastModified: Date.now(),
      tags: ctx.tags,
    })
  }

  async revalidateTag(tags) {
    // tags es un string o un array de strings
    tags = [tags].flat()
    // Iterar sobre todas las entradas en la caché
    for (let [key, value] of cache) {
      // Si los tags del valor incluyen el tag especificado, eliminar esta entrada
      if (value.tags.some((tag) => tags.includes(tag))) {
        cache.delete(key)
      }
    }
  }

  // Si deseas tener caché temporal en memoria para una sola solicitud que se reinicia
  // antes de la siguiente solicitud, puedes aprovechar este método
  resetRequestCache() {}
}

Usar un manejador de caché personalizado te permitirá garantizar consistencia entre todos los pods que alojan tu aplicación Next.js. Por ejemplo, puedes guardar los valores en caché en cualquier lugar, como Redis o AWS S3.

Es bueno saber:

  • revalidatePath es una capa de conveniencia sobre los tags de caché. Llamar a revalidatePath llamará a la función revalidateTag con un tag especial por defecto para la página proporcionada.

Caché de construcción

Next.js genera un ID durante next build para identificar qué versión de tu aplicación se está sirviendo. La misma construcción debe usarse e iniciarse en múltiples contenedores.

Si estás reconstruyendo para cada etapa de tu entorno, necesitarás generar un ID de construcción consistente para usar entre contenedores. Usa el comando generateBuildId en next.config.js:

next.config.js
module.exports = {
  generateBuildId: async () => {
    // Esto podría ser cualquier cosa, usando el último hash git
    return process.env.GIT_HASH
  },
}

Desfase de versión

Next.js mitigará automáticamente la mayoría de los casos de desfase de versión y recargará automáticamente la aplicación para recuperar nuevos recursos cuando se detecte. Por ejemplo, si hay una discrepancia en el deploymentId, las transiciones entre páginas realizarán una navegación dura versus usar un valor precargado.

Cuando la aplicación se recarga, puede haber una pérdida del estado de la aplicación si no está diseñado para persistir entre navegaciones de página. Por ejemplo, usar el estado de la URL o almacenamiento local persistiría el estado después de una recarga de página. Sin embargo, el estado del componente como useState se perdería en tales navegaciones.

Streaming y Suspense

El App Router de Next.js soporta respuestas en streaming con autoalojamiento. Si estás usando Nginx o un proxy similar, necesitarás configurarlo para desactivar el buffering y permitir streaming.

Por ejemplo, puedes desactivar buffering en Nginx estableciendo X-Accel-Buffering como no:

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*{/}?',
        headers: [
          {
            key: 'X-Accel-Buffering',
            value: 'no',
          },
        ],
      },
    ]
  },
}

Prerenderizado parcial

Prerenderizado parcial (experimental) funciona por defecto con Next.js y no es una característica exclusiva de CDN. Esto incluye despliegue como servidor Node.js (a través de next start) y cuando se usa con un contenedor Docker.

Uso con CDNs

Cuando usas un CDN delante de tu aplicación Next.js, la página incluirá el header de respuesta Cache-Control: private cuando se accede a APIs dinámicas. Esto asegura que la página HTML resultante se marque como no almacenable en caché. Si la página está completamente prerenderizada a estático, incluirá Cache-Control: public para permitir que la página se almacene en caché en el CDN.

Si no necesitas una mezcla de componentes estáticos y dinámicos, puedes hacer que toda tu ruta sea estática y almacenar en caché el HTML resultante en un CDN. Esta Optimización Estática Automática es el comportamiento por defecto al ejecutar next build si no se usan APIs dinámicas.

A medida que el Prerenderizado parcial se estabilice, proporcionaremos soporte a través de la API de Deployment Adapters.

after

after es totalmente compatible con autoalojamiento usando next start.

Al detener el servidor, asegura un apagado elegante enviando señales SIGINT o SIGTERM y esperando. Esto permite que el servidor Next.js espere hasta que las funciones de callback pendientes o promesas usadas dentro de after hayan terminado.