Daily Actions: Next.js on AWS (Static Export): S3 + CloudFront in minutes with CDK

Next.js on AWS (Static Export): S3 + CloudFront in minutes with CDK

Hosting a Next.js app statically on AWS uses the same building blocks: a private S3 bucket for files, CloudFront for global delivery, and OAC to restrict direct S3 access. We automate everything with the AWS CDK in TypeScript, without depending on Route 53.

This guide scaffolds a Next.js app (static export), then deploys the exported output (out/) to S3 behind CloudFront in one command.

Prerequisites

You will need:

BASH
aws configure
Click to expand and view more
BASH
npm install -g aws-cdk
Click to expand and view more

1. Project structure

Keep the app and infrastructure under one root folder:

SQL
/host-nextjs-app
├── /frontend          --------------> Next.js app (static export)
└── /infrastructure    --------------> AWS CDK code
Click to expand and view more

Create the root folder and the Next.js frontend

SH
mkdir host-nextjs-app && cd host-nextjs-app
npx create-next-app@latest frontend --typescript --eslint --app
cd frontend && npm install && cd ..
Click to expand and view more

Enable static export in Next.js

Edit frontend/next.config.js (or next.config.mjs) and set:

JS
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
};

module.exports = nextConfig;
Click to expand and view more

After that, run:

BASH
cd frontend
npm run build
Click to expand and view more

Next.js will generate the static site in frontend/out/.

Create the infrastructure project

SH
mkdir infrastructure && cd infrastructure
cdk init app --language typescript
Click to expand and view more

2. Infrastructure code (CDK)

Open infrastructure/lib/infrastructure-stack.ts. This stack creates the S3 bucket, the CloudFront distribution with OAC, and uploads your Next.js exported output (out/). For missing routes, we map 404 to Next.js 404.html.

TS
import * as cdk from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';

export class InfrastructureStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const bucket = new s3.Bucket(this, 'NextAppBucket', {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      bucketName: 'next-daily-app,
    });

    const distribution = new cloudfront.Distribution(this, 'NextAppDistribution', {
      defaultBehavior: {
        origin: origins.S3BucketOrigin.withOriginAccessControl(bucket),
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
      },
      defaultRootObject: 'index.html',
      errorResponses: [
        {
          httpStatus: 404,
          responseHttpStatus: 404,
          responsePagePath: '/404.html',
        },
      ],
    });

    // Next.js static export output: frontend/out
    new s3deploy.BucketDeployment(this, 'DeployNextApp', {
      sources: [s3deploy.Source.asset('../frontend/out')],
      destinationBucket: bucket,
      distribution,
      distributionPaths: ['/*'],
    });

    new cdk.CfnOutput(this, 'DistributionDomainName', {
      value: distribution.domainName,
    });
  }
}
Click to expand and view more

3. One-step deployment

In infrastructure/package.json, add a script that builds the frontend then runs CDK deploy (paths are relative to the infrastructure folder):

JSON
"deploy:all": "cd ../frontend && npm run build && cd ../infrastructure && cdk deploy"
Click to expand and view more

Then run:

BASH
cd infrastructure
npm run deploy:all
Click to expand and view more

The stack output lists the CloudFront URL (for example https://xxxxxxxxxxxx.cloudfront.net/) where the app is served.

application view

4. Pointing your domain (without Route 53) — bonus

If your DNS lives at OVH, GoDaddy, or another provider, add a CNAME record:

5. Clean up

To avoid ongoing charges after experiments:

SH
cd infrastructure
cdk destroy
Click to expand and view more

Comments

Start searching

Enter keywords to search articles

↑↓
ESC
⌘K Shortcut