← Back to list

Distribuindo uma SPA através de uma CDN de alta disponibilidade e segurança na AWS

Utilizando Route53, S3, CloudFront, ACM, Lambda@edge e Serverless Framework.

João Emboaba · 2025-02-28 05:02 · 6 claps · 14.5 min read
#route-53 #cloudfront #s3 #lambda-edge #aws-certificate-manager
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

Distribuindo uma SPA através de uma CDN de alta disponibilidade e segurança na AWS

Utilizando Route53, S3, CloudFront, ACM, Lambda@edge e Serverless Framework.

Desenho arquitetural da CDN

Desenho arquitetural da CDN

Neste artigo, vamos passar por alguns conceitos de Cloud, distribuição de conteúdo, DNS, IaC, Name Servers, Serverless e segurança na web para podermos implementar a nossa CDN de forma correta e sem dúvidas; Mas primeiro, um overview dos serviços e stacks que irão ser utilizadas aqui:

Para gerenciar os domínios e direcionar o tráfego, utilizamos o Route 53, o serviço de DNS da AWS. Com ele, podemos configurar registros que apontam para diferentes recursos, como um bucket no S3, um Load Balancer ou uma distribuição do CloudFront, garantindo que os usuários sejam direcionados corretamente para a origem dos conteúdos.

Para garantir que a comunicação entre usuários e a CDN seja segura, utilizamos o AWS Certificate Manager (ACM), que permite a emissão e gerenciamento de certificados SSL/TLS sem custo adicional. Com o ACM, conseguimos ativar o HTTPS em nosso domínio sem precisar lidar manualmente com a renovação dos certificados, pois o serviço faz isso automaticamente.

O Lambda@Edge entra como um complemento poderoso ao CloudFront, permitindo a execução de código nas bordas da rede, antes que uma solicitação chegue à origem. Isso possibilita a modificação dinâmica das requisições e respostas, a inserção de headers de segurança, a personalização de conteúdos com base na geolocalização do usuário e até mesmo a restrição de acessos não autorizados. Essa abordagem reduz a carga sobre os servidores de origem e melhora a segurança geral da arquitetura.

Por fim, para facilitar o gerenciamento e a automação de toda essa infraestrutura serverless, utilizamos o Serverless Framework, que permite definir e implantar funções Lambda, configurações do CloudFront e outros recursos da AWS de maneira simplificada e declarativa. Com ele, conseguimos provisionar rapidamente nossa aplicação sem precisar configurar tudo manualmente pelo console da AWS.

Partindo do pressuposto que possuímos uma conta da AWS criada e um dominío registrado, vamos começar a criação da CDN.

Dentro do console da AWS, vamos entrar no serviço do Route53 e criar uma Hosted Zone (Zona hospedada), que nada mais é do que um contêiner para registros que definem como o tráfego vai ser direcionado para os nossos domínios e subdomínios.

Vale notar que o domínio usado para criar a zona hospedada, foi registrado usando a feature domain registrar do próprio Route53, caso queira importar um domínio que já existe em outro lugar, consulte esta doc.

Agora que já criamos a Hosted zone, vamos para o próximo passo, criar o bucket no S3 para “armazenar” o site;

Na página do serviço S3, criaremos um bucket com um nome único, seguindo todas as configurações padrões

No nome, devemos dar um nome único global para o bucket.

Nessa configuração de bloquear acesso ao Bucket, bloquearemos todo e qualquer acesso público ao bucket e aos objetos contidos dentro dele, o conteúdo será apenas acessível via DNS Record do Route53.

Obs: “O Bucket Versioning é uma configuração opcional cuja função é criar versões de objetos inseridos no bucket que possuem o mesmo nome, evitando a substituição de conteúdo e mitigando o risco de perda de arquivos importantes. No entanto, é importante ressaltar que o armazenamento do bucket aumentará exponencialmente conforme novos arquivos forem enviados, especialmente com a opção de versionamento ativa. Por isso, recomenda-se avaliar a ativação das Lifecycle Rules para gerenciar os objetos do bucket de forma eficiente.”

Podemos ignorar as demais opções e criar o bucket.

