Uma aplicação pode estar perfeitamente construída — código limpo, banco de dados otimizado, infraestrutura resiliente — e ainda assim oferecer uma experiência ruim ao usuário se a camada de entrega não estiver bem configurada. Latência alta por distância geográfica, certificados SSL expirados, DNS mal configurado, ausência de cache para conteúdo estático — esses problemas são invisíveis para quem desenvolve localmente e devastadores para quem usa o sistema em produção.
Os três serviços abordados neste artigo formam a camada de entrega da AWS: o Route53 gerencia o DNS, o CloudFront distribui o conteúdo globalmente com baixa latência, e o ACM provê e renova certificados SSL/TLS automaticamente. Juntos, eles fazem com que uma requisição de São Paulo para uma aplicação hospedada em us-east-1 seja respondida com baixa latência, com HTTPS e com os headers de segurança corretos.
Route53: DNS como Infraestrutura
O Route53 é o serviço de DNS da AWS — altamente disponível, distribuído globalmente e integrado nativamente com os demais serviços AWS. A diferença fundamental entre o Route53 e um provedor de DNS tradicional é a capacidade de usar alias records — registros que apontam diretamente para recursos AWS como load balancers, distribuições CloudFront e buckets S3, resolvendo automaticamente para os IPs corretos sem custo de query.
Conceitos Fundamentais
Hosted Zone — o container que agrupa todos os registros DNS de um domínio. Uma hosted zone pública é acessível pela internet. Uma hosted zone privada é acessível apenas dentro de uma VPC — útil para nomes de serviços internos.
Record Types — os tipos de registro mais relevantes na AWS:
A — mapeia um nome para um endereço IPv4. Usado para apontar diretamente para IPs.
AAAA — mapeia um nome para um endereço IPv6.
CNAME — mapeia um nome para outro nome (canonical name). Não pode ser usado na raiz do domínio — empresa.com não pode ter um CNAME, mas www.empresa.com pode.
ALIAS — extensão proprietária do Route53 que se comporta como um CNAME mas pode ser usado na raiz do domínio e não gera custo de query quando aponta para recursos AWS.
MX — registros de email, apontando para servidores de correio.
TXT — texto livre, usado para verificação de domínio (Google Search Console, SPF, DKIM).
Configurando DNS com Terraform
# dns.tf
# Hosted Zone pública para o domínio principal
resource "aws_route53_zone" "principal" {
name = var.domain_name
comment = "Hosted zone principal — ${var.project_name}"
tags = local.tags_comuns
}
# Registro raiz apontando para o CloudFront
resource "aws_route53_record" "raiz" {
zone_id = aws_route53_zone.principal.zone_id
name = var.domain_name
type = "A"
alias {
name = aws_cloudfront_distribution.principal.domain_name
zone_id = aws_cloudfront_distribution.principal.hosted_zone_id
evaluate_target_health = false
}
}
# Registro www com redirect para raiz
resource "aws_route53_record" "www" {
zone_id = aws_route53_zone.principal.zone_id
name = "www.${var.domain_name}"
type = "A"
alias {
name = aws_cloudfront_distribution.principal.domain_name
zone_id = aws_cloudfront_distribution.principal.hosted_zone_id
evaluate_target_health = false
}
}
# Registro de API apontando para o ALB
resource "aws_route53_record" "api" {
zone_id = aws_route53_zone.principal.zone_id
name = "api.${var.domain_name}"
type = "A"
alias {
name = aws_lb.aplicacao.dns_name
zone_id = aws_lb.aplicacao.zone_id
evaluate_target_health = true
}
}
# Registro para ambiente de staging
resource "aws_route53_record" "staging" {
zone_id = aws_route53_zone.principal.zone_id
name = "staging.${var.domain_name}"
type = "A"
alias {
name = aws_lb.staging.dns_name
zone_id = aws_lb.staging.zone_id
evaluate_target_health = true
}
}
# Hosted Zone privada para serviços internos
resource "aws_route53_zone" "interna" {
name = "interno.${var.domain_name}"
vpc {
vpc_id = module.vpc.vpc_id
}
tags = local.tags_comuns
}
# Registro interno para o banco de dados
# Permite trocar o endpoint real sem alterar a configuração das aplicações
resource "aws_route53_record" "banco_dados" {
zone_id = aws_route53_zone.interna.zone_id
name = "db.interno.${var.domain_name}"
type = "CNAME"
ttl = 60
records = [aws_db_instance.principal.address]
}
# Registro interno para o Redis
resource "aws_route53_record" "redis" {
zone_id = aws_route53_zone.interna.zone_id
name = "cache.interno.${var.domain_name}"
type = "CNAME"
ttl = 60
records = [aws_elasticache_replication_group.redis.primary_endpoint_address]
}
Roteamento Avançado do Route53
O Route53 oferece políticas de roteamento que vão além do DNS simples. Para arquiteturas de alta disponibilidade e distribuição geográfica:
# Roteamento por latência — direciona para a região com menor latência
resource "aws_route53_record" "api_latencia_us" {
zone_id = aws_route53_zone.principal.zone_id
name = "api.${var.domain_name}"
type = "A"
set_identifier = "us-east-1"
latency_routing_policy {
region = "us-east-1"
}
alias {
name = aws_lb.api_us_east.dns_name
zone_id = aws_lb.api_us_east.zone_id
evaluate_target_health = true
}
}
resource "aws_route53_record" "api_latencia_eu" {
zone_id = aws_route53_zone.principal.zone_id
name = "api.${var.domain_name}"
type = "A"
set_identifier = "eu-west-1"
latency_routing_policy {
region = "eu-west-1"
}
alias {
name = aws_lb.api_eu_west.dns_name
zone_id = aws_lb.api_eu_west.zone_id
evaluate_target_health = true
}
}
# Health Check para failover automático
resource "aws_route53_health_check" "api_principal" {
fqdn = "api.${var.domain_name}"
port = 443
type = "HTTPS"
resource_path = "/health"
failure_threshold = 3
request_interval = 30
tags = merge(local.tags_comuns, {
Name = "api-health-check"
})
}
# Registro primário com health check
resource "aws_route53_record" "api_primario" {
zone_id = aws_route53_zone.principal.zone_id
name = "api.${var.domain_name}"
type = "A"
set_identifier = "primario"
failover_routing_policy {
type = "PRIMARY"
}
health_check_id = aws_route53_health_check.api_principal.id
alias {
name = aws_lb.aplicacao.dns_name
zone_id = aws_lb.aplicacao.zone_id
evaluate_target_health = true
}
}
# Registro secundário — ativa automaticamente se o primário falha
resource "aws_route53_record" "api_secundario" {
zone_id = aws_route53_zone.principal.zone_id
name = "api.${var.domain_name}"
type = "A"
set_identifier = "secundario"
failover_routing_policy {
type = "SECONDARY"
}
alias {
name = aws_lb.dr_region.dns_name
zone_id = aws_lb.dr_region.zone_id
evaluate_target_health = true
}
}
ACM: Certificados SSL/TLS Gerenciados
O AWS Certificate Manager provê certificados SSL/TLS gratuitos para uso com serviços AWS. Os certificados são renovados automaticamente — sem o processo manual de renovação anual que historicamente causava incidentes por certificados expirados.
# acm.tf
# Certificado para o domínio principal e subdomínios
resource "aws_acm_certificate" "principal" {
domain_name = var.domain_name
validation_method = "DNS"
subject_alternative_names = [
"*.${var.domain_name}", # Todos os subdomínios
"api.${var.domain_name}",
"staging.${var.domain_name}",
]
lifecycle {
create_before_destroy = true
}
tags = local.tags_comuns
}
# Registros DNS para validação do certificado
resource "aws_route53_record" "acm_validacao" {
for_each = {
for dvo in aws_acm_certificate.principal.domain_validation_options :
dvo.domain_name => {
name = dvo.resource_record_name
type = dvo.resource_record_type
record = dvo.resource_record_value
}
}
zone_id = aws_route53_zone.principal.zone_id
name = each.value.name
type = each.value.type
ttl = 60
records = [each.value.record]
allow_overwrite = true
}
# Aguarda a validação ser concluída
resource "aws_acm_certificate_validation" "principal" {
certificate_arn = aws_acm_certificate.principal.arn
validation_record_fqdns = [for record in aws_route53_record.acm_validacao : record.fqdn]
}
# Certificado em us-east-1 — obrigatório para CloudFront
# CloudFront aceita apenas certificados criados em us-east-1
resource "aws_acm_certificate" "cloudfront" {
provider = aws.us_east_1
domain_name = var.domain_name
validation_method = "DNS"
subject_alternative_names = [
"*.${var.domain_name}",
]
lifecycle {
create_before_destroy = true
}
tags = local.tags_comuns
}
resource "aws_acm_certificate_validation" "cloudfront" {
provider = aws.us_east_1
certificate_arn = aws_acm_certificate.cloudfront.arn
validation_record_fqdns = [for record in aws_route53_record.acm_validacao : record.fqdn]
}
CloudFront: CDN Global com Edge Locations
O CloudFront é a CDN da AWS com mais de 600 pontos de presença (edge locations) distribuídos globalmente. Quando um usuário em São Paulo acessa um conteúdo servido pelo CloudFront, a requisição vai para o edge mais próximo — não para us-east-1 diretamente. O edge verifica se tem o conteúdo em cache; se sim, responde localmente; se não, busca na origem e armazena para requisições futuras.
Para aplicações web, o CloudFront serve múltiplos propósitos além do cache: terminação SSL, proteção contra DDoS via AWS Shield Standard (gratuito), georestrição de conteúdo, redirects HTTPS forçados e adição de headers de segurança.
# cloudfront.tf
# Origin Access Control — acesso seguro ao S3 pelo CloudFront
resource "aws_cloudfront_origin_access_control" "s3" {
name = "${var.project_name}-${var.environment}-s3-oac"
description = "OAC para acesso ao S3"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
# Cache Policy para assets estáticos — cache longo
resource "aws_cloudfront_cache_policy" "assets_estaticos" {
name = "${var.project_name}-assets-estaticos"
comment = "Cache de longa duração para assets com hash no nome"
default_ttl = 86400 # 1 dia
max_ttl = 31536000 # 1 ano
min_ttl = 0
parameters_in_cache_key_and_forwarded_to_origin {
cookies_config {
cookie_behavior = "none"
}
headers_config {
header_behavior = "none"
}
query_strings_config {
query_string_behavior = "none"
}
enable_accept_encoding_brotli = true
enable_accept_encoding_gzip = true
}
}
# Cache Policy para a API — sem cache por padrão
resource "aws_cloudfront_cache_policy" "api_sem_cache" {
name = "${var.project_name}-api-sem-cache"
comment = "Sem cache para respostas dinâmicas da API"
default_ttl = 0
max_ttl = 0
min_ttl = 0
parameters_in_cache_key_and_forwarded_to_origin {
cookies_config {
cookie_behavior = "none"
}
headers_config {
header_behavior = "none"
}
query_strings_config {
query_string_behavior = "none"
}
}
}
# Origin Request Policy — headers a encaminhar para a origem
resource "aws_cloudfront_origin_request_policy" "api" {
name = "${var.project_name}-api-origin-policy"
comment = "Encaminha headers relevantes para a API"
cookies_config {
cookie_behavior = "none"
}
headers_config {
header_behavior = "whitelist"
headers {
items = [
"Authorization",
"Content-Type",
"X-Request-ID",
"X-Forwarded-For",
"CloudFront-Viewer-Country",
]
}
}
query_strings_config {
query_string_behavior = "all"
}
}
# Response Headers Policy — headers de segurança
resource "aws_cloudfront_response_headers_policy" "seguranca" {
name = "${var.project_name}-security-headers"
comment = "Headers de segurança HTTP aplicados em todas as respostas"
security_headers_config {
content_security_policy {
content_security_policy = join("; ", [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' https://fonts.gstatic.com",
"img-src 'self' data: https:",
"connect-src 'self' https://api.${var.domain_name}",
"frame-ancestors 'none'",
])
override = true
}
content_type_options {
override = true
}
frame_options {
frame_option = "DENY"
override = true
}
referrer_policy {
referrer_policy = "strict-origin-when-cross-origin"
override = true
}
strict_transport_security {
access_control_max_age_sec = 31536000
include_subdomains = true
preload = true
override = true
}
xss_protection {
mode_block = true
protection = true
override = true
}
}
custom_headers_config {
items {
header = "Permissions-Policy"
value = "camera=(), microphone=(), geolocation=()"
override = true
}
}
}
# Distribuição CloudFront principal
resource "aws_cloudfront_distribution" "principal" {
enabled = true
is_ipv6_enabled = true
comment = "${var.project_name} ${var.environment}"
default_root_object = "index.html"
price_class = "PriceClass_All"
aliases = [var.domain_name, "www.${var.domain_name}"]
web_acl_id = aws_wafv2_web_acl.cloudfront.arn
# ── Origem 1: Assets estáticos no S3 ──────────────
origin {
origin_id = "s3-assets"
domain_name = aws_s3_bucket.frontend.bucket_regional_domain_name
origin_access_control_id = aws_cloudfront_origin_access_control.s3.id
}
# ── Origem 2: API no ALB ───────────────────────────
origin {
origin_id = "alb-api"
domain_name = aws_lb.aplicacao.dns_name
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
origin_keepalive_timeout = 60
origin_read_timeout = 30
}
custom_header {
name = "X-CloudFront-Secret"
value = var.cloudfront_origin_secret
}
}
# ── Comportamento padrão: S3 com cache longo ───────
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "s3-assets"
viewer_protocol_policy = "redirect-to-https"
compress = true
cache_policy_id = aws_cloudfront_cache_policy.assets_estaticos.id
response_headers_policy_id = aws_cloudfront_response_headers_policy.seguranca.id
}
# ── Comportamento para a API: sem cache, encaminha tudo ─
ordered_cache_behavior {
path_pattern = "/api/*"
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "alb-api"
viewer_protocol_policy = "redirect-to-https"
compress = true
cache_policy_id = aws_cloudfront_cache_policy.api_sem_cache.id
origin_request_policy_id = aws_cloudfront_origin_request_policy.api.id
response_headers_policy_id = aws_cloudfront_response_headers_policy.seguranca.id
}
# ── Comportamento para assets com hash: cache máximo ─
ordered_cache_behavior {
path_pattern = "/static/*"
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "s3-assets"
viewer_protocol_policy = "redirect-to-https"
compress = true
cache_policy_id = aws_cloudfront_cache_policy.assets_estaticos.id
response_headers_policy_id = aws_cloudfront_response_headers_policy.seguranca.id
}
# SPA: redireciona 404 e 403 do S3 para index.html
custom_error_response {
error_code = 404
response_code = 200
response_page_path = "/index.html"
error_caching_min_ttl = 10
}
custom_error_response {
error_code = 403
response_code = 200
response_page_path = "/index.html"
error_caching_min_ttl = 10
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
acm_certificate_arn = aws_acm_certificate_validation.cloudfront.certificate_arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
logging_config {
include_cookies = false
bucket = "${aws_s3_bucket.cloudfront_logs.id}.s3.amazonaws.com"
prefix = "cloudfront/${var.environment}/"
}
tags = local.tags_comuns
}
# Bucket S3 para o frontend
resource "aws_s3_bucket" "frontend" {
bucket = "${var.project_name}-${var.environment}-frontend"
tags = local.tags_comuns
}
resource "aws_s3_bucket_public_access_block" "frontend" {
bucket = aws_s3_bucket.frontend.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# Política do bucket — permite acesso apenas pelo CloudFront OAC
resource "aws_s3_bucket_policy" "frontend" {
bucket = aws_s3_bucket.frontend.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "AllowCloudFrontOAC"
Effect = "Allow"
Principal = {
Service = "cloudfront.amazonaws.com"
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.frontend.arn}/*"
Condition = {
StringEquals = {
"AWS:SourceArn" = aws_cloudfront_distribution.principal.arn
}
}
}]
})
}
Deploy do Frontend no CloudFront
O pipeline de deploy do frontend sincroniza os arquivos com o S3 e invalida o cache do CloudFront para que a nova versão seja servida imediatamente:
# .github/workflows/deploy-frontend.yml
name: Deploy Frontend
on:
push:
branches: [main]
paths: ['frontend/**']
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Instala dependências
run: npm ci
- name: Build de produção
run: npm run build
env:
VITE_API_URL: https://api.${{ vars.DOMAIN_NAME }}
VITE_APP_VERSION: ${{ github.sha }}
- name: Configura credenciais AWS
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
aws-region: us-east-1
- name: Sincroniza assets estáticos com cache longo
run: |
# Assets com hash no nome — cache de 1 ano
aws s3 sync dist/assets/ \
s3://${{ vars.FRONTEND_BUCKET }}/assets/ \
--cache-control "public, max-age=31536000, immutable" \
--delete
- name: Sincroniza HTML e arquivos de configuração
run: |
# HTML e outros arquivos sem hash — sem cache
aws s3 sync dist/ \
s3://${{ vars.FRONTEND_BUCKET }}/ \
--exclude "assets/*" \
--cache-control "no-cache, no-store, must-revalidate" \
--delete
- name: Invalida cache do CloudFront
run: |
INVALIDATION_ID=$(aws cloudfront create-invalidation \
--distribution-id ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }} \
--paths "/*" \
--query 'Invalidation.Id' \
--output text)
echo "Invalidação criada: $INVALIDATION_ID"
# Aguarda a invalidação completar
aws cloudfront wait invalidation-completed \
--distribution-id ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }} \
--id $INVALIDATION_ID
echo "Cache invalidado com sucesso"
- name: Verifica deploy
run: |
sleep 10
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
https://${{ vars.DOMAIN_NAME }})
if [ "$STATUS" != "200" ]; then
echo "ERRO: Site retornou status $STATUS"
exit 1
fi
echo "Deploy verificado — status $STATUS"
WAF: Proteção da Camada de Aplicação
O AWS WAF (Web Application Firewall) protege o CloudFront contra ataques comuns na camada de aplicação — SQL injection, XSS, bots maliciosos, requisições de IPs com reputação ruim:
# waf.tf
resource "aws_wafv2_web_acl" "cloudfront" {
provider = aws.us_east_1 # WAF para CloudFront deve ser em us-east-1
name = "${var.project_name}-${var.environment}-waf"
scope = "CLOUDFRONT"
default_action {
allow {}
}
# ── Regras gerenciadas pela AWS ────────────────────
# Proteção contra ameaças comuns (OWASP Top 10)
rule {
name = "aws-managed-common-rules"
priority = 10
override_action { none {} }
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "aws-managed-common"
sampled_requests_enabled = true
}
}
# Proteção contra bots maliciosos
rule {
name = "aws-managed-bot-control"
priority = 20
override_action { none {} }
statement {
managed_rule_group_statement {
name = "AWSManagedRulesBotControlRuleSet"
vendor_name = "AWS"
managed_rule_group_configs {
aws_managed_rules_bot_control_rule_set {
inspection_level = "COMMON"
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "aws-managed-bot-control"
sampled_requests_enabled = true
}
}
# ── Regras customizadas ────────────────────────────
# Rate limiting global — máximo de 1000 req/5min por IP
rule {
name = "rate-limit-global"
priority = 30
action { block {} }
statement {
rate_based_statement {
limit = 1000
aggregate_key_type = "IP"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "rate-limit-global"
sampled_requests_enabled = true
}
}
# Rate limiting agressivo no endpoint de autenticação
rule {
name = "rate-limit-auth"
priority = 25
action { block {} }
statement {
rate_based_statement {
limit = 50
aggregate_key_type = "IP"
scope_down_statement {
byte_match_statement {
field_to_match {
uri_path {}
}
positional_constraint = "STARTS_WITH"
search_string = "/api/auth"
text_transformation {
priority = 0
type = "LOWERCASE"
}
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "rate-limit-auth"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.project_name}-waf"
sampled_requests_enabled = true
}
tags = local.tags_comuns
}
Outputs e Configurações para a Aplicação
# outputs.tf
output "cloudfront_url" {
description = "URL da distribuição CloudFront"
value = "https://${aws_cloudfront_distribution.principal.domain_name}"
}
output "url_producao" {
description = "URL pública de produção"
value = "https://${var.domain_name}"
}
output "nameservers" {
description = "Nameservers do Route53 — configurar no registrador de domínio"
value = aws_route53_zone.principal.name_servers
}
output "cloudfront_distribution_id" {
description = "ID da distribuição CloudFront — necessário para invalidação de cache"
value = aws_cloudfront_distribution.principal.id
}
output "frontend_bucket" {
description = "Nome do bucket S3 do frontend"
value = aws_s3_bucket.frontend.id
}
Encerrando o Módulo 7
Com este artigo encerra-se o Módulo 7 — AWS em Profundidade. Foram cobertos os serviços fundamentais de computação, banco de dados, cache e rede que formam a espinha dorsal de qualquer aplicação de produção na AWS: EC2 com suas famílias e ciclo de vida, ECS e Lambda para computação gerenciada, RDS e ElastiCache para dados e caching, e Route53, CloudFront e ACM para entrega e segurança.
O Módulo 8 aborda Kubernetes — o orquestrador de containers que, quando a escala e a complexidade justificam, oferece controle e flexibilidade além do que os serviços gerenciados da AWS proporcionam isoladamente.
Referências para Aprofundamento
Documentação oficial AWS - Amazon Route53 Documentation — docs.aws.amazon.com — Documentação completa do Route53, incluindo tipos de registros, políticas de roteamento e health checks. - Amazon CloudFront Documentation — docs.aws.amazon.com — Referência completa do CloudFront, cobrindo origens, behaviors, cache policies e funções Lambda@Edge. - AWS Certificate Manager — docs.aws.amazon.com — Documentação do ACM com guia de validação DNS, renovação automática e integração com serviços AWS.
Segurança - AWS WAF Documentation — docs.aws.amazon.com — Documentação completa do WAF, incluindo regras gerenciadas, rate limiting e integração com CloudFront e ALB. - OWASP Secure Headers Project — owasp.org — Referência completa dos headers de segurança HTTP recomendados pelo OWASP, com valores recomendados e explicações de cada header.
Performance - CloudFront Cache Optimization — docs.aws.amazon.com — Guia oficial para maximizar o cache hit ratio do CloudFront, incluindo configuração de TTLs, compressão e cache policies.