static-frontend-hosting
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseStatic Frontend Hosting with Edge Authentication
带边缘认证能力的静态前端托管
This is a reference pattern. Learn from the approach, adapt to your context — don't copy verbatim.
Problem: Need low-cost, globally distributed frontend hosting with authentication but without requiring users to log in.
Solution: Static site on S3 + CloudFront CDN + Lambda@Edge for transparent authentication.
这是一个参考架构模式:请学习其设计思路,结合自身场景调整使用,请勿直接照搬。
问题:需要低成本、全球分布式的前端托管服务,同时具备认证能力,但无需用户手动登录。
解决方案:将静态站点部署在S3上,搭配CloudFront CDN + Lambda@Edge实现透明认证。
Pattern
模式说明
Architecture:
User Request
↓
CloudFront (CDN)
↓
Lambda@Edge (us-east-1) ← Validates token, sets cookies
↓
S3 Bucket (Static Files)Key Components:
- S3 Bucket: Hosts static frontend build (HTML, JS, CSS)
- CloudFront: Global CDN for fast delivery
- Lambda@Edge: Intercepts requests to handle authentication
- Origin Access Control: Secures S3 bucket (only CloudFront can access)
架构:
User Request
↓
CloudFront (CDN)
↓
Lambda@Edge (us-east-1) ← Validates token, sets cookies
↓
S3 Bucket (Static Files)核心组件:
- S3 Bucket:托管静态前端构建产物(HTML、JS、CSS)
- CloudFront:全球CDN,实现内容快速分发
- Lambda@Edge:拦截请求,处理认证逻辑
- Origin Access Control:保障S3 Bucket安全,仅允许CloudFront访问
Why This Pattern?
为什么选择这个模式?
Benefits:
- Low Cost: No servers running 24/7, pay only for storage and bandwidth
- Global Performance: CloudFront edge locations worldwide
- Scalability: Handles traffic spikes automatically
- Transparent Auth: Users get authenticated via link, no login screen
- Static Security: No server-side vulnerabilities, just static files
Use Cases:
- Portfolio sites with personalized access
- Demo applications with invite-only access
- Marketing sites with gated content
- Documentation with customer-specific views
优势:
- 低成本:无需24小时运行服务器,仅需为存储和带宽付费
- 全球性能:CloudFront在全球拥有大量边缘节点
- 可扩展性:可自动应对流量峰值
- 透明认证:用户通过链接完成认证,无需跳转登录页面
- 静态安全:仅提供静态文件,不存在服务端漏洞
适用场景:
- 需个性化访问权限的作品集站点
- 仅受邀用户可访问的演示应用
- 带门控内容的营销站点
- 提供客户专属视图的文档站点
Implementation
实现方案
S3 Bucket:
typescript
const websiteBucket = new s3.Bucket(this, 'WebsiteBucket', {
bucketName: `${PROJECT_ID}-web-${environment}`,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, // Not public!
removalPolicy: cdk.RemovalPolicy.DESTROY
});CloudFront Distribution:
typescript
const distribution = new cloudfront.Distribution(this, 'Distribution', {
defaultBehavior: {
origin: new origins.S3Origin(websiteBucket),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
edgeLambdas: [{
functionVersion: authFunction.currentVersion,
eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST
}]
}
});Lambda@Edge Function:
javascript
export async function handler(event) {
const request = event.Records[0].cf.request;
const queryParams = new URLSearchParams(request.querystring);
const token = queryParams.get('token');
if (token) {
// Validate token (check signature, expiration, etc.)
const isValid = await validateToken(token);
if (isValid) {
// Set authentication cookie
return {
status: '302',
headers: {
'location': [{ value: '/' }],
'set-cookie': [{ value: `auth_token=${token}; Secure; HttpOnly` }]
}
};
}
}
// Check existing cookie
const cookies = request.headers.cookie?.[0]?.value || '';
if (cookies.includes('auth_token=')) {
return request; // Authenticated, proceed
}
// Not authenticated, show public view or error
return request;
}S3 Bucket:
typescript
const websiteBucket = new s3.Bucket(this, 'WebsiteBucket', {
bucketName: `${PROJECT_ID}-web-${environment}`,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, // Not public!
removalPolicy: cdk.RemovalPolicy.DESTROY
});CloudFront Distribution:
typescript
const distribution = new cloudfront.Distribution(this, 'Distribution', {
defaultBehavior: {
origin: new origins.S3Origin(websiteBucket),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
edgeLambdas: [{
functionVersion: authFunction.currentVersion,
eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST
}]
}
});Lambda@Edge Function:
javascript
export async function handler(event) {
const request = event.Records[0].cf.request;
const queryParams = new URLSearchParams(request.querystring);
const token = queryParams.get('token');
if (token) {
// Validate token (check signature, expiration, etc.)
const isValid = await validateToken(token);
if (isValid) {
// Set authentication cookie
return {
status: '302',
headers: {
'location': [{ value: '/' }],
'set-cookie': [{ value: `auth_token=${token}; Secure; HttpOnly` }]
}
};
}
}
// Check existing cookie
const cookies = request.headers.cookie?.[0]?.value || '';
if (cookies.includes('auth_token=')) {
return request; // Authenticated, proceed
}
// Not authenticated, show public view or error
return request;
}Critical Tradeoff: Lambda@Edge Region Requirement
关键权衡:Lambda@Edge的区域要求
Constraint: Lambda@Edge functions MUST be deployed in us-east-1 region.
Why: CloudFront is a global service managed from us-east-1. Edge functions replicate globally from this region.
Implications:
- Your CDK stack for web hosting must deploy to us-east-1
- If your main infrastructure is in another region (e.g., eu-central-1), you need separate stacks
- Cross-region references become complex
Workaround:
typescript
// Main stack in eu-central-1
const mainStack = new MainStack(app, 'MainStack', {
env: { region: 'eu-central-1' }
});
// Web stack in us-east-1 (for Lambda@Edge)
const webStack = new WebStack(app, 'WebStack', {
env: { region: 'us-east-1' }
});
// Pass data via SSM Parameter Store, not direct references限制:Lambda@Edge函数必须部署在us-east-1区域。
原因:CloudFront是由us-east-1区域统一管理的全球服务,边缘函数会从该区域同步到全球各个节点。
影响:
- 你的前端托管CDK栈必须部署到us-east-1区域
- 如果你的核心基础设施部署在其他区域(比如eu-central-1),你需要拆分独立的部署栈
- 跨区域引用会变得更复杂
解决方案:
typescript
// Main stack in eu-central-1
const mainStack = new MainStack(app, 'MainStack', {
env: { region: 'eu-central-1' }
});
// Web stack in us-east-1 (for Lambda@Edge)
const webStack = new WebStack(app, 'WebStack', {
env: { region: 'us-east-1' }
});
// Pass data via SSM Parameter Store, not direct referencesDeployment Pipeline
部署流水线
Build Process:
- Build static frontend ()
npm run build - Deploy infrastructure (S3, CloudFront, Lambda@Edge)
- Upload static files to S3
- Invalidate CloudFront cache
Example:
yaml
undefined构建流程:
- 构建静态前端()
npm run build - 部署基础设施(S3、CloudFront、Lambda@Edge)
- 将静态文件上传到S3
- 失效CloudFront缓存
示例:
yaml
undefinedbuildspec.yml
buildspec.yml
phases:
build:
commands:
- cd frontend && npm run build
- cd infrastructure && cdk deploy WebStack
- aws s3 sync frontend/out s3://${BUCKET_NAME}
- aws cloudfront create-invalidation --distribution-id ${DIST_ID} --paths "/*"
---phases:
build:
commands:
- cd frontend && npm run build
- cd infrastructure && cdk deploy WebStack
- aws s3 sync frontend/out s3://${BUCKET_NAME}
- aws cloudfront create-invalidation --distribution-id ${DIST_ID} --paths "/*"
---When NOT to Use
不适用场景
- Server-Side Rendering: Use ECS/Lambda with API Gateway instead
- Real-time Updates: Use WebSocket API with Lambda
- Complex Auth Flows: Use Cognito Hosted UI or custom auth service
- Frequent Content Updates: Consider incremental static regeneration or dynamic rendering
- 服务端渲染场景:请改用搭配API Gateway的ECS/Lambda方案
- 实时更新场景:请使用带Lambda的WebSocket API
- 复杂认证流程场景:请使用Cognito托管UI或自定义认证服务
- 内容更新频繁场景:可考虑增量静态生成或动态渲染方案
Related Patterns
相关模式
- Link-Based Authentication - How to implement the auth tokens
- Environment Deployment Strategy - How to deploy across environments
- CDK Stack Organization - Managing multi-region stacks
- 基于链接的认证 - 如何实现认证令牌逻辑
- 环境部署策略 - 如何实现多环境部署
- CDK栈组织方式 - 多区域栈的管理方法
Progressive Improvement
持续优化建议
If the developer corrects a behavior that this skill should have prevented, suggest a specific amendment to this skill to prevent the same correction in the future.
如果开发者修正了本技能本应规避的错误行为,请给出具体的技能修订建议,避免后续再次出现同类问题。