Feito isso, criaremos a distribuição do CloudFront, que será o núcleo da entrega do conteúdo do bucket de forma rápida e eficiente. No console, basta digitar ‘CloudFront’ na barra de pesquisa e selecionar o serviço. Se for a primeira vez acessando ou se não houver nenhuma distribuição criada, a tela exibida será semelhante a esta:

Clicando em “Create CloudFront Distribution”, irá abrir a tela para configurar a distribuição.

Em Origin Domain, iremos escolher o bucket que criamos anteriormente, ele vai ser a origem do cache da nossa CDN.

O Origin path é um campo opcional, ele define no caso do bucket, o caminho para consultar os objetos, como aqui não informaremos nada, a consulta será na pasta “root” do bucket.

Já o Name, é mais intuititvo, sendo o nome da distribuição / origem.

Nas próximas opções temos:

1 — Origin Access: Aqui definiremos como a origem (Bucket) será acessada. Neste caso, permitiremos o acesso exclusivamente para esta distribuição do CloudFront. Para isso, criaremos um OAC (Origin Access Control), que nada mais é do que uma política gerada automaticamente para que possamos modificar as permissões de acesso do bucket.

2 — Viewer Protocol Policy: Define a política de visualização do conteúdo distrubuído.

3 — **Allowed HTTP methods: **Define quais métodos HTTPs podem ser usados ao fazer uma requisição para essa origem

1.1 — Antes de continuarmos, é necessário realizar a criação do OAC, para isso, basta clicar no botão “Create new OAC”

Com o OAC criado, podemos partir para as próximas configurações

1 — O WAF (Web Application Firewall) é uma ferramenta de segurança que protege aplicativos web contra ataques, como DoS, entre outros. Neste Hands-On, não utilizaremos o WAF, mas, em um ambiente de produção, seu uso é altamente recomendado. O custo do serviço é de aproximadamente 14 dólares para cada 10 milhões de requisições por mês. Você pode simular os custos utilizando a calculadora de preços da AWS.

2 — Alternate domain name (CNAME) — optional: Aqui podemos configurar o registro de DNS da Zona hospedada que vai redirecionar para a distribuição.

3 — Custom SSL Cerficate — optional: Para que o site tenha um certificado SSL e aceite conexões via HTTPS, é necessário emitir um certificado pelo ACM (AWS Certificate Manager), que será nosso próximo passo.

3.1 — Ao clicar no botão Request Certificate, o serviço do ACM será aberto, onde os passos para a criação do certificado são bem intuitivos.

Nesta etapa, podemos manter todas as configurações padrão, incluindo a validação via DNS e a chave de algoritmo. No campo Fully Qualified Domain Name (FQDN), é recomendável adicionar um asterisco (*.seudominio.com) antes do seu domínio de segundo nível. Isso garante que o certificado seja válido para todos os subdomínios que forem criados nesse domínio.

Após solicitar o certificado, é necessário criar o registro na zona hospedada, conforme as informações fornecidas pelo ACM. Esse passo permite que a AWS valide que temos controle total sobre o domínio.

Copiaremos o CNAME name e o CNAME value.

No serviço Route53 dentro da zona hospedada, criaremos o registro:

Cole as informações copiadas na parte de records do seu certificado

Feito isso, aguardaremos um pouco para o certificado ser validado e emitido.

Caso tenha seguido os passos corretamente, o status tanto do certificado e do record devem estar como issued e sucess.

Se passou um tempo após o processo ter sido realizado e mesmo assim o certificado ainda não foi validado, verifique se os Name Servers do seu domínio registrado está igual ao record do type NS na sua zona hospedada.

Você pode verificar os seus Name servers aqui:

Selecione o seu domínio

“No local indicado é onde vai estar os name servers do seu domínio, caso esteja importando o domínio de outro provedor, procure esses valores no painel de gerenciamento do seu domain registrar.”

Voltando para a sua zona hospedada, verifique se os names servers do seu domínio são iguais a este

Caso não seja, altere o valor desse registro com os name servers corretos.

Se, mesmo após essa configuração, o certificado ainda não for emitido ou tudo parecer estar correto, mas o problema persistir, consulte este post no AWS re:Post: https://repost.aws/knowledge-center/acm-certificate-pending-validation

