Sessão 025 — Lambda@Edge vs CloudFront Functions: casos de uso e limites
Duração estimada: 60 minutos
Pré-requisitos: session-024-lambda-observabilidade-xray-insights
Objetivo
Ao final, você conseguirá escolher entre Lambda@Edge e CloudFront Functions para um requisito dado (baseado em latência de execução, acesso ao body, número de regiões, custo e tempo de deploy), implementar um header injection simples com CloudFront Functions, e entender os limites de timeout e memória de cada opção.
Contexto
[FATO] CloudFront é a CDN (Content Delivery Network) da AWS. Ela distribui conteúdo a partir de mais de 600 pontos de presença (PoPs) ao redor do mundo, chamados edge locations. Quando uma requisição chega ao CloudFront, ela é processada no edge location mais próximo do usuário — não na região AWS da origem. Isso significa que qualquer lógica executada no edge tem latência radicalmente menor do que uma chamada de volta à origem.
[FATO] Dois serviços permitem executar código no edge do CloudFront: CloudFront Functions (lançado em 2021) e Lambda@Edge (lançado em 2017). Eles diferem fundamentalmente em capacidade, latência, custo e casos de uso. Não são concorrentes diretos — cada um resolve uma classe diferente de problemas.
[CONSENSO] A regra geral adotada pela maioria das arquiteturas de produção: se a lógica é simples (manipulação de headers, URL rewrites, normalização de cache keys) e roda em cada request, use CloudFront Functions. Se a lógica é complexa (acesso ao body, chamadas a serviços externos, autenticação JWT completa, decisões baseadas em banco de dados), use Lambda@Edge. O custo de CloudFront Functions é aproximadamente 1/6 do Lambda@Edge por requisição.
Conceitos principais
1. Os quatro pontos de interceptação do CloudFront
[FATO] O CloudFront intercepta o fluxo HTTP em quatro pontos distintos. Cada ponto tem características diferentes e suporta diferentes tipos de edge function:
Usuário
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ CLOUDFRONT EDGE LOCATION │
│ │
│ ① viewer-request ② viewer-response │
│ Antes do cache check Antes de retornar ao usuário │
│ (CloudFront Functions ✓) (CloudFront Functions ✓) │
│ (Lambda@Edge ✓) (Lambda@Edge ✓) │
│ │ ▲ │
│ ▼ │ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ Cache hit? │──── HIT ─────────────────►│ Serve do cache │ │
│ └──────┬───────┘ └─────────────────┘ │
│ │ MISS │
│ ▼ │
│ ③ origin-request ④ origin-response │
│ Antes de ir à origem Após receber da origem (e cachear) │
│ (Lambda@Edge ✓ apenas) (Lambda@Edge ✓ apenas) │
│ │ ▲ │
└─────────┼────────────────────────────────────┼──────────────────────────────┘
│ │
▼ │
ORIGEM (S3, ALB, EC2, API Gateway...)───┘
[FATO] CloudFront Functions só pode ser associado a viewer-request e viewer-response. Lambda@Edge pode ser associado aos quatro eventos. Essa distinção é fundamental: apenas Lambda@Edge pode interceptar e modificar requests/responses para a origem.
2. CloudFront Functions — sub-millisegundo, escala massiva, limitações estritas
[FATO] CloudFront Functions executa código JavaScript (ES2015+) em um runtime próprio — não é Node.js. É uma engine JavaScript leve, sem módulos npm, sem acesso à rede, sem timers assíncronos, sem require(). O código deve ser síncrono e terminar em menos de 1ms de compute time.
Limites e capacidades
[FATO] Limites documentados:
┌──────────────────────────────┬──────────────────────────────────────────┐
│ Limite │ Valor │
├──────────────────────────────┼──────────────────────────────────────────┤
│ Tempo máximo de execução │ ~1ms (compute utilization 0-100) │
│ Memória │ 2 MB │
│ Tamanho máximo do código │ 10 KB │
│ Runtime │ JavaScript (ES2015+) — NÃO é Node.js │
│ Acesso ao body da requisição │ NÃO │
│ Chamadas de rede │ NÃO │
│ Variáveis de ambiente │ NÃO (use Key Value Store) │
│ Acesso a sistema de arquivos │ NÃO │
│ Eventos suportados │ viewer-request, viewer-response │
│ Escala │ Milhões de req/s instantaneamente │
│ Deploy │ CloudFront nativo (não via Lambda) │
│ Teste integrado │ Console CloudFront │
└──────────────────────────────┴──────────────────────────────────────────┘
Estrutura do evento (CloudFront Functions)
[FATO] O objeto de evento tem estrutura diferente do Lambda@Edge:
// evento recebido pela função (viewer-request)
{
"version": "1.0",
"context": {
"distributionDomainName": "d111111abcdef8.cloudfront.net",
"distributionId": "EDFDVBD6EXAMPLE",
"eventType": "viewer-request",
"requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
},
"viewer": {
"ip": "1.2.3.4"
},
"request": {
"method": "GET",
"uri": "/index.html",
"querystring": {
"cat": { "value": "meow" }
},
"headers": {
"host": { "value": "www.example.com" },
"accept-language": { "value": "pt-BR,pt;q=0.9" }
},
"cookies": {
"session": { "value": "abc123" }
}
}
}
[FATO] A função deve retornar o objeto request (para viewer-request) ou response (para viewer-response). Se retornar um objeto response em viewer-request, a requisição é interrompida — nunca chega ao cache ou à origem.
Exemplos de CloudFront Functions
// 1. Injetar Security Headers (viewer-response)
function handler(event) {
var response = event.response;
var headers = response.headers;
headers['strict-transport-security'] = { value: 'max-age=31536000; includeSubdomains; preload' };
headers['x-content-type-options'] = { value: 'nosniff' };
headers['x-frame-options'] = { value: 'DENY' };
headers['x-xss-protection'] = { value: '1; mode=block' };
headers['referrer-policy'] = { value: 'strict-origin-when-cross-origin' };
headers['permissions-policy'] = { value: 'camera=(), microphone=(), geolocation=()' };
return response;
}
// 2. Normalizar cache key — remover query strings irrelevantes (viewer-request)
// Sem isso, "?utm_source=email" e "?utm_source=social" geram cache misses separados
function handler(event) {
var request = event.request;
var qs = request.querystring;
// Remove parâmetros de tracking — não afetam o conteúdo
var tracking = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'fbclid', 'gclid'];
tracking.forEach(function(param) { delete qs[param]; });
return request;
}
// 3. URL rewrite — SPA com todas as rotas servindo index.html (viewer-request)
function handler(event) {
var request = event.request;
var uri = request.uri;
// Se não tem extensão de arquivo, serve index.html
if (!uri.includes('.')) {
request.uri = '/index.html';
}
return request;
}
// 4. Redirect HTTP → HTTPS e www → apex (viewer-request)
function handler(event) {
var request = event.request;
var headers = request.headers;
var host = headers.host ? headers.host.value : '';
// Redireciona www para apex
if (host.startsWith('www.')) {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
'location': { value: 'https://' + host.slice(4) + request.uri }
}
};
}
return request;
}
// 5. A/B testing via cookie (viewer-request)
// Atribui usuários novos a uma variante e redireciona para o path correto
function handler(event) {
var request = event.request;
var cookies = request.cookies;
// Usuário já tem variante atribuída
if (cookies['ab-variant']) {
var variant = cookies['ab-variant'].value;
request.uri = '/' + variant + request.uri;
return request;
}
// Atribui nova variante (50/50)
var variant = Math.random() < 0.5 ? 'a' : 'b';
request.uri = '/' + variant + request.uri;
// Retorna response para definir o cookie — próximo request não será atribuído
return {
statusCode: 302,
statusDescription: 'Found',
headers: {
'location': { value: request.uri },
'set-cookie': { value: 'ab-variant=' + variant + '; Path=/; Max-Age=2592000' }
}
};
}
3. Lambda@Edge — poder completo, complexidade operacional maior
[FATO] Lambda@Edge são funções Lambda comuns, com as seguintes restrições específicas para execução no edge:
┌─────────────────────────────────┬──────────────────────┬──────────────────────┐
│ Limite │ Viewer events │ Origin events │
├─────────────────────────────────┼──────────────────────┼──────────────────────┤
│ Timeout máximo │ 5 segundos │ 30 segundos │
├─────────────────────────────────┼──────────────────────┼──────────────────────┤
│ Memória máxima │ 128 MB │ 128 MB – 10.240 MB │
├─────────────────────────────────┼──────────────────────┼──────────────────────┤
│ Tamanho do package (comprimido) │ 1 MB │ 50 MB │
├─────────────────────────────────┼──────────────────────┼──────────────────────┤
│ Acesso ao body │ Sim (40 KB) │ Sim (1 MB) │
├─────────────────────────────────┼──────────────────────┼──────────────────────┤
│ Chamadas de rede │ Sim │ Sim │
├─────────────────────────────────┼──────────────────────┼──────────────────────┤
│ Variáveis de ambiente │ NÃO │ NÃO │
├─────────────────────────────────┼──────────────────────┼──────────────────────┤
│ Layers Lambda │ NÃO │ NÃO │
├─────────────────────────────────┼──────────────────────┼──────────────────────┤
│ Runtime │ Node.js, Python │ Node.js, Python │
├─────────────────────────────────┼──────────────────────┼──────────────────────┤
│ Deploy │ us-east-1 apenas │ us-east-1 apenas │
└─────────────────────────────────┴──────────────────────┴──────────────────────┘
[FATO] Lambda@Edge deve ser deployada na região us-east-1. A replicação para todos os edge locations do mundo é feita automaticamente pelo CloudFront ao associar a função a uma distribuição. Você gerencia apenas a função em us-east-1.
[FATO] Lambda@Edge não suporta variáveis de ambiente. Configurações que normalmente iriam em environment variables devem ser hardcoded, buscar de SSM Parameter Store/Secrets Manager em runtime (com cache no init phase), ou injetadas via CloudFormation ao fazer o deploy.
[FATO] A execution role da função Lambda@Edge deve ter como trusted principal tanto lambda.amazonaws.com quanto edgelambda.amazonaws.com:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": ["lambda.amazonaws.com", "edgelambda.amazonaws.com"]
},
"Action": "sts:AssumeRole"
}]
}
Estrutura do evento (Lambda@Edge)
[FATO] O evento tem uma estrutura mais verbosa — os dados estão aninhados em Records[0].cf:
# viewer-request event (Python)
{
"Records": [{
"cf": {
"config": {
"distributionDomainName": "d111111abcdef8.cloudfront.net",
"distributionId": "EDFDVBD6EXAMPLE",
"eventType": "viewer-request",
"requestId": "4TyzHTa..."
},
"request": {
"clientIp": "203.0.113.178",
"method": "GET",
"uri": "/picture.jpg",
"querystring": "size=large&color=red",
"headers": {
"host": [{ "key": "Host", "value": "d111111abcdef8.cloudfront.net" }],
"accept-language": [{ "key": "Accept-Language", "value": "en-US,en;q=0.5" }]
},
"body": { # apenas se include_body=True e método POST/PUT
"inputTruncated": False,
"action": "read-only",
"encoding": "base64",
"data": "aGVsbG8="
}
}
}
}]
}
[FATO] A função Lambda@Edge deve retornar um objeto com a mesma estrutura do request ou response (sem o wrapper Records). Para interromper a requisição, retorne um objeto response:
# Exemplo: autenticação JWT em viewer-request (Lambda@Edge)
import json
import base64
import hmac
import hashlib
# Cache da chave pública (inicializado no init phase, reusado em warm starts)
_JWT_SECRET = None
def get_secret():
global _JWT_SECRET
if _JWT_SECRET is None:
import boto3
# NOTA: Lambda@Edge em us-east-1, SSM deve estar em us-east-1 também
ssm = boto3.client('ssm', region_name='us-east-1')
_JWT_SECRET = ssm.get_parameter(
Name='/app/jwt-secret', WithDecryption=True
)['Parameter']['Value']
return _JWT_SECRET
def handler(event, context):
request = event['Records'][0]['cf']['request']
headers = request['headers']
# Extrai token do header Authorization
auth_header = headers.get('authorization', [{}])[0].get('value', '')
if not auth_header.startswith('Bearer '):
return unauthorized_response()
token = auth_header[7:]
try:
payload = verify_jwt(token, get_secret())
# Injeta user_id como header para a origem
request['headers']['x-user-id'] = [{
'key': 'X-User-Id',
'value': payload['sub']
}]
return request
except Exception:
return unauthorized_response()
def verify_jwt(token, secret):
parts = token.split('.')
if len(parts) != 3:
raise ValueError("Invalid JWT")
header_payload = f"{parts[0]}.{parts[1]}"
signature = base64.urlsafe_b64decode(parts[2] + '==')
expected = hmac.new(secret.encode(), header_payload.encode(), hashlib.sha256).digest()
if not hmac.compare_digest(signature, expected):
raise ValueError("Invalid signature")
payload = json.loads(base64.urlsafe_b64decode(parts[1] + '=='))
return payload
def unauthorized_response():
return {
'status': '401',
'statusDescription': 'Unauthorized',
'headers': {
'www-authenticate': [{ 'key': 'WWW-Authenticate', 'value': 'Bearer' }],
'content-type': [{ 'key': 'Content-Type', 'value': 'application/json' }]
},
'body': json.dumps({ 'error': 'Unauthorized' })
}
4. Tabela de decisão — CloudFront Functions vs Lambda@Edge
[FATO] A documentação oficial da AWS resume a decisão em:
USE CloudFront Functions quando: USE Lambda@Edge quando:
────────────────────────────────────────── ──────────────────────────────────────────
✓ URL normalization e rewrites ✓ Autenticação/autorização complexa
✓ Manipulação de headers (add/remove/mod) ✓ Lógica dependente do body da requisição
✓ Normalizar cache keys (remover params) ✓ Chamadas a AWS (DynamoDB, SSM, etc.)
✓ Cookie manipulation simples ✓ Resposta diferente por geolocalização
✓ A/B redirect por cookie ✓ Processamento de imagens
✓ Redirecionar HTTP → HTTPS ✓ Geração de respostas dinâmicas
✓ Injetar security headers ✓ Reescrita de URL baseada em dados externos
✓ Custo mínimo (milhões de req/s) ✓ Interceptar origin-request/response
✓ Deploy instantâneo ✓ Modificar request antes de ir à S3/ALB
Custo comparativo (aproximado):
CloudFront Functions:
$0.10 por 1.000.000 de invocações
Lambda@Edge:
$0.60 por 1.000.000 de invocações (viewer events)
+ $0.00000625125 por GB-segundo
Exemplo: 100M requests/mês, 1ms avg:
CloudFront Functions: $10/mês
Lambda@Edge: $60/mês + duração ≈ $65/mês
Diferença: ~6,5x mais caro
5. Deploy via CDK
[FATO] Lambda@Edge deve ser definida em us-east-1. Em uma CDK app multi-stack, isso requer uma stack separada deployada nessa região:
from aws_cdk import (
App, Stack, Environment,
aws_lambda as lambda_,
aws_cloudfront as cloudfront,
aws_cloudfront_origins as origins,
aws_s3 as s3,
Duration,
)
# ── Stack de Edge Functions (deve estar em us-east-1) ──────────────────────────
class EdgeFunctionsStack(Stack):
def __init__(self, scope, construct_id, **kwargs):
super().__init__(scope, construct_id, **kwargs)
# Lambda@Edge — viewer-request para autenticação JWT
self.auth_function = cloudfront.experimental.EdgeFunction(
self, "AuthFunction",
runtime=lambda_.Runtime.PYTHON_3_12,
handler="auth.handler",
code=lambda_.Code.from_asset("src/edge/auth"),
timeout=Duration.seconds(5),
memory_size=128,
description="JWT authentication at edge",
# Não suporta: environment_variables, layers
)
# ── Stack principal da aplicação ───────────────────────────────────────────────
class AppStack(Stack):
def __init__(self, scope, construct_id, edge_stack: EdgeFunctionsStack, **kwargs):
super().__init__(scope, construct_id, **kwargs)
bucket = s3.Bucket(self, "StaticAssets")
# CloudFront Function — injetar security headers (nativa, não Lambda)
security_headers_fn = cloudfront.Function(
self, "SecurityHeadersFn",
code=cloudfront.FunctionCode.from_inline("""
function handler(event) {
var response = event.response;
var headers = response.headers;
headers['strict-transport-security'] = { value: 'max-age=31536000; includeSubdomains' };
headers['x-content-type-options'] = { value: 'nosniff' };
headers['x-frame-options'] = { value: 'DENY' };
return response;
}
"""),
function_name="SecurityHeadersInjection",
comment="Inject security headers on all responses",
)
distribution = cloudfront.Distribution(
self, "Distribution",
default_behavior=cloudfront.BehaviorOptions(
origin=origins.S3BucketOrigin.with_origin_access_control(bucket),
viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED,
# Lambda@Edge: autenticação JWT em viewer-request
edge_lambdas=[
cloudfront.EdgeLambda(
function_version=edge_stack.auth_function.current_version,
event_type=cloudfront.LambdaEdgeEventType.VIEWER_REQUEST,
include_body=False,
)
],
# CloudFront Function: security headers em viewer-response
function_associations=[
cloudfront.FunctionAssociation(
function=security_headers_fn,
event_type=cloudfront.FunctionEventType.VIEWER_RESPONSE,
)
],
),
)
# ── App com múltiplas stacks ───────────────────────────────────────────────────
app = App()
edge_stack = EdgeFunctionsStack(
app, "EdgeFunctionsStack",
env=Environment(region="us-east-1") # OBRIGATÓRIO para Lambda@Edge
)
app_stack = AppStack(
app, "AppStack",
edge_stack=edge_stack,
env=Environment(region="sa-east-1") # Região principal da aplicação
)
app_stack.add_dependency(edge_stack)
app.synth()
[FATO] cloudfront.experimental.EdgeFunction no CDK cria automaticamente a função em us-east-1 (independente da região da stack onde é usado), replica para os edge locations, e configura o trust policy correto com edgelambda.amazonaws.com.
Exemplo prático
Cenário: Plataforma de conteúdo com três requisitos: (a) todos os responses devem ter security headers, (b) usuários autenticados veem conteúdo personalizado — o origin precisa saber o user_id, (c) arquivos de imagem devem ser servidos com cache longo sem que parâmetros de analytics poluam o cache.
Solução arquitetada:
viewer-request (CloudFront Function):
→ Remove query params de tracking (utm_*, fbclid)
→ Normaliza cache key
viewer-request (Lambda@Edge — somente paths /api/*):
→ Verifica JWT
→ Injeta X-User-Id no request para a origem
viewer-response (CloudFront Function):
→ Injeta security headers em todos os responses
// cloudfront-function-normalize.js (viewer-request)
// Aplica ao behavior padrão (assets estáticos)
function handler(event) {
var request = event.request;
var qs = request.querystring;
// Remove tracking params que não afetam o conteúdo
['utm_source','utm_medium','utm_campaign','utm_content',
'utm_term','fbclid','gclid','_ga'].forEach(function(p) {
delete qs[p];
});
// Normaliza Accept-Language para pt ou en (reduz variações de cache)
var lang = request.headers['accept-language'];
if (lang) {
var value = lang.value || '';
request.headers['cf-accept-language'] = {
value: value.startsWith('pt') ? 'pt' : 'en'
};
}
return request;
}
# lambda_edge_auth.py (viewer-request — behavior /api/*)
import json, os, base64, hmac, hashlib, boto3
_SECRET = None
def _load_secret():
global _SECRET
if _SECRET is None:
ssm = boto3.client('ssm', region_name='us-east-1')
_SECRET = ssm.get_parameter(
Name='/plataforma/jwt-secret', WithDecryption=True
)['Parameter']['Value']
return _SECRET
def handler(event, context):
request = event['Records'][0]['cf']['request']
headers = request.get('headers', {})
auth = headers.get('authorization', [{}])[0].get('value', '')
if not auth.startswith('Bearer '):
return _deny()
try:
payload = _decode_jwt(auth[7:], _load_secret())
request['headers']['x-user-id'] = [{'key':'X-User-Id','value': payload['sub']}]
request['headers']['x-user-plan'] = [{'key':'X-User-Plan','value': payload.get('plan','free')}]
return request
except Exception:
return _deny()
def _decode_jwt(token, secret):
h, p, s = token.split('.')
sig = base64.urlsafe_b64decode(s + '==')
expected = hmac.new(secret.encode(), f"{h}.{p}".encode(), hashlib.sha256).digest()
if not hmac.compare_digest(sig, expected):
raise ValueError('bad sig')
return json.loads(base64.urlsafe_b64decode(p + '=='))
def _deny():
return {
'status': '401',
'statusDescription': 'Unauthorized',
'headers': {'content-type': [{'key':'Content-Type','value':'application/json'}]},
'body': '{"error":"Unauthorized"}'
}
Armadilhas comuns
Armadilha 1 — Usar variáveis de ambiente em Lambda@Edge
O erro: O desenvolvedor cria a função Lambda@Edge com environment_variables={"JWT_SECRET": "..."} no CDK. O deploy falha com erro: Lambda@Edge does not support environment variables.
Por que acontece: Lambda@Edge replica a função para dezenas de edge locations globais. Variáveis de ambiente são regionais — não há mecanismo para replicá-las junto com o código para cada PoP.
Como evitar: Três alternativas em ordem de preferência:
1. SSM Parameter Store / Secrets Manager (lido no init phase, cacheado em variável global)
2. Hardcode para valores não-sensíveis e estáticos
3. CloudFront Key Value Store — um KV store gerenciado da AWS disponível em CloudFront Functions (não Lambda@Edge) para pares chave-valor leves
Para Lambda@Edge, a opção 1 é a padrão de produção: a primeira invocação (cold start) busca o segredo, as subsequentes (warm) reusam o valor cacheado em memória. O custo extra é uma chamada SSM por cold start.
Armadilha 2 — Deploy em região diferente de us-east-1 para Lambda@Edge
O erro: O desenvolvedor cria a função Lambda em sa-east-1 (São Paulo) porque é a região principal da aplicação, depois tenta associá-la ao CloudFront. O erro é: InvalidLambdaFunctionAssociation: The function ARN must be in the same account as the distribution and must be in us-east-1.
Por que acontece: O sistema de replicação do Lambda@Edge é gerenciado a partir de us-east-1. A AWS precisa de uma função "mestre" em us-east-1 para replicar para os edge locations globais.
Como evitar:
- Use cloudfront.experimental.EdgeFunction no CDK — ele garante o deploy em us-east-1 automaticamente, independente da região da stack.
- Se usar Lambda diretamente, crie a função manualmente em us-east-1 e referencie o ARN com a versão publicada (não $LATEST).
- Nunca use $LATEST em Lambda@Edge — apenas versões publicadas são suportadas.
Armadilha 3 — Esquecer que CloudFront Functions é JavaScript síncrono (não Node.js)
O erro: O desenvolvedor escreve código com async/await, fetch(), require(), ou acessa process.env. O código falha com erros como ReferenceError: fetch is not defined ou SyntaxError: Unexpected token.
Por que acontece: CloudFront Functions usa uma engine JavaScript própria (não Node.js). Não há process, não há require, não há I/O assíncrono. O runtime é intencionalmente minimalista para garantir execução sub-milissegundo.
Como evitar:
- Código síncrono apenas — sem async, sem Promises, sem callbacks de I/O
- Sem require() — todo o código deve estar em um único arquivo
- Sem fetch ou qualquer chamada de rede — se precisar de dados externos, use Lambda@Edge
- Teste sempre no console CloudFront antes de associar à distribuição — ele detecta esses erros antes do deploy
- Para configurações dinâmicas, use CloudFront Key Value Store (API do runtime: CloudFrontFunction.cf.kvs.get())
Exercício de reflexão
Você está redesenhando a camada de edge de uma plataforma de streaming de vídeo. Os requisitos são: (1) bloquear requisições de IPs em uma lista de bloqueio que é atualizada diariamente, (2) adicionar um header X-Country baseado na geolocalização do CloudFront, (3) verificar se o usuário tem um plano ativo consultando o DynamoDB antes de servir vídeos Premium, e (4) adicionar headers de cache corretos (Cache-Control) em todas as respostas.
Questão: Para cada um dos quatro requisitos, você usaria CloudFront Functions ou Lambda@Edge? Em qual evento (viewer-request, origin-request, etc.)? Justifique sua escolha considerando latência, custo, acesso a dados externos e impacto no cache. Em particular, para o requisito 3, como você evitaria que a verificação no DynamoDB aconteça a cada request de um usuário já autenticado?
Recursos para aprofundar
-
Differences between CloudFront Functions and Lambda@Edge
URL: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions-choosing.html
A tabela comparativa oficial entre os dois serviços, com todos os limites quantitativos (timeout, memória, package size, eventos, body access) e recomendações de caso de uso. É a primeira página a consultar quando precisar decidir entre os dois. -
Customize at the edge with CloudFront Functions
URL: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html
Documentação completa de CloudFront Functions: estrutura do evento, runtime JavaScript, como criar e testar no console, Key Value Store, e galeria de exemplos de uso (redirect, header manipulation, A/B testing). Inclui o tutorial passo-a-passo para criar a primeira função. -
Customize at the edge with Lambda@Edge
URL: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html
Guia completo de Lambda@Edge: os quatro event types, estrutura dos eventos de request e response, como configurar triggers, restrições específicas (us-east-1, sem env vars, sem layers), e exemplos avançados (autenticação, geração de responses, manipulação de body).