@thunder-so/thunder 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,267 @@
1
+ # Deploy Full-Stack Meta-Frameworks to AWS Lambda + S3 + CloudFront
2
+
3
+ Thunder's `Serverless` construct deploys modern full-stack frameworks with server-side rendering (SSR) to AWS using a hybrid architecture: [AWS Lambda](https://aws.amazon.com/lambda/) handles dynamic server requests, [S3](https://aws.amazon.com/s3/) hosts static assets, and [CloudFront](https://aws.amazon.com/cloudfront/) unifies both behind a single domain with intelligent routing.
4
+
5
+ This pattern works with any meta-framework that uses [Nitro](https://nitro.unjs.io/) or a compatible server runtime: Nuxt, Astro, TanStack Start, SvelteKit, Solid Start, AnalogJS, and more.
6
+
7
+ ## Architecture
8
+
9
+ ```
10
+ User Request
11
+
12
+ CloudFront (CDN)
13
+ ├─→ /assets/* → S3 (static files: JS, CSS, images)
14
+ ├─→ /api/* → Lambda (API routes)
15
+ └─→ /* → Lambda (SSR pages)
16
+ ```
17
+
18
+ - **Static assets** (`*.js`, `*.css`, `*.png`, etc.) are cached long-term at CloudFront edge locations
19
+ - **Dynamic requests** (SSR pages, API routes) hit Lambda through API Gateway
20
+ - **Single domain** - no CORS issues, unified caching strategy
21
+
22
+ ## AWS Resources
23
+
24
+ | Resource | Purpose |
25
+ |---|---|
26
+ | [Lambda Function](https://aws.amazon.com/lambda/) | Runs your server-side code (SSR, API routes) |
27
+ | [API Gateway HTTP API](https://aws.amazon.com/api-gateway/) | Routes dynamic requests to Lambda |
28
+ | [S3 Bucket](https://aws.amazon.com/s3/) | Hosts static assets (JS, CSS, images) |
29
+ | [CloudFront Distribution](https://aws.amazon.com/cloudfront/) | Global CDN with origin routing |
30
+ | [Origin Access Control (OAC)](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html) | Secures S3 - no public bucket access |
31
+ | [ACM Certificate](https://aws.amazon.com/certificate-manager/) | SSL/TLS for custom domain (optional) |
32
+ | [Route53](https://aws.amazon.com/route53/) | DNS A + AAAA records (optional) |
33
+
34
+ ## Supported Frameworks
35
+
36
+ | Framework | Construct | Server Runtime | Notes |
37
+ |---|---|---|---|
38
+ | [Nuxt](https://nuxt.com/) | `Nuxt` | Nitro | Vue-based, `aws-lambda` preset |
39
+ | [Astro](https://astro.build/) | `Astro` | @astro-aws/adapter | Requires Lambda@Edge fallback |
40
+ | [TanStack Start](https://tanstack.com/start) | `TanStackStart` | Nitro | React-based, explicit `aws-lambda` preset required |
41
+ | [SvelteKit](https://kit.svelte.dev/) | `SvelteKit` | @foladayo/sveltekit-adapter-lambda | Requires `serveStatic: true` |
42
+ | [Solid Start](https://start.solidjs.com/) | `SolidStart` | Nitro | SolidJS-based, `aws-lambda` preset |
43
+ | [AnalogJS](https://analogjs.org/) | `AnalogJS` | Nitro | Angular-based, `aws-lambda` preset |
44
+
45
+ See [framework-specific guides](#framework-guides) below for setup instructions.
46
+
47
+ ## Prerequisites
48
+
49
+ - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) configured
50
+ - [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) bootstrapped:
51
+ ```bash
52
+ cdk bootstrap aws://YOUR_ACCOUNT_ID/us-east-1
53
+ ```
54
+ - Your app built with framework-specific build command
55
+
56
+ ## Installation
57
+
58
+ ```bash
59
+ bun add @thunder-so/thunder --development
60
+ # or
61
+ npm install @thunder-so/thunder --save-dev
62
+ ```
63
+
64
+ ## Basic Example (Nuxt)
65
+
66
+ ```typescript
67
+ import { Cdk, Nuxt, type NuxtProps } from '@thunder-so/thunder';
68
+
69
+ const config: NuxtProps = {
70
+ env: { account: '123456789012', region: 'us-east-1' },
71
+ application: 'myapp',
72
+ service: 'web',
73
+ environment: 'prod',
74
+ rootDir: '.',
75
+ };
76
+
77
+ new Nuxt(new Cdk.App(), 'myapp-web-prod-stack', config);
78
+ ```
79
+
80
+ ## Deploy
81
+
82
+ ```bash
83
+ # Build your app first
84
+ bun run build
85
+
86
+ # Deploy
87
+ npx cdk deploy --app "npx tsx stack/prod.ts" --profile default
88
+ ```
89
+
90
+ CDK outputs the CloudFront URL and API Gateway URL:
91
+
92
+ ```
93
+ Outputs:
94
+ myapp-web-prod-stack.CloudFrontUrl = https://d1234abcd.cloudfront.net
95
+ myapp-web-prod-stack.ApiGatewayUrl = https://abc123.execute-api.us-east-1.amazonaws.com
96
+ ```
97
+
98
+ ## Custom Domain (Optional)
99
+
100
+ 1. [Create a Route53 Hosted Zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/AboutHZWorkingWith.html)
101
+ 2. [Request a global ACM certificate](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html) in **`us-east-1`** (for CloudFront)
102
+ 3. [Request a regional ACM certificate](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html) in your **function's region** (for API Gateway)
103
+
104
+ ```typescript
105
+ const config: ServerlessBaseProps = {
106
+ // ...
107
+ domain: 'app.example.com',
108
+ hostedZoneId: 'Z1234567890ABC',
109
+ globalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',
110
+ regionalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/def-456',
111
+ };
112
+ ```
113
+
114
+ > CloudFront requires a certificate in `us-east-1` (global). API Gateway requires a certificate in the same region as your Lambda function.
115
+
116
+ ## Configuration
117
+
118
+ ### Server (Lambda)
119
+
120
+ ```typescript
121
+ serverProps: {
122
+ memorySize: 1792,
123
+ timeout: 10,
124
+ keepWarm: true,
125
+ tracing: true,
126
+ variables: [
127
+ { NODE_ENV: 'production' },
128
+ ],
129
+ secrets: [
130
+ { key: 'DATABASE_URL', resource: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:/myapp/db-abc123' },
131
+ ],
132
+ },
133
+ ```
134
+
135
+ ### CloudFront Cache Behavior
136
+
137
+ ```typescript
138
+ // Cache control
139
+ allowHeaders: ['Accept-Language'],
140
+ allowCookies: ['session-*'],
141
+ allowQueryParams: ['lang'],
142
+ denyQueryParams: ['utm_source', 'fbclid'], // mutually exclusive with allowQueryParams
143
+
144
+ // Custom error page
145
+ errorPagePath: '/404.html',
146
+ ```
147
+
148
+ ## Container Mode (Docker)
149
+
150
+ For larger apps or custom runtimes, use Docker:
151
+
152
+ ```typescript
153
+ serverProps: {
154
+ dockerFile: 'Dockerfile',
155
+ dockerBuildArgs: { NODE_ENV: 'production' },
156
+ memorySize: 2048,
157
+ },
158
+ ```
159
+
160
+ Thunder builds and pushes the image to [Amazon ECR](https://aws.amazon.com/ecr/) automatically. See framework-specific docs for Dockerfile examples.
161
+
162
+ ## Environment Variables
163
+
164
+ ```typescript
165
+ serverProps: {
166
+ variables: [
167
+ { NODE_ENV: 'production' },
168
+ { API_BASE_URL: 'https://api.example.com' },
169
+ ],
170
+ },
171
+ ```
172
+
173
+ ## Secrets from AWS Secrets Manager
174
+
175
+ ```bash
176
+ aws secretsmanager create-secret \
177
+ --name "/myapp/DATABASE_URL" \
178
+ --secret-string "postgres://user:pass@host/db"
179
+ ```
180
+
181
+ ```typescript
182
+ serverProps: {
183
+ secrets: [
184
+ { key: 'DATABASE_URL', resource: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:/myapp/DATABASE_URL-abc123' },
185
+ ],
186
+ },
187
+ ```
188
+
189
+ ## Keep Warm
190
+
191
+ Prevent cold starts by pinging the Lambda every 5 minutes:
192
+
193
+ ```typescript
194
+ serverProps: {
195
+ keepWarm: true,
196
+ },
197
+ ```
198
+
199
+ ## Stack Outputs
200
+
201
+ | Output | Description |
202
+ |---|---|
203
+ | `CloudFrontUrl` | CloudFront distribution URL |
204
+ | `ApiGatewayUrl` | API Gateway endpoint URL |
205
+ | `Route53Domain` | Custom domain URL (only if domain is configured) |
206
+
207
+ ## Framework Guides
208
+
209
+ Detailed setup instructions for each framework:
210
+
211
+ - [Nuxt Serverless](./frameworks/nuxt-serverless.md)
212
+ - [Astro Serverless](./frameworks/astro-serverless.md)
213
+ - [TanStack Start Serverless](./frameworks/tanstack-start-serverless.md)
214
+ - [SvelteKit Serverless](./frameworks/sveltekit-serverless.md)
215
+ - [Solid Start Serverless](./frameworks/solidstart-serverless.md)
216
+ - [AnalogJS Serverless](./frameworks/analogjs-serverless.md)
217
+
218
+ ## Generic Serverless Deployment
219
+
220
+ For any Vite/Nitro-based meta-framework not explicitly supported, use the generic `Serverless` construct:
221
+
222
+ ```typescript
223
+ import { Cdk, Serverless, type ServerlessProps } from '@thunder-so/thunder';
224
+
225
+ const config: ServerlessProps = {
226
+ env: { account: '123456789012', region: 'us-east-1' },
227
+ application: 'myapp',
228
+ service: 'web',
229
+ environment: 'prod',
230
+ rootDir: '.',
231
+
232
+ serverProps: {
233
+ codeDir: '.output/server', // Your framework's server output
234
+ handler: 'index.handler',
235
+ runtime: Cdk.aws_lambda.Runtime.NODEJS_22_X,
236
+ architecture: Cdk.aws_lambda.Architecture.ARM_64,
237
+ memorySize: 1792,
238
+ timeout: 10,
239
+ },
240
+
241
+ clientProps: {
242
+ outputDir: '.output/public', // Your framework's static assets
243
+ },
244
+ };
245
+
246
+ new Serverless(
247
+ new Cdk.App(),
248
+ 'myapp-web-prod-stack',
249
+ { ...config, framework: 'custom' }
250
+ );
251
+ ```
252
+
253
+ This works with any framework that outputs a Lambda-compatible handler and static assets.
254
+
255
+
256
+
257
+ ```bash
258
+ npx cdk destroy --app "npx tsx stack/prod.ts" --profile default
259
+ ```
260
+
261
+ > The S3 bucket uses `RemovalPolicy.RETAIN` to prevent accidental data loss. Delete it manually from the AWS console if needed.
262
+
263
+ ## Related
264
+
265
+ - [lambda-basic.md](./lambda-basic.md) - Lambda construct reference
266
+ - [static-basic.md](./static-basic.md) - Static construct reference
267
+ - [fargate-basic.md](./fargate-basic.md) - Fargate construct reference
@@ -0,0 +1,151 @@
1
+ # Deploy Static Sites and SPAs to AWS S3 + CloudFront
2
+
3
+ Host your frontend on AWS in minutes. Thunder's `Static` construct deploys your build output to [Amazon S3](https://aws.amazon.com/s3/) and serves it globally through a [CloudFront](https://aws.amazon.com/cloudfront/) CDN - with HTTPS, HTTP/3, Brotli compression, and security headers out of the box.
4
+
5
+ Works with any framework that produces a static output folder: Vite (React, Vue, Svelte, Solid), Next.js static export, Astro SSG, Gatsby, and more.
6
+
7
+ ## AWS Resources
8
+
9
+ | Resource | Purpose |
10
+ |---|---|
11
+ | [S3 Bucket](https://aws.amazon.com/s3/) | Stores your build output. Private, accessed only via CloudFront OAC. |
12
+ | [CloudFront Distribution](https://aws.amazon.com/cloudfront/) | Global CDN. HTTP/3, TLS 1.2+, Brotli/Gzip compression. |
13
+ | [Origin Access Control (OAC)](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html) | Secures S3 - no public bucket access. |
14
+ | [ACM Certificate](https://aws.amazon.com/certificate-manager/) | SSL/TLS for your custom domain (optional). |
15
+ | [Route53](https://aws.amazon.com/route53/) | DNS A + AAAA records for IPv4/IPv6 (optional). |
16
+
17
+ ## Prerequisites
18
+
19
+ - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) configured with credentials
20
+ - [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) bootstrapped in your target account/region:
21
+ ```bash
22
+ cdk bootstrap aws://YOUR_ACCOUNT_ID/us-east-1
23
+ ```
24
+ - Your app's build output directory (e.g. `dist/`)
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ bun add @thunder-so/thunder --development
30
+ # or
31
+ npm install @thunder-so/thunder --save-dev
32
+ ```
33
+
34
+ ## Stack File
35
+
36
+ Create `stack/dev.ts` (use separate files per environment):
37
+
38
+ ```typescript
39
+ import { Cdk, Static, type StaticProps } from '@thunder-so/thunder';
40
+
41
+ const config: StaticProps = {
42
+ env: {
43
+ account: '123456789012',
44
+ region: 'us-east-1',
45
+ },
46
+ application: 'myapp',
47
+ service: 'web',
48
+ environment: 'dev',
49
+
50
+ rootDir: '.', // monorepo: e.g. 'apps/web'
51
+ outputDir: 'dist', // your framework's build output folder
52
+ };
53
+
54
+ new Static(
55
+ new Cdk.App(),
56
+ `${config.application}-${config.service}-${config.environment}-stack`,
57
+ config
58
+ );
59
+ ```
60
+
61
+ ## Deploy
62
+
63
+ ```bash
64
+ npx cdk deploy --app "npx tsx stack/dev.ts" --profile default
65
+ ```
66
+
67
+ After deployment, CDK outputs the CloudFront distribution URL:
68
+
69
+ ```
70
+ Outputs:
71
+ myapp-web-dev-stack.DistributionUrl = https://d1234abcd.cloudfront.net
72
+ ```
73
+
74
+ ## Custom Domain (Optional)
75
+
76
+ To serve from your own domain you need:
77
+
78
+ 1. A [Route53 Hosted Zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/AboutHZWorkingWith.html) for your domain
79
+ 2. A [public ACM certificate](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html) issued in **`us-east-1`** (required for CloudFront, regardless of your app's region)
80
+
81
+ ```typescript
82
+ const config: StaticProps = {
83
+ // ...
84
+ domain: 'app.example.com',
85
+ globalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',
86
+ hostedZoneId: 'Z1234567890ABC',
87
+ };
88
+ ```
89
+
90
+ > The certificate **must** be in `us-east-1` because CloudFront is a global service.
91
+
92
+ ## Configuration Reference
93
+
94
+ ### Core
95
+
96
+ | Property | Type | Required | Description |
97
+ |---|---|---|---|
98
+ | `env.account` | `string` | Yes | AWS account ID |
99
+ | `env.region` | `string` | Yes | AWS region |
100
+ | `application` | `string` | Yes | Project identifier |
101
+ | `service` | `string` | Yes | Service identifier |
102
+ | `environment` | `string` | Yes | Environment name (e.g. `prod`) |
103
+ | `rootDir` | `string` | No | Root of your app. Defaults to `.` |
104
+ | `outputDir` | `string` | No | Build output directory. Defaults to `dist` |
105
+ | `debug` | `boolean` | No | Enables S3 access logs and CloudFront logging |
106
+
107
+ ### Domain
108
+
109
+ | Property | Type | Description |
110
+ |---|---|---|
111
+ | `domain` | `string` | Custom domain, e.g. `app.example.com` |
112
+ | `globalCertificateArn` | `string` | ACM certificate ARN (must be `us-east-1`) |
113
+ | `hostedZoneId` | `string` | Route53 hosted zone ID |
114
+
115
+ ### CloudFront Behavior
116
+
117
+ | Property | Type | Description |
118
+ |---|---|---|
119
+ | `errorPagePath` | `string` | Custom 404 page path, e.g. `/404.html`. Defaults to `/index.html` |
120
+ | `allowHeaders` | `string[]` | Headers to include in cache key and forward to origin |
121
+ | `allowCookies` | `string[]` | Cookies to include in cache key |
122
+ | `allowQueryParams` | `string[]` | Query params to include in cache key |
123
+ | `denyQueryParams` | `string[]` | Query params to strip from cache key (e.g. UTM params). Mutually exclusive with `allowQueryParams` |
124
+
125
+ ## Default Security Headers
126
+
127
+ The distribution ships with a secure-by-default response headers policy:
128
+
129
+ | Header | Value |
130
+ |---|---|
131
+ | `Strict-Transport-Security` | `max-age=31536000; includeSubDomains; preload` |
132
+ | `X-Frame-Options` | `DENY` |
133
+ | `X-Content-Type-Options` | `nosniff` |
134
+ | `Referrer-Policy` | `strict-origin-when-cross-origin` |
135
+ | `Content-Security-Policy` | `default-src 'self'; style-src https: 'unsafe-inline'; ...` |
136
+ | `X-XSS-Protection` | `1; mode=block` |
137
+
138
+ To add custom headers per path, see [static-edge-functions.md](./static-edge-functions.md).
139
+
140
+ ## Destroy
141
+
142
+ ```bash
143
+ npx cdk destroy --app "npx tsx stack/dev.ts" --profile default
144
+ ```
145
+
146
+ > The S3 hosting bucket uses `RemovalPolicy.RETAIN` to prevent accidental data loss. Delete it manually from the AWS console if needed.
147
+
148
+ ## Next Steps
149
+
150
+ - [static-edge-functions.md](./static-edge-functions.md) - Add redirects, rewrites, and custom headers via Lambda@Edge
151
+ - [static-full.md](./static-full.md) - Full configuration reference with all options
@@ -0,0 +1,187 @@
1
+ # CloudFront Redirects, Rewrites, and Custom Headers with Lambda@Edge
2
+
3
+ Add URL redirects, path rewrites, and custom HTTP response headers to your CloudFront distribution - no origin round-trip required. Thunder deploys [Lambda@Edge](https://aws.amazon.com/lambda/edge/) functions automatically when you configure these options, running your rules at AWS edge locations worldwide.
4
+
5
+ Two functions are created:
6
+
7
+ | Function | CloudFront Event | Purpose |
8
+ |---|---|---|
9
+ | `RedirectRewriteFunction` | `viewer-request` | Evaluates redirects and rewrites before the cache |
10
+ | `HeadersFunction` | `viewer-response` | Injects custom headers into responses |
11
+
12
+ > Lambda@Edge functions are always deployed to `us-east-1` and replicated globally by CloudFront.
13
+
14
+ ## Redirects
15
+
16
+ A redirect returns an HTTP `301 Moved Permanently` response, causing the browser to navigate to a new URL. Use redirects when a URL has permanently moved.
17
+
18
+ ```typescript
19
+ import { Cdk, Static, type StaticProps } from '@thunder-so/thunder';
20
+
21
+ const config: StaticProps = {
22
+ // ...core config...
23
+
24
+ redirects: [
25
+ // Static redirect
26
+ { source: '/home', destination: '/' },
27
+
28
+ // Wildcard - captures everything after /guide/
29
+ { source: '/guide/*', destination: '/docs/*' },
30
+
31
+ // Named placeholders
32
+ { source: '/blog/:year/:month', destination: '/posts/:year/:month' },
33
+ ],
34
+ };
35
+ ```
36
+
37
+ ### Pattern Syntax
38
+
39
+ | Pattern | Matches |
40
+ |---|---|
41
+ | `/about` | Exact path `/about` |
42
+ | `/blog/*` | Any path starting with `/blog/` |
43
+ | `/user/:id` | `/user/123`, `/user/abc`, etc. |
44
+ | `/a/:x/b/:y` | `/a/foo/b/bar` → placeholders `x=foo`, `y=bar` |
45
+
46
+ Wildcards (`*`) and placeholders (`:name`) can be used in both `source` and `destination`. Placeholders are positional - they map by name between source and destination.
47
+
48
+ ## Rewrites
49
+
50
+ A rewrite changes the path the request is forwarded to internally, without changing the URL in the browser. Use rewrites to serve a different file while keeping the original URL visible.
51
+
52
+ ```typescript
53
+ const config: StaticProps = {
54
+ // ...
55
+
56
+ rewrites: [
57
+ // Serve index.html for all app routes (SPA fallback)
58
+ { source: '/app/*', destination: '/index.html' },
59
+
60
+ // Map a vanity URL to a real path
61
+ { source: '/profile/:username', destination: '/user/:username' },
62
+ ],
63
+ };
64
+ ```
65
+
66
+ ### SPA Fallback Pattern
67
+
68
+ For client-side routed SPAs, rewrite all unmatched paths to `index.html`:
69
+
70
+ ```typescript
71
+ rewrites: [
72
+ { source: '/*', destination: '/index.html' },
73
+ ],
74
+ ```
75
+
76
+ > Rewrites run after redirects. If a path matches a redirect rule, the rewrite is never evaluated.
77
+
78
+ ## Custom HTTP Headers
79
+
80
+ Use `headers` to inject or override HTTP response headers for specific path patterns. This runs at the `viewer-response` stage, so it applies to both cached and uncached responses.
81
+
82
+ ```typescript
83
+ const config: StaticProps = {
84
+ // ...
85
+
86
+ headers: [
87
+ // Cache HTML for 10 minutes
88
+ { path: '/*', name: 'Cache-Control', value: 'public, max-age=600' },
89
+
90
+ // Long-lived cache for hashed assets
91
+ { path: '/assets/*', name: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
92
+
93
+ // Restrict embedding to same origin
94
+ { path: '/**', name: 'X-Frame-Options', value: 'SAMEORIGIN' },
95
+
96
+ // CORS for a specific API path
97
+ { path: '/api/*', name: 'Access-Control-Allow-Origin', value: 'https://app.example.com' },
98
+ ],
99
+ };
100
+ ```
101
+
102
+ ### Path Syntax
103
+
104
+ | Path | Matches |
105
+ |---|---|
106
+ | `/*` | Root-level paths only |
107
+ | `/**` | All paths including nested |
108
+ | `/blog/*` | All paths under `/blog/` |
109
+ | `/assets/*.{js,css}` | JS and CSS files under `/assets/` |
110
+
111
+ ### Header Evaluation Order
112
+
113
+ Headers are applied in array order. Later entries for the same header name on the same path will overwrite earlier ones.
114
+
115
+ > Custom headers defined here are applied **after** the built-in security headers policy. To override a default security header (e.g. `X-Frame-Options`), specify it explicitly here.
116
+
117
+ ## Default Security Headers
118
+
119
+ These are always applied by the CloudFront Response Headers Policy, regardless of your `headers` config:
120
+
121
+ | Header | Default |
122
+ |---|---|
123
+ | `Strict-Transport-Security` | `max-age=31536000; includeSubDomains; preload` |
124
+ | `X-Frame-Options` | `DENY` |
125
+ | `X-Content-Type-Options` | `nosniff` |
126
+ | `Referrer-Policy` | `strict-origin-when-cross-origin` |
127
+ | `X-XSS-Protection` | `1; mode=block` |
128
+ | `Content-Security-Policy` | `default-src 'self'; style-src https: 'unsafe-inline'; script-src https: 'unsafe-inline' 'wasm-unsafe-eval'; ...` |
129
+
130
+ Default CORS headers (applied to all origins):
131
+
132
+ | Header | Default |
133
+ |---|---|
134
+ | `Access-Control-Allow-Origin` | `*` |
135
+ | `Access-Control-Allow-Methods` | `GET, HEAD, OPTIONS` |
136
+ | `Access-Control-Allow-Headers` | `*` |
137
+ | `Access-Control-Max-Age` | `600` |
138
+
139
+ ## Full Example
140
+
141
+ ```typescript
142
+ import { Cdk, Static, type StaticProps } from '@thunder-so/thunder';
143
+
144
+ const config: StaticProps = {
145
+ env: { account: '123456789012', region: 'us-east-1' },
146
+ application: 'myapp',
147
+ service: 'web',
148
+ environment: 'prod',
149
+ rootDir: '.',
150
+ outputDir: 'dist',
151
+
152
+ domain: 'app.example.com',
153
+ globalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',
154
+ hostedZoneId: 'Z1234567890ABC',
155
+
156
+ redirects: [
157
+ { source: '/old-page', destination: '/new-page' },
158
+ ],
159
+
160
+ rewrites: [
161
+ { source: '/app/*', destination: '/index.html' },
162
+ ],
163
+
164
+ headers: [
165
+ { path: '/**', name: 'X-Frame-Options', value: 'SAMEORIGIN' },
166
+ { path: '/assets/*', name: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
167
+ ],
168
+ };
169
+
170
+ new Static(new Cdk.App(), 'myapp-web-prod-stack', config);
171
+ ```
172
+
173
+ ## Troubleshooting
174
+
175
+ **Redirects not firing:** Lambda@Edge logs appear in CloudWatch in the region closest to the viewer, not `us-east-1`. Check the relevant regional log group `/aws/lambda/us-east-1.RedirectRewriteFunction`.
176
+
177
+ **Headers not appearing:** Verify the path pattern matches your URL. Use `/**` to match all paths including nested ones.
178
+
179
+ **CSP blocking resources:** The default CSP is strict. Override it with a `headers` entry:
180
+ ```typescript
181
+ { path: '/**', name: 'Content-Security-Policy', value: 'your-custom-policy' }
182
+ ```
183
+
184
+ ## Related
185
+
186
+ - [static-basic.md](./static-basic.md) - Basic setup
187
+ - [static-full.md](./static-full.md) - Full configuration reference