Static

Deploy any client-side Single Page Application (SPA) or static site generator (SSG) on AWS S3 and CloudFront. Thunder’s Static construct ships with HTTPS, HTTP/3, Brotli compression, and security headers out of the box.

Supported Frameworks

AWS Resources

ResourcePurpose
S3 BucketStores your build output. Private, accessed only via CloudFront OAC.
CloudFront DistributionGlobal CDN. HTTP/3, TLS 1.2+, Brotli/Gzip compression.
Origin Access ControlSecures S3 — no public bucket access.
ACM CertificateSSL/TLS for custom domain (optional).
Route53DNS A + AAAA records for IPv4/IPv6 (optional).

Hosting Architecture

Users
Route 53
CloudFront
S3

Route 53 handles DNS resolution, directing users to the nearest CloudFront edge location. CloudFront serves as a global CDN, caching static assets at edge locations worldwide for low-latency delivery. S3 stores the application files as the origin, providing durable and scalable object storage.

Quick Start

Installation

Terminal window
bun add -D @thunder-so/thunder

Configuration

stack/prod.ts
import { Cdk, Static, type StaticProps } from "@thunder-so/thunder";
const config: StaticProps = {
env: {
account: 'YOUR_ACCOUNT_ID',
region: 'us-east-1'
},
application: 'myapp',
service: 'web',
environment: 'prod',
rootDir: '.', // monorepo: e.g. 'apps/web'
outputDir: 'dist',
};
new Static(
new Cdk.App(),
`${config.application}-${config.service}-${config.environment}-stack`,
config
);

Deploy

Terminal window
bun run build
npx cdk deploy --app "bunx tsx stack/prod.ts" --profile default

CDK outputs the CloudFront URL:

Outputs:
myapp-web-prod-stack.DistributionUrl = https://d1234abcd.cloudfront.net

Custom Domain

Connect your own domain using Route53 and an ACM certificate. The certificate must be issued in us-east-1 — CloudFront is a global service and requires it there regardless of your app’s region.

stack/prod.ts
const config: StaticProps = {
// ...
domain: 'app.example.com',
hostedZoneId: 'Z1D633PJN98FT9',
globalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',
};

CloudFront Cache Behavior

Control what gets included in the cache key and forwarded to the origin:

stack/prod.ts
const config: StaticProps = {
// ...
errorPagePath: '/404.html', // custom 404 page (default: /index.html)
allowHeaders: ['Accept-Language'],
allowCookies: ['session-*'],
allowQueryParams: ['lang', 'theme'],
// denyQueryParams: ['utm_source', 'fbclid'], // mutually exclusive with allowQueryParams
};

Lambda@Edge

URL Redirects and Rewrites

Configure URL redirects and rewrites using Lambda@Edge functions that run at CloudFront edge locations worldwide. Redirects return HTTP 301 responses to the client; rewrites transparently serve different content without changing the URL.

stack/prod.ts
const config: StaticProps = {
// ...
redirects: [
{ source: '/home', destination: '/' },
{ source: '/blog/:slug', destination: '/posts/:slug' },
],
rewrites: [
{ source: '/app/*', destination: '/index.html' }, // SPA fallback
],
};

Pattern syntax:

PatternMatches
/aboutExact path /about
/blog/*Any path starting with /blog/
/user/:id/user/123, /user/abc, etc.
/a/:x/b/:y/a/foo/b/barx=foo, y=bar

Custom Headers

Add custom HTTP response headers per path pattern. Headers run at the viewer-response stage and apply to both cached and uncached responses.

stack/prod.ts
const config: StaticProps = {
// ...
headers: [
{ path: '/assets/*', name: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
{ path: '/**', name: 'X-Frame-Options', value: 'SAMEORIGIN' },
],
};

Path syntax:

PathMatches
/*Root-level paths only
/**All paths including nested
/blog/*All paths under /blog/

CI/CD Pipeline

AWS CodePipeline Integration

GitHub
CodePipeline
CodeBuild
S3
CloudFront

GitHub triggers the pipeline on code changes. CodePipeline orchestrates the deployment workflow, while CodeBuild executes the build process. Built assets are uploaded to S3 and the CloudFront cache is invalidated to serve the latest version.

Configuration

stack/prod.ts
const config: StaticProps = {
// ...
accessTokenSecretArn: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:github-token-XXXXXX',
sourceProps: {
owner: 'your-username',
repo: 'your-repo',
branchOrRef: 'main',
},
buildProps: {
runtime: 'nodejs',
runtime_version: '22',
installcmd: 'bun install',
buildcmd: 'bun run build',
},
};

Stack Outputs

OutputDescription
DistributionIdCloudFront distribution ID
DistributionUrlCloudFront URL (https://xxxx.cloudfront.net)
Route53DomainCustom domain URL (only if domain is configured)

Destroy

Terminal window
npx cdk destroy --app "bunx tsx stack/prod.ts" --profile default