Skip to content

Commit a98e4b3

Browse files
committed
Doc: update README on how 404 pages are handled
1 parent 787c1b2 commit a98e4b3

File tree

1 file changed

+33
-22
lines changed

1 file changed

+33
-22
lines changed

README.md

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -128,40 +128,46 @@ Create a Lambda function using the code in the `.open-next/server-function` fold
128128
This function handles all other types of requests from the Next.js app, including Server-side Rendering (SSR) requests 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:
129129

130130
```
131-
.next/ -> NextServer
132-
node_modules/ -> dependencies
131+
.next/ -> NextServer
132+
node_modules/ -> dependencies
133133
```
134134

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

137137
```diff
138-
.next/ -> NextServer
139-
node_modules/ -> dependencies
140-
+ index.mjs -> server function adapter
138+
.next/ -> NextServer
139+
+ .open-next/
140+
+ public-files.json -> `/public` file listing
141+
node_modules/ -> dependencies
142+
+ index.mjs -> server function adapter
141143
```
142144

145+
The file `public-files.json` contains the top-level file and directory names in your app's `public/` folder. At runtime, the server function will forward any requests made to these files and directories to S3. And S3 will serve them directly. [See why.](#workaround-public-static-files-served-out-by-server-function-aws-specific)
146+
143147
**Monorepo**
144148

145149
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:
146150

147151
```
148152
packages/
149153
web/
150-
.next/ -> NextServer
151-
node_modules/ -> dependencies from root node_modules (optional)
152-
node_modules/ -> dependencies from package node_modules
154+
.next/ -> NextServer
155+
node_modules/ -> dependencies from root node_modules (optional)
156+
node_modules/ -> dependencies from package node_modules
153157
```
154158

155159
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:
156160

157161
```diff
158162
packages/
159163
web/
160-
.next/ -> NextServer
161-
node_modules/ -> dependencies from root node_modules (optional)
162-
+ index.mjs -> server function adapter
163-
node_modules/ -> dependencies from package node_modules
164-
+ index.mjs -> adapter wrapper
164+
.next/ -> NextServer
165+
+ .open-next/
166+
+ public-files.json -> `/public` file listing
167+
node_modules/ -> dependencies from root node_modules (optional)
168+
+ index.mjs -> server function adapter
169+
node_modules/ -> dependencies from package node_modules
170+
+ index.mjs -> adapter wrapper
165171
```
166172

167173
This ensures that the Lambda handler remains at `index.mjs`.
@@ -170,13 +176,13 @@ This ensures that the Lambda handler remains at `index.mjs`.
170176

171177
Create a CloudFront distribution, and dispatch requests to their corresponding handlers (behaviors). The following behaviors are configured:
172178

173-
| Behavior | Requests | Origin |
174-
| ----------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
175-
| `/_next/static/*` | Hashed static files | S3 bucket |
176-
| `/_next/image` | Image optimization | image optimization function |
177-
| `/_next/data/*` | data requests | server function |
178-
| `/api/*` | API | server function |
179-
| `/*` | catch all | server function<br />fallback to S3 bucket<br />[see why](#workaround-public-static-files-served-out-by-server-function-aws-specific) |
179+
| Behavior | Requests | Origin |
180+
| ----------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
181+
| `/_next/static/*` | Hashed static files | S3 bucket |
182+
| `/_next/image` | Image optimization | image optimization function |
183+
| `/_next/data/*` | data requests | server function |
184+
| `/api/*` | API | server function |
185+
| `/*` | catch all | server function fallback to<br />S3 bucket on 503<br />[see why](#workaround-public-static-files-served-out-by-server-function-aws-specific) |
180186

181187
#### Running at edge
182188

@@ -204,9 +210,14 @@ https://my-nextjs-app.com/favicon.ico
204210

205211
This requires the CloudFront distribution to have the behavior `/favicon.ico` and set the S3 bucket as the origin. However, CloudFront has a [default limit of 25 behaviors per distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-web-distributions), so it is not a scalable solution to create one behavior per file.
206212

207-
To work around the issue, requests for `public/` files are handled by the catch all behavior `/*`. The behavior sends the request to the server function first, and if the server fails to handle the request, it will fall back to the S3 bucket.
213+
To work around the issue, requests for `public/` files are handled by the catch all behavior `/*`. This behavior sends the request to the server function first, and if the server fails to handle the request, it will fall back to the S3 bucket.
214+
215+
During the build process, the top-level file and directory names in the `public/` folder are saved to the `.open-next/public-files.json` file within the server function bundle. At runtime, the server function checks the request URL path against the file. If the request is made to a file in the `public/` folder:
216+
217+
- When deployed to a single region (Lambda), the server function returns a 503 response right away, and S3, which is configured as the failover origin on 503 status code, will serve the file. [Refer to the CloudFront setup.](#cloudfront-distribution)
218+
- When deployed to the edge (Lambda@Edge), the server function returns the request object. And the request will be handled by S3, which is configured as the origin. [Refer to the CloudFront setup.](#running-at-edge)
208219

209-
This means that on cache miss, the request will take slightly longer to process.
220+
This means that on cache miss, the request may take slightly longer to process.
210221

211222
#### WORKAROUND: `NextServer` does not set cache response headers for HTML pages
212223

0 commit comments

Comments
 (0)