Agora, voltando para a criação da distribuição, com o certificado emitido, selecionaremos o mesmo na aba de certificados:

Agora podemos criar nossa distribuição.

Logo após criarmos a distro, vai aparecer uma notificação para copiarmos a police de acesso ao bucket no S3

Clique em Copy Police.

Porém, antes de atualizarmos a política de permissionamento do bucket, vamos criar uma error page na distro

Essa configuração consiste em uma página personalizada de erro, permitindo redirecionar o usuário caso ele receba um erro de permissão negada (403).

  • Observação: O TTL (Time to Live) define o tempo que o conteúdo permanecerá em cache no navegador do usuário antes que uma nova requisição à CDN seja necessária.

Com essa configuração criada, podemos seguir para a atualização da política do S3. Para isso, retornamos ao bucket que criamos e acessamos a aba Permissions.

Clique em editar e cole a policy que copiamos anteriormente

Pronto, agora o bucket pode ser acessado via a CDN que criamos :)

Com essa configuração pronta, podemos subir o nosso frontend no S3. A escolha da tecnologia ou ferramenta para o desenvolvimento do projeto fica a critério do desenvolvedor. Para este artigo, optarei pelo Angular, criando uma interface simples que evidencie o funcionamento da CDN. No caso do Angular, basta executar o comando para construir a aplicação e posteriormente fazer o deploy dos arquivos gerados para o bucket do S3.

ng build --prod

na pasta root do projeto, ele vai compilar os arquivos e inseri-los na pasta /dist e copia-los direto para o servidor,

Com esses arquivos gerados, podemos copia-los e fazer o upload no bucket

Após realizar o upload, o último passo para testar a CDN é criar o registro na zona hospedada que configuramos anteriormente no campo ‘Alternate Domain Name (CNAME)’ na distribuição do CloudFront.

Para isso, voltamos ao Route 53, na seção de criação de registros, e configuramos um novo registro apontando para a distribuição do CloudFront.

Feito isso, podemos testar o registro e ver se a CDN funcionou, colando o nome do registro que acabamos de criar no navegador de sua preferência:

E funcionou!!!!

Vale destacar que, neste registro e em todos os outros que criarmos neste domínio, já temos o protocolo HTTPS configurado. Ao selecionarmos o certificado, podemos verificar que ele foi emitido pela AWS por meio do ACM.

Agora que temos uma CDN de alta disponibilidade e, em teoria, “segura”, com certificado SSL e orquestrada pela infraestrutura da AWS, vamos verificar a pontuação dela no HTTP Observatory Report. Esse site analisa os registros dos nossos domínios, apontando melhores práticas, como cabeçalhos de segurança e configurações de chaves.

Ao inserir o registro que criamos, recebemos uma classificação “D”, com uma pontuação de 30 em 100. Mas por que isso?

O motivo é que o nosso registro não está implementando os cabeçalhos de segurança recomendados. Portanto, vamos adicioná-los por meio do Lambda@Edge. E é exatamente aqui que entra a segunda parte deste artigo.

Para fazer isso de forma rápida e sem utilizar o console da AWS, vamos usar o Serverless Framwork.

Em uma pasta vazia, abra o cmd e digite o comando serverless, caso não tenha instalado o framework na sua máquina, digite o comando:

npm install -g serverless

Agora com o framework instalado na sua máquina, vamos criar o projeto

Neste hands-on, criaremos a lambda com Node.js, selecionando a opção Simple Function

Na opção de criar ou selecionar um aplicativo, ao atribuir a função que estamos criando ao nosso dashboard do Serverless, podemos optar por selecionar a opção “Skip adding an App”. No entanto, essa escolha fica a critério do desenvolvedor, dependendo das necessidades e preferências do projeto, porém isso não vai influenciar em nada na nossa implementação.

Ao abrir a pasta do projeto criado, podemos ver a estrutura que é usada, com destaque em dois arquivos

Handler.mjs = É o arquivo que contém o código executado quando a função Lambda é invocada. Ele define a lógica que será processada, sendo o ponto de entrada para a execução do seu código sempre que a Lambda for acionada.

