Default Architecture

Architecture overview

OpenNext does not create the underlying infrastructure. You can create the infrastructure for your app with your preferred tool — SST, AWS CDK, Terraform, Serverless Framework, etc.

This is the recommended setup.

Architecture

We are going to take a look at every component created by OpenNext.

Asset files

OpenNext will create a .open-next/assets folder containing the hashed and un-hashed files. Those files can be directly served without going through the server function. Files in .open-next/assets should be served from the root of your website. For example, the file .open-next/assets/favicon.ico should be served from /favicon.ico.

There are two types of files in the .open-next/assets folder:

Hashed files

These are files with a hash component in the file name. Hashed files are be found in the .open-next/assets/_next folder, such as .open-next/assets/_next/static/css/0275f6d90e7ad339.css. The hash values in the filenames are guaranteed to change when the content of the files is modified. Therefore, hashed files should be cached both at the CDN level and at the browser level. The recommended cache control setting for these file is

public,max-age=31536000,immutable

Un-hashed files

Other files inside the .open-next/assets folder are copied from your app's public/ folder, such as .open-next/assets/favicon.ico. The filename for un-hashed files may remain unchanged when the content is modified. Un-hashed files should be cached at the CDN level, but not at the browser level. When the content of un-hashed files is modified, the CDN cache should be invalidated on deploy. The recommended cache control setting for these file is

public,max-age=0,s-maxage=31536000,must-revalidate

Cache files

OpenNext will create a .open-next/cache folder containing cache data used by the incremental cache(i.e. ISR and SSG routes). These files should not be publicly accessible.

There are two types of caches in the .open-next/cache folder:

  • Route cache: This cache includes html and json or rsc files that are prerendered during the build. They are merged into a single .cache file. They are used to seed the revalidation cache.
  • Fetch cache: This cache includes fetch call responses, which might contain sensitive information. Make sure these files are not publicly accessible.

Image optimization backend

This backend handles image optimization requests when the Next.js <Image> component is used. The sharp (opens in a new tab) library, which is bundled with the function, is used to convert the image. The library is compiled against the selected architecture (by default arm64) and is intended to run on Node.

Note that the image optimization backend responds with the Cache-Control header, so the image will be cached both at the CDN level and at the browser level.

See Image Optimization for more details.

Servers Lambda backend

These backends handles all other types of requests from the Next.js app, including Server-side Rendering (SSR) requests, Incremental Static Regeneration (ISR) requests, Static Site Generation requests (SSG) and API requests. OpenNext builds the Next.js app in standalone mode. The standalone mode generates a .next folder containing the NextServer class that handles requests and a node_modules folder with all the dependencies needed to run the NextServer. The structure looks like this:

  .next/                -> NextServer
  node_modules/         -> dependencies

The server backend adapter wraps around NextServer and exports a handler function that supports the Lambda request and response. The server-function bundle looks like this:

  .next/                -> NextServer
+ .open-next/
  node_modules/         -> dependencies
+ index.mjs             -> server function adapter

Monorepo

In the case of a monorepo, the build output looks slightly different. For example, if the app is located in packages/web, the build output looks like this:

  packages/
    web/
      .next/            -> NextServer
      node_modules/     -> dependencies from root node_modules (optional)
  node_modules/         -> dependencies from package node_modules

In this case, the server function adapter needs to be created inside packages/web next to .next/. This is to ensure that the adapter can import dependencies from both node_modules folders. It is not a good practice to have the Lambda configuration coupled with the project structure, so instead of setting the Lambda handler to packages/web/index.mjs, we will add a wrapper index.mjs at the server-function bundle root that re-exports the adapter. The resulting structure looks like this:

  packages/
    web/
      .next/                -> NextServer
+     .open-next/
      node_modules/          -> dependencies from root node_modules (optional)
+     index.mjs              -> server function adapter
  node_modules/              -> dependencies from package node_modules
+ index.mjs                  -> adapter wrapper

This ensures that the function handler remains at index.mjs.

Revalidation backend

OpenNext will create a .open-next/revalidation-function folder containing the revalidation backend.

This backend is supposed to handle revalidation requests from the revalidation queue. The revalidation queue is a FIFO queue that contains messages for revalidating routes. The revalidation backend polls the queue for messages and sends a HEAD request to the specified route for revalidation.

Warmer backend

OpenNext will create a .open-next/warmer-function folder containing the warmer backend.

Read more on how warming works.

Tag Provider backend

This is used to populate the revalidation table with tags.