Lambda@Edge 完整指南:CDN 邊緣運算應用與實戰
Lambda@Edge 完整指南:CDN 邊緣運算應用與實戰
網站用了 CDN,全球用戶都能快速載入靜態檔案。但如果你需要在 CDN 層面做一些「動態處理」呢?
URL 重寫、A/B 測試、安全 Header 注入、圖片格式轉換——這些需求傳統上要經過 Origin Server 處理,延遲高且增加後端負擔。
Lambda@Edge 讓你在 CloudFront 的全球節點執行程式碼,把邏輯搬到離用戶最近的地方執行。
本文將完整解析 Lambda@Edge 的概念、限制與實戰應用。如果你還不熟悉 Lambda 基礎概念,建議先閱讀 AWS Lambda 完整指南。
Lambda@Edge 是什麼
邊緣運算概念
邊緣運算(Edge Computing) 是將運算能力從中央伺服器移到靠近用戶的位置。
傳統架構:
用戶(台北)→ CloudFront Edge → Origin(東京)→ 處理邏輯 → 回傳
延遲:CDN 快取命中 ~20ms,未命中 ~200ms
Lambda@Edge 架構:
用戶(台北)→ CloudFront Edge → Lambda@Edge 處理 → 直接回傳
延遲:~50ms(不需要回 Origin)
與一般 Lambda 的差異
圖片說明:一般 Lambda 與 Lambda@Edge 的架構比較圖,顯示執行位置、觸發方式、限制等差異。
| 特性 | 一般 Lambda | Lambda@Edge |
|---|---|---|
| 執行位置 | 單一 Region | CloudFront 全球 200+ 節點 |
| 部署區域 | 任意 Region | 僅能在 us-east-1 |
| 觸發方式 | 多種觸發器 | CloudFront Events |
| 最長執行時間 | 15 分鐘 | Viewer: 5 秒 / Origin: 30 秒 |
| 記憶體上限 | 10 GB | 128 MB - 10 GB |
| 環境變數 | 支援 | 不支援 |
| VPC | 支援 | 不支援 |
| Container Image | 支援 | 不支援 |
CloudFront 整合原理
Lambda@Edge 透過 CloudFront 的事件觸發。當請求到達 CloudFront 時,可以在 4 個不同的時機點執行 Lambda:
用戶請求 → Viewer Request → [快取檢查] → Origin Request → Origin
↓
用戶 ← Viewer Response ← [快取] ← Origin Response ← Origin 回應
執行時機點
Viewer Request
觸發時機:CloudFront 收到用戶請求後,快取檢查前。
典型用途:
- URL 重寫(多語系路由)
- A/B 測試分流
- 請求驗證
- 地理位置判斷
// Viewer Request 範例:根據語言重導向
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
// 取得 Accept-Language
const acceptLanguage = headers['accept-language']?.[0]?.value || '';
// 判斷語言並重寫 URI
if (acceptLanguage.includes('zh')) {
request.uri = '/zh' + request.uri;
} else if (acceptLanguage.includes('ja')) {
request.uri = '/ja' + request.uri;
} else {
request.uri = '/en' + request.uri;
}
return request;
};
Origin Request
觸發時機:快取未命中,轉發請求到 Origin 前。
典型用途:
- 動態選擇 Origin(藍綠部署)
- 加入認證 Header
- 修改請求參數
// Origin Request 範例:動態選擇 Origin
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
// 根據路徑選擇不同 Origin
if (request.uri.startsWith('/api/v2')) {
request.origin = {
custom: {
domainName: 'api-v2.example.com',
port: 443,
protocol: 'https',
sslProtocols: ['TLSv1.2']
}
};
request.headers['host'] = [{ key: 'host', value: 'api-v2.example.com' }];
}
return request;
};
Origin Response
觸發時機:收到 Origin 回應後,存入快取前。
典型用途:
- 加入安全 Header
- 修改回應 Header
- 壓縮或轉換內容
Viewer Response
觸發時機:回傳給用戶前(不論快取命中或未命中)。
典型用途:
- 加入 Cookie
- 修改 Cache-Control Header
- 移除敏感 Header
如何選擇正確的觸發點
圖片說明:Lambda@Edge 觸發點選擇決策樹,根據需求(修改請求/回應、快取影響)引導選擇正確的觸發時機。
| 需求 | 建議觸發點 | 原因 |
|---|---|---|
| URL 重寫、A/B 測試 | Viewer Request | 快取檢查前處理 |
| 動態選擇 Origin | Origin Request | 快取未命中時才需要 |
| 加入安全 Header | Origin Response | 只處理一次,快取後自動帶 |
| 設定用戶 Cookie | Viewer Response | 每次請求都要執行 |
效能考量:Viewer 觸發點每次請求都執行,Origin 觸發點只在快取未命中時執行。能用 Origin 就不要用 Viewer。
Lambda@Edge 限制
與一般 Lambda 的差異表
| 限制項目 | Lambda@Edge (Viewer) | Lambda@Edge (Origin) | 一般 Lambda |
|---|---|---|---|
| 執行時間 | 5 秒 | 30 秒 | 15 分鐘 |
| 記憶體 | 128 MB - 128 MB | 128 MB - 10 GB | 128 MB - 10 GB |
| 部署套件 | 1 MB | 50 MB | 250 MB |
| 環境變數 | 不支援 | 不支援 | 支援 |
| VPC | 不支援 | 不支援 | 支援 |
| Layers | 不支援 | 不支援 | 支援 |
| Provisioned Concurrency | 不支援 | 不支援 | 支援 |
重要限制說明
1. 僅能在 us-east-1 部署
Lambda@Edge 函數必須在 N. Virginia (us-east-1) 建立,AWS 會自動複製到全球所有 CloudFront 節點。
# 錯誤:在其他 Region 建立會無法與 CloudFront 關聯
aws lambda create-function --region ap-northeast-1 ... # 無法用於 Lambda@Edge
# 正確:必須在 us-east-1 建立
aws lambda create-function --region us-east-1 ...
2. 不支援環境變數
無法使用 process.env.MY_VAR。替代方案:
// 將設定寫在程式碼中
const CONFIG = {
API_ENDPOINT: 'https://api.example.com',
FEATURE_FLAG: true
};
// 或從 S3/Parameter Store 動態讀取(會增加延遲)
3. 不支援 VPC
Lambda@Edge 無法存取 VPC 內的資源。如果需要存取 RDS、ElastiCache,考慮:
- 使用公開的 API Gateway 端點
- 使用 DynamoDB(全球表)
- 重新設計架構
CloudFront Functions vs Lambda@Edge
圖片說明:CloudFront Functions 與 Lambda@Edge 的功能與適用場景比較表。
| 特性 | CloudFront Functions | Lambda@Edge |
|---|---|---|
| 執行時間 | < 1 ms | 5-30 秒 |
| 記憶體 | 2 MB | 128 MB - 10 GB |
| 程式語言 | JavaScript (ES 5.1) | Node.js, Python |
| 網路存取 | 不支援 | 支援 |
| 費用 | ~$0.10/百萬次 | ~$0.60/百萬次 |
| 觸發點 | Viewer 階段 | 全部 4 個 |
選擇建議:
- 簡單的 Header 操作、URL 重寫 → CloudFront Functions(更快更便宜)
- 需要呼叫外部 API、複雜邏輯 → Lambda@Edge
不確定 Lambda@Edge 適不適合你?預約 CDN 評估,讓專家幫你分析。
實戰應用
應用一:URL 重寫(多語系、Pretty URL)
// 將 /blog/123 重寫為 /blog/123.html
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const uri = request.uri;
// 如果是目錄路徑,加上 index.html
if (uri.endsWith('/')) {
request.uri += 'index.html';
}
// 如果沒有副檔名,加上 .html
else if (!uri.includes('.')) {
request.uri += '.html';
}
return request;
};
應用二:A/B 測試
// 依據 Cookie 進行 A/B 測試分流
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
// 檢查是否有 A/B 測試 Cookie
let experimentGroup = 'A';
const cookies = headers.cookie?.[0]?.value || '';
const match = cookies.match(/ab-test=([AB])/);
if (match) {
experimentGroup = match[1];
} else {
// 新用戶隨機分配
experimentGroup = Math.random() < 0.5 ? 'A' : 'B';
}
// 根據分組重寫路徑
if (experimentGroup === 'B') {
request.uri = '/experiment-b' + request.uri;
}
// 加入 Header 讓 Origin 知道分組
request.headers['x-experiment-group'] = [{ key: 'X-Experiment-Group', value: experimentGroup }];
return request;
};
應用三:安全 Header(CSP、HSTS)
// Origin Response:加入安全 Header
exports.handler = async (event) => {
const response = event.Records[0].cf.response;
const headers = response.headers;
// Strict Transport Security
headers['strict-transport-security'] = [{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains; preload'
}];
// Content Security Policy
headers['content-security-policy'] = [{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-inline' cdn.example.com;"
}];
// X-Frame-Options
headers['x-frame-options'] = [{
key: 'X-Frame-Options',
value: 'DENY'
}];
// X-Content-Type-Options
headers['x-content-type-options'] = [{
key: 'X-Content-Type-Options',
value: 'nosniff'
}];
return response;
};
應用四:圖片優化(WebP 轉換)
// Viewer Request:根據瀏覽器支援度重寫圖片路徑
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const uri = request.uri;
// 檢查是否為圖片請求
if (uri.match(/\.(jpe?g|png)$/i)) {
// 檢查瀏覽器是否支援 WebP
const accept = headers.accept?.[0]?.value || '';
if (accept.includes('image/webp')) {
// 重寫為 WebP 版本
request.uri = uri.replace(/\.(jpe?g|png)$/i, '.webp');
}
}
return request;
};
應用五:邊緣身份驗證
// Viewer Request:JWT Token 驗證
const jwt = require('jsonwebtoken'); // 需要打包進部署套件
const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----`;
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
// 取得 Authorization Header
const authHeader = headers.authorization?.[0]?.value;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return {
status: '401',
statusDescription: 'Unauthorized',
headers: {
'content-type': [{ key: 'Content-Type', value: 'application/json' }]
},
body: JSON.stringify({ error: 'Missing or invalid token' })
};
}
const token = authHeader.substring(7);
try {
const decoded = jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] });
// 將用戶資訊傳給 Origin
request.headers['x-user-id'] = [{ key: 'X-User-Id', value: decoded.sub }];
return request;
} catch (err) {
return {
status: '401',
statusDescription: 'Unauthorized',
headers: {
'content-type': [{ key: 'Content-Type', value: 'application/json' }]
},
body: JSON.stringify({ error: 'Invalid token' })
};
}
};
與 API Gateway 的身份驗證方式比較,請參考 API Gateway 身份驗證。
部署教學
使用 AWS Console
Step 1:在 us-east-1 建立 Lambda 函數
- 前往 Lambda Console(確認在 N. Virginia)
- 建立函數 → Author from scratch
- Runtime:Node.js 20.x
- 貼上程式碼並部署
Step 2:發布版本
- Actions → Publish new version
- Lambda@Edge 需要版本號,不能用 $LATEST
Step 3:加入 CloudFront 觸發器
- 函數頁面 → Add trigger
- 選擇 CloudFront
- 選擇 Distribution 和觸發點
- 勾選「Deploy to Lambda@Edge」
使用 Terraform
詳細的 Terraform Lambda 部署基礎,請參考 Terraform 部署 Lambda 教學。
# provider.tf - 必須在 us-east-1
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}
# Lambda@Edge 函數
resource "aws_lambda_function" "edge" {
provider = aws.us_east_1
function_name = "my-edge-function"
role = aws_iam_role.edge_lambda.arn
handler = "index.handler"
runtime = "nodejs20.x"
publish = true # 必須發布版本
filename = data.archive_file.edge_lambda.output_path
source_code_hash = data.archive_file.edge_lambda.output_base64sha256
}
# IAM 角色
resource "aws_iam_role" "edge_lambda" {
provider = aws.us_east_1
name = "edge-lambda-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com" # Lambda@Edge 需要這個
]
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "edge_lambda_logs" {
provider = aws.us_east_1
role = aws_iam_role.edge_lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
# CloudFront Distribution
resource "aws_cloudfront_distribution" "main" {
# ... 其他設定 ...
default_cache_behavior {
# ... 其他設定 ...
lambda_function_association {
event_type = "viewer-request"
lambda_arn = aws_lambda_function.edge.qualified_arn
include_body = false
}
}
}
CDN 架構需要優化? Lambda@Edge、CloudFront Functions、邊緣快取各有適用場景。 預約 CDN 評估,讓我們幫你設計最佳 CDN 架構。
效能與成本
費用計算
Lambda@Edge 的計費方式:
| 計費項目 | 單價 |
|---|---|
| 請求次數 | $0.60 / 百萬次(比一般 Lambda 貴 3 倍) |
| 執行時間 | 依記憶體,128 MB 約 $0.00000625 / 128ms |
範例試算:每月 1000 萬次請求,平均執行 50ms,128 MB 記憶體
請求費用:10,000,000 × $0.0000006 = $6.00
執行時間:10,000,000 × 50ms × $0.00000625 / 128ms = $24.41
總計:約 $30.41 / 月
與一般 Lambda 成本比較
更詳細的 Lambda 費用計算,請參考 Lambda 費用計算詳解。
| 場景 | Lambda@Edge | Lambda + API Gateway |
|---|---|---|
| 請求費用(每百萬) | $0.60 | $0.20 + $1.00 = $1.20 |
| 全球延遲 | 低(邊緣執行) | 較高(需回 Region) |
| 適用情境 | 簡單處理、低延遲需求 | 複雜邏輯、需要 VPC |
結論:如果邏輯簡單且對延遲敏感,Lambda@Edge 可能比 Lambda + API Gateway 更划算。
效能最佳化技巧
圖片說明:Lambda@Edge 效能最佳化要點,包含程式碼最小化、避免外部 API、善用快取等技巧。
1. 減少部署套件大小
# 使用 esbuild 或 webpack 打包,移除未使用的程式碼
npm install -D esbuild
esbuild index.js --bundle --minify --platform=node --outfile=dist/index.js
2. 避免不必要的外部 API 呼叫
// 不好:每次請求都呼叫外部 API
const response = await fetch('https://api.example.com/config');
// 較好:使用程式碼內的靜態設定,或快取結果
let cachedConfig = null;
let cacheTime = 0;
async function getConfig() {
const now = Date.now();
if (cachedConfig && now - cacheTime < 60000) {
return cachedConfig;
}
cachedConfig = await fetch('https://api.example.com/config').then(r => r.json());
cacheTime = now;
return cachedConfig;
}
3. 善用 CloudFront 快取
在 Origin Response 加入 Header,讓結果被快取:
response.headers['cache-control'] = [{
key: 'Cache-Control',
value: 'public, max-age=86400' // 快取 1 天
}];
常見問題 FAQ
Lambda@Edge 的 Log 在哪裡看?
Lambda@Edge 的 CloudWatch Logs 會寫入執行該函數的 Region,不是 us-east-1。台灣用戶的請求可能在東京(ap-northeast-1)或新加坡(ap-southeast-1)執行。你需要到各 Region 的 CloudWatch 查看,Log Group 名稱是 /aws/lambda/us-east-1.<function-name>。
Lambda@Edge 可以使用 Node.js 以外的語言嗎?
可以,Lambda@Edge 支援 Node.js 和 Python。但 CloudFront Functions 只支援 JavaScript (ES 5.1)。如果你的邏輯很簡單,建議使用 CloudFront Functions,執行速度更快、費用更低。
更新 Lambda@Edge 需要多久生效?
Lambda@Edge 更新後,需要等待 CloudFront 將新版本傳播到全球節點,通常需要 5-15 分鐘。如果急需更新,可以考慮建立新的 CloudFront Distribution。
Lambda@Edge 可以讀取 Request Body 嗎?
可以,但需要特別設定。在 CloudFront Behavior 中勾選「Include Body」,且只支援 Viewer Request 和 Origin Request 觸發點。Body 大小限制是 40 KB(Viewer)或 1 MB(Origin)。
結語
Lambda@Edge 讓你在全球 CDN 節點執行程式碼,實現低延遲的動態處理。
本文重點回顧:
- 4 個觸發點:Viewer Request/Response、Origin Request/Response,各有適用場景
- 重要限制:僅能在 us-east-1 部署、不支援環境變數和 VPC
- vs CloudFront Functions:簡單邏輯用 CF Functions,複雜邏輯用 Lambda@Edge
- 實戰應用:URL 重寫、A/B 測試、安全 Header、圖片優化、邊緣驗證
適合使用 Lambda@Edge 的場景:
- 需要在 CDN 層面處理請求/回應
- 對全球用戶延遲敏感
- 邏輯相對簡單,執行時間短
當 Lambda@Edge 遇到問題時,錯誤處理方式與一般 Lambda 略有不同,建議參考 Lambda 錯誤處理完整指南 了解基礎概念。
不適合的場景:
- 需要存取 VPC 內資源
- 執行時間超過 30 秒
- 需要大量環境變數或 Layers
CDN 與邊緣運算需要專業規劃?
如果你正在:
- 評估 Lambda@Edge 的適用場景
- 優化網站的全球存取速度
- 設計複雜的 CDN 邊緣邏輯
預約架構諮詢,我們會在 24 小時內回覆你。 正確的 CDN 架構能大幅提升用戶體驗與轉換率。
相關文章
Terraform 部署 AWS Lambda 完整教學:IaC 最佳實踐
如何用 Terraform 部署 AWS Lambda?本文完整教學 IaC 最佳實踐,包含 Module 使用、CI/CD 整合、多環境部署,幫你實現可重複的基礎設施管理。
AWS LambdaAWS Lambda 費用計算完整指南:Free Tier、計價方式與省錢攻略【2025】
AWS Lambda 怎麼收費?本文詳解 Lambda 計費模式、Free Tier 免費額度、Memory Size 成本關係,並提供實用省錢攻略,幫你找出最佳成本效益。
AWS LambdaAWS Lambda + API Gateway 整合教學:建立 REST API 完整指南
如何用 Lambda + API Gateway 建立 REST API?本文完整教學 Lambda Proxy Integration、Lambda Authorizer 設定,並比較 Function URL 的適用場景。