Serverless.yml = É o arquivo central do projeto, onde definimos os recursos da função Lambda, bem como outros serviços que irão interagir com ela. Ele é fundamental para a configuração e orquestração de toda a infraestrutura serverless, garantindo que os serviços e recursos sejam corretamente integrados e implantados.

Agora, vou colar o código que vai adicionar os cabeçalhos de segurança à resposta da requisição no arquivo handler.mjs

export const invoke = async (event) => {
  try {
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    headers["strict-transport-security"] = [
      {
        key: "Strict-Transport-Security",
        value: "max-age=63072000; includeSubdomains; preload",
      },
    ];
    headers["content-security-policy"] = [
      {
        "key": "Content-Security-Policy",
        "value": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; media-src 'self'; object-src 'none'; frame-src 'none';"
      }
    ];
    headers["x-content-type-options"] = [
      { key: "X-Content-Type-Options", value: "nosniff" },
    ];
    headers["x-frame-options"] = [{ key: "X-Frame-Options", value: "DENY" }];
    headers["x-xss-protection"] = [
      { key: "X-XSS-Protection", value: "1; mode=block" },
    ];
    headers["referrer-policy"] = [
      { key: "Referrer-Policy", value: "same-origin" },
    ];

    return response;
  } catch (error) {
    console.error("Error modifying headers:", error);
    throw error;
  }
}

Com isso, já podemos partir para configuração do arquivo Serverless.yml

# Define o nome do serviço e a versão do framework Serverless que será usada
service: security-headers-article-domain
frameworkVersion: '4'

# Configuração do provedor AWS
provider:
  name: aws  # Define a AWS como provedor de nuvem
  runtime: nodejs22.x  # Define a versão do Node.js para executar a função Lambda
  region: us-east-1  # Especifica a região onde a função será implantada
  memorySize: 128  # Define a quantidade de memória alocada para a função Lambda

# Definição das funções Lambda
functions:
  api:
    handler: handler.invoke  # Especifica o arquivo e a função de entrada da Lambda
    role: !GetAtt BasicLambdaEdgeExecutionRole.Arn  # Associa a função Lambda a um papel IAM
    events:
      - preExistingCloudFront:  # Vincula a função Lambda@Edge a uma distribuição CloudFront existente
          distributionId: E3SC0HA20118EQ  # ID da distribuição do CloudFront, aqui você vai inserir o ID da distribuição que criamos
          eventType: origin-response  # Define que a função será acionada na resposta da origem
          pathPattern: '*'  # Aplica a função a todas as solicitações
          includeBody: false  # Indica que o corpo da resposta não será incluído

# Definição de recursos adicionais
resources:
  Resources:
    BasicLambdaEdgeExecutionRole:  # Criação da role IAM para a Lambda@Edge
      Type: AWS::IAM::Role
      Properties:
        RoleName: basicLambdaEdgeExecutionRole  # Nome da role
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - edgelambda.amazonaws.com  # Permite que a Lambda@Edge assuma essa role
                  - lambda.amazonaws.com  # Permite que a Lambda normal assuma essa role
              Action: sts:AssumeRole
        Policies:
          - PolicyName: BasicLambdaEdgeExecutionPolicy  # Nome da política anexada à role
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action:
                    - logs:CreateLogGroup  # Permite criar grupos de logs no CloudWatch
                    - logs:CreateLogStream  # Permite criar streams de logs
                    - logs:PutLogEvents  # Permite gravar eventos de log
                  Resource: arn:aws:logs:*:*:*  # Aplica permissões a todos os logs na conta

# Lista de plugins utilizados
plugins:
  - serverless-lambda-edge-pre-existing-cloudfront  # Plugin que facilita a vinculação de Lambda@Edge a distribuições CloudFront existentes

Cada comando do código acima está explicado com a sua devida função e utilidade.

Como mostrado acima no código de exemplo, vamos usar um plugin para atribuir diretamente uma distro à função lambda, então para fazer o uso deste plugin, devemos realizar a instalação do mesmo pelo seguinte comando:

 npm install --save-dev serverless-lambda-edge-pre-existing-cloudfront

Com os códigos devidamente implementados nos arquivos correspondentes, podemos agora fazer o deploy da nossa função para a AWS. Para isso, é necessário que as credenciais da sua conta estejam configuradas na sua máquina, garantindo as permissões adequadas para escrita no CloudFormation — o serviço de Infraestrutura como Código (IaC) da AWS, similar ao Terraform.

