Skip to content

Commit 2c8c7bc

Browse files
committed
Update architecture to create one cache behavior per top-level file and folder in /public
1 parent ad502ec commit 2c8c7bc

File tree

3 files changed

+51
-27
lines changed

3 files changed

+51
-27
lines changed

README.md

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,10 @@ The server function adapter wraps around `NextServer` and exports a handler func
172172
```diff
173173
.next/ -> NextServer
174174
+ .open-next/
175-
+ public-files.json -> `/public` file listing
176175
node_modules/ -> dependencies
177176
+ index.mjs -> server function adapter
178177
```
179178

180-
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)
181-
182179
**Monorepo**
183180

184181
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:
@@ -198,7 +195,6 @@ In this case, the server function adapter needs to be created inside `packages/w
198195
web/
199196
.next/ -> NextServer
200197
+ .open-next/
201-
+ public-files.json -> `/public` file listing
202198
node_modules/ -> dependencies from root node_modules (optional)
203199
+ index.mjs -> server function adapter
204200
node_modules/ -> dependencies from package node_modules
@@ -211,27 +207,29 @@ This ensures that the Lambda handler remains at `index.mjs`.
211207

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

214-
| Behavior | Requests | CloudFront Function | Origin |
215-
| ----------------- | ------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
216-
| `/_next/static/*` | Hashed static files | - | S3 bucket |
217-
| `/_next/image` | Image optimization | - | image optimization function |
218-
| `/_next/data/*` | data requests | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function |
219-
| `/api/*` | API | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function |
220-
| `/*` | catch all | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function fallback to<br />S3 bucket on 503<br />[see why](#workaround-public-static-files-served-out-by-server-function-aws-specific) |
210+
| Behavior | Requests | CloudFront Function | Origin |
211+
| ----------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------- | --------------------------- |
212+
| `/_next/static/*` | Hashed static files | - | S3 bucket |
213+
| `/favicon.ico`<br />`/my-images/*`<br />[see why](#workaround-public-static-files-served-out-by-server-function-aws-specific) | public assets | - | S3 bucket |
214+
| `/_next/image` | Image optimization | - | image optimization function |
215+
| `/_next/data/*` | data requests | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function |
216+
| `/api/*` | API | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function |
217+
| `/*` | catch all | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function |
221218

222219
#### Running at edge
223220

224221
The server function can also run at edge locations by configuring it as Lambda@Edge on Origin Request. The server function can accept both regional request events (API payload version 2.0) and edge request events (CloudFront Origin Request payload). Depending on the shape of the Lambda event object, the function will process the request accordingly.
225222

226223
To configure the CloudFront distribution:
227224

228-
| Behavior | Requests | CloudFront Function | Lambda@Edge | Origin |
229-
| ----------------- | ------------------- | ------------------------------------------------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------- |
230-
| `/_next/static/*` | Hashed static files | - | - | S3 bucket |
231-
| `/_next/image` | Image optimization | - | - | image optimization function |
232-
| `/_next/data/*` | data requests | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function | - |
233-
| `/api/*` | API | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function | - |
234-
| `/*` | catch all | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function | S3 bucket<br />[see why](#workaround-public-static-files-served-out-by-server-function-aws-specific) |
225+
| Behavior | Requests | CloudFront Function | Lambda@Edge | Origin |
226+
| ----------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------- | --------------- | --------------------------- |
227+
| `/_next/static/*` | Hashed static files | - | - | S3 bucket |
228+
| `/favicon.ico`<br />`/my-images/*`<br />[see why](#workaround-public-static-files-served-out-by-server-function-aws-specific) | public assets | - | - | S3 bucket |
229+
| `/_next/image` | Image optimization | - | - | image optimization function |
230+
| `/_next/data/*` | data requests | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function | - |
231+
| `/api/*` | API | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function | - |
232+
| `/*` | catch all | set `x-forwarded-host`<br />[see why](#workaround-set-x-forwarded-host-header-aws-specific) | server function | - |
235233

236234
#### Revalidation function
237235

@@ -389,26 +387,49 @@ This cost estimate is based on the `us-east-1` region pricing and does not consi
389387

390388
## Limitations and workarounds
391389

392-
#### WORKAROUND: `public/` static files served by the server function (AWS specific)
390+
#### WORKAROUND: Create one cache behavior per top-level file and folder in `public/` (AWS specific)
393391

394-
As mentioned in the [S3 bucket](#s3-bucket) section, files in your app's `public/` folder are static and are uploaded to the S3 bucket. Ideally, requests for these files should be handled by the S3 bucket, like so:
392+
As mentioned in the [Asset files](#asset-files) section, files in your app's `public/` folder are static and are uploaded to the S3 bucket. And requests for these files are handled by the S3 bucket, like so:
395393

396394
```
397-
398395
https://my-nextjs-app.com/favicon.ico
396+
https://my-nextjs-app.com/my-images/avatar.png
397+
```
398+
399+
Ideally, we would create a single cache behavior that routes all requests for `public/` files to the S3 bucket. Unfortunately, CloudFront does not support regex or advanced string patternss for cache behaviors (ie. `/favicon.ico|my-images\/*/` ).
400+
401+
To work around this limitation, we create a separate cache behavior for each top-level file and folder in `public/`. For example, if your folder structure is:
399402

400403
```
404+
public/
405+
favicon.ico
406+
my-images/
407+
avatar.png
408+
avatar-dark.png
409+
foo/
410+
bar.png
411+
```
412+
413+
You would create three cache behaviors: `/favicon.ico`, `/my-images/*`, and `/foo/*`. Each of these behaviors points to the S3 bucket.
401414

402-
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.
415+
One thing to be aware of is that CloudFront has a [default limit of 25 behaviors per distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-web-distributions). If you have a lot of top-level files and folders, you may reach this limit. To avoid this, consider moving some or all files and folders into a subdirectory:
403416

404-
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.
417+
```
418+
public/
419+
files/
420+
favicon.ico
421+
my-images/
422+
avatar.png
423+
avatar-dark.png
424+
foo/
425+
bar.png
426+
```
405427

406-
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:
428+
In this case, you only need to create one cache behavior: `/files/*`.
407429

408-
- 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)
409-
- 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)
430+
Make sure to update your code accordingly to reflect the new file paths.
410431

411-
This means that on cache miss, the request may take slightly longer to process.
432+
Alternatively, you can [request an increase to the limit through AWS Support](https://console.aws.amazon.com/support/home#/case/create?issueType=service-limit-increase&limitType=service-code-cloudfront-distributions).
412433

413434
#### WORKAROUND: Set `x-forwarded-host` header (AWS specific)
414435

docs/public/architecture.png

9.91 KB
Loading

packages/open-next/src/adapters/server-adapter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ export async function handler(
8989
}
9090

9191
// WORKAROUND: public/ static files served by the server function (AWS specific) — https://github.com/serverless-stack/open-next#workaround-public-static-files-served-by-the-server-function-aws-specific
92+
// TODO: This is no longer required if each top-level file and folder in "/public"
93+
// is handled by a separate cache behavior. Leaving here for backward compatibility.
94+
// Remove this on next major release.
9295
if (publicAssets.files.includes(internalEvent.rawPath)) {
9396
return internalEvent.type === "cf"
9497
? formatCloudFrontFailoverResponse(event as CloudFrontRequestEvent)

0 commit comments

Comments
 (0)