static-frontend-hosting

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Static 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 references

Deployment Pipeline

部署流水线

Build Process:
  1. Build static frontend (
    npm run build
    )
  2. Deploy infrastructure (S3, CloudFront, Lambda@Edge)
  3. Upload static files to S3
  4. Invalidate CloudFront cache
Example:
yaml
undefined
构建流程
  1. 构建静态前端(
    npm run build
  2. 部署基础设施(S3、CloudFront、Lambda@Edge)
  3. 将静态文件上传到S3
  4. 失效CloudFront缓存
示例
yaml
undefined

buildspec.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.
如果开发者修正了本技能本应规避的错误行为,请给出具体的技能修订建议,避免后续再次出现同类问题。