Vamos fazer o deploy digitando o seguinte comando

serverless deploy

Após a execução do comando, o terminal exibirá a confirmação da criação da stack no CloudFormation e informará que a Lambda@Edge está sendo implantada nas regiões necessárias.

Agora podemos ir no console da AWS e acessar o serviço do CloudFormation para ver se os serviços realmente foram criados

Podemos observar que a stack foi criada com sucesso, junto com quatro recursos, que são:

  • ApiLambdaFunction: A função lambda para alterar a resposta da requisição para a CDN
  • ApiLambdaVersion8vvVMotME5GcgNisfrqxNaiz4Zeh8T0qH2Xmx0U: A versão 1 criada da lambda para realizar a implantação nas edge location.
  • ApiLogGroup: Grupo de logs no cloudWatch da Função lambda criada.
  • **BasicLambdaEdgeExecutionRole: **Role com permissões padrões para a função lambda criada.

Podemos acessar a lambda e ver que o código está lá, porém o trigger que adicionamos (cloudFront) não está;

Isso ocorre porque, para implantar uma função Lambda@Edge, é necessário distribuí-la nas edge locations associadas à distribuição atribuída. Para isso, uma nova versão da função é gerada automaticamente. Para visualizar essa versão, basta acessar a aba de versões e selecionar a V1.

Clicando nessa versão, podemos ver o cloudFront como trigger da função.

Ótimo, após verificar que está tudo certo com a criação da função e a atribuição da nossa distribuição, podemos testar e ver se os cabeçalhos de segurança foram adicionados.

Para isso, vamos fazer uma invalidação de cache na nossa distribuição e testar o scan no HTTP Observatory Report novamente.

Obs: Usaremos “/” para o cache ser invalidado para todos os objetos da nossa Origin.

Após criar a invalidação, podemos verificar no HTTP Observatory Report e…

Bem melhor agora né? 0.0

Ao final deste processo, agora de fato podemos afirmar que construímos uma CDN de alta disponibilidade, segura, escalável, performática e totalmente serverless na AWS. Com essa arquitetura, garantimos uma entrega eficiente de conteúdo, aproveitando ao máximo os serviços gerenciados da AWS. 🚀

Link do projeto com Serverless: https://github.com/JoaoEmboaba/security-headers-lambda-edge-applier

Referências

[embed]Amazon S3 Armazenamento de objetos para armazenar e recuperar qualquer quantidade de dados de qualquer local ✔O Amazon S3 é um…aws.amazon.com

[embed]Amazon Route 53 - AWS Experimente o Amazon Route 53, um web service de DNS altamente disponível, escalável, de baixo custo e confiável. Ele é…aws.amazon.com

[embed]Amazon CloudFront - Rede de entrega de conteúdo - AWS Conheça o Amazon CloudFront, uma rede de entrega de conteúdo para fornecer dados, vídeos e recursos aos clientes, onde…aws.amazon.com

[embed]Adding HTTP Security Headers Using Lambda@Edge and Amazon CloudFront | Amazon Web Services Adding security response headers is often achievable by modifications to your application configuration. In this blog…aws.amazon.com

[embed]Serverless Framework Documentation An introduction to the Serverless Framework, its philosophy, overview, and key highlights. Learn how to build…www.serverless.com


메타데이터
post_id
057dcc3de2fc
slug
distribuindo-uma-spa-através-de-uma-cdn-de-alta-disponibilidade-e-segurança-na-aws-057dcc3de2fc
url
https://medium.com/@jemboaba/distribuindo-uma-spa-atrav%C3%A9s-de-uma-cdn-de-alta-disponibilidade-e-seguran%C3%A7a-na-aws-057dcc3de2fc
canonical_url
https://medium.com/@jemboaba/distribuindo-uma-spa-atrav%C3%A9s-de-uma-cdn-de-alta-disponibilidade-e-seguran%C3%A7a-na-aws-057dcc3de2fc
author_url
https://medium.com/@jemboaba
status
ok
fetched_at
2026-06-26 06:47:43