Alternar tema

Otimização de desempenho do Nginx: gzip, cache e pools de conexões

Easton editorial illustration: service mesh rail yard

Na semana passada, recebi um alerta: o tempo de carregamento da página inicial de um e-commerce tinha disparado para 4 segundos. Abri o Chrome DevTools e encontrei um HTML de 120 KB, mais 350 KB de CSS e JavaScript — todos arquivos originais, sem compactação. Para piorar, cada requisição chegava ao backend, e a taxa de acerto do cache era de apenas 12%. Naquela noite, passei duas horas ajustando o Nginx: ativei a compactação gzip, completei a estratégia de cache e corrigi os parâmetros dos pools de conexões. Na manhã seguinte, a página inicial já carregava em 1,6 segundo, e o QPS do backend havia caído quase pela metade.

Esse problema é muito comum. Muita gente instala o Nginx e o deixa funcionando com gzip desativado por padrão, duas linhas improvisadas de cache e o limite padrão de conexões. Quando chega um pico de tráfego, o servidor não aguenta.

Reuni aqui as configurações de otimização de desempenho do Nginx que validei em produção. A compactação gzip pode reduzir o volume transferido em 60% a 80%; com 95% de acerto do cache, a carga do backend pode cair 90%; e uma configuração adequada dos pools de conexões pode multiplicar a capacidade simultânea por três ou quatro. Vou detalhar cada módulo, os problemas que encontrei e os números medidos nos testes.

Capítulo 1: configuração do gzip — reduza o volume transferido

Primeiro, vale entender por que o gzip é tão importante. Imagine um arquivo HTML de 100 KB. Depois da compactação gzip, ele pode ocupar apenas 20 a 25 KB. Esses 75 a 80 KB economizados representam carregamento mais rápido para o usuário e menor custo de tráfego para você.

Percebi isso pela primeira vez em um projeto de cliente. Mais de 60% dos usuários acessavam pelo celular, muitos deles em redes 4G. A página inicial levava de 3 a 4 segundos para carregar, e a taxa de rejeição havia subido para 70%. Depois de ativar o gzip, o volume transferido caiu 70%, e o carregamento da primeira tela chegou a cerca de 1,5 segundo.

1.1 Configuração básica: primeiro, faça funcionar

A configuração de gzip no Nginx não é complicada. O essencial cabe em poucas linhas:

http {
    gzip on;
    gzip_vary on;
    gzip_min_length 1000;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}

gzip on é o interruptor e dispensa explicação. Já gzip_vary on é essencial: ele adiciona Vary: Accept-Encoding ao cabeçalho de resposta para informar ao CDN e ao navegador que o conteúdo varia conforme a capacidade de compactação do cliente, evitando inconsistências no cache.

Definir gzip_min_length como 1000 bytes significa não compactar arquivos menores que 1 KB. Arquivos muito pequenos trazem pouco ganho com a compactação e ainda consomem CPU. gzip_types especifica os tipos MIME que serão compactados. Por padrão, apenas text/html é incluído; você precisa adicionar CSS, JavaScript, JSON e XML.

1.2 Configuração avançada: nível de compactação e lista de tipos MIME

O nível de compactação exige equilíbrio. gzip_comp_level aceita valores de 1 a 9: quanto maior o número, maior a taxa de compactação, mas também maior o consumo de CPU.

Fiz uma série de testes:

Nível de compactaçãoTaxa de compactação do HTMLTempo de CPU (ms)Cenário recomendado
165%2CPU limitada
472%3Equilíbrio (recomendado)
675%5Largura de banda limitada (recomendado)
978%12Cenários extremos

Os níveis 4 e 6 são as melhores escolhas na maioria dos casos. O nível 9 dobra o consumo de CPU e melhora a taxa de compactação em apenas alguns pontos percentuais, por isso não compensa.

Esta é a configuração completa de gzip que uso em produção:

# Configuração de compactação gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/json
    application/javascript
    application/xml
    application/xml+rss
    application/xhtml+xml
    application/x-javascript;
gzip_disable "msie6";

O parâmetro gzip_proxied any costuma passar despercebido. Se o Nginx atua como proxy reverso e o cabeçalho retornado pelo backend não contém Content-Length, a resposta não é compactada por padrão. Definir o valor como any força a compactação de todas as respostas elegíveis.

gzip_disable "msie6" existe para compatibilidade com versões antigas do IE6, que têm problemas com gzip. Hoje o IE6 praticamente desapareceu e essa linha pode ser removida, mas costumo mantê-la por precaução.

1.3 Quais tipos de arquivo oferecem o maior ganho?

Nos meus testes, o resultado da compactação varia bastante conforme o tipo de arquivo:

Tipo de arquivoTamanho originalApós a compactaçãoTaxa de compactação
HTML100 KB20-25 KB75-80%
CSS80 KB24-28 KB65-70%
JavaScript120 KB36-42 KB65-70%
API JSON50 KB20-25 KB50-60%
Imagens/vídeosJá compactadosSem benefício0-5%
75-80%
Taxa de compactação de arquivos HTML
Source: Dados medidos em produção

Imagens e vídeos já são compactados em formatos como JPEG, PNG e MP4. Aplicar gzip novamente pode até aumentar o tamanho. Portanto, nunca inclua image/* nem video/* em gzip_types: isso produz o efeito contrário.

Uma vez, ao investigar um problema, encontrei image/jpeg na lista gzip_types. O tamanho das imagens tinha aumentado de 3% a 5%. É um erro básico que provavelmente também cometi quando ainda não entendia o assunto e queria incluir todos os tipos MIME possíveis.

Capítulo 2: estratégia de cache — acelere conteúdo estático

O cache oferece um dos ganhos mais diretos na otimização de desempenho. Com uma configuração adequada, 95% das requisições podem ser respondidas diretamente pelo Nginx, sem chegar ao backend. Já vi muitos sistemas com servidores de aplicação trabalhando no limite enquanto o cache do Nginx quase não era usado — não porque estivesse desativado, mas porque havia sido configurado incorretamente.

2.1 Como escolher entre proxy_cache e fastcgi_cache?

O Nginx oferece dois mecanismos de cache:

  • proxy_cache: armazena em cache as respostas de servidores upstream e é indicado para proxy reverso, como serviços em Node.js, Python ou Go
  • fastcgi_cache: armazena em cache as respostas de processos FastCGI e é indicado para PHP-FPM

A escolha depende da tecnologia do backend. Se você usa PHP, escolha fastcgi_cache; se usa Node.js, Python ou Go, escolha proxy_cache. A lógica de configuração é praticamente igual nos dois casos. Nos exemplos a seguir, usarei proxy_cache.

2.2 Configuração completa do proxy_cache

Primeiro, defina o caminho do cache no bloco http:

http {
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=my_cache:10m
                     max_size=10g
                     inactive=60m
                     use_temp_path=off;
}

Veja o que cada linha faz:

  • levels=1:2: define os níveis do diretório de cache; 1:2 cria uma estrutura de dois níveis para evitar arquivos demais em um único diretório
  • keys_zone=my_cache:10m: define o nome da zona de cache e o espaço em memória para metadados; 10m comporta cerca de 80 mil chaves de cache
  • max_size=10g: define o tamanho total máximo; ao ultrapassá-lo, o Nginx remove itens conforme o algoritmo LRU
  • inactive=60m: remove dados do cache que não forem acessados durante 60 minutos
  • use_temp_path=off: grava diretamente no diretório de cache e evita o custo de mover arquivos temporários

Em seguida, ative o cache em server ou location:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_cache my_cache;

        # Validade do cache
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_valid any 1m;

        # Desenho da chave do cache
        proxy_cache_key $scheme$request_method$host$request_uri;

        # Estratégia de fallback (detalhada a seguir)
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

        # Cabeçalho com o status do cache (para depuração)
        add_header X-Cache-Status $upstream_cache_status;
    }
}

proxy_cache_valid é a configuração central e define a duração do cache para diferentes códigos de status:

  • 200 302 10m: mantém respostas normais em cache por 10 minutos
  • 404 1m: mantém erros 404 em cache por 1 minuto e evita que requisições maliciosas sobrecarreguem o backend
  • any 1m: mantém outros códigos de status em cache por 1 minuto

2.3 Desenho da chave e estratégia de invalidação do cache

A chave definida em proxy_cache_key determina quais requisições serão consideradas “iguais”. O padrão é $scheme$proxy_host$request_uri, mas recomendo declará-la explicitamente:

proxy_cache_key $scheme$request_method$host$request_uri;

Assim, a chave inclui protocolo, método da requisição, nome do host e URI completa. Se o site aceita GET e POST ou responde por vários domínios, essa configuração é mais precisa.

A invalidação do cache costuma dar trabalho. Estas são as estratégias mais comuns:

  1. Expiração por tempo: defina o período em proxy_cache_valid; o cache expira automaticamente ao final
  2. Desvio ativo: use proxy_cache_bypass para ignorar o cache
  3. Limpeza de cache: a versão comercial Nginx Plus oferece proxy_cache_purge

Normalmente uso a segunda opção e faço o controle por um cabeçalho de requisição:

# Ignora o cache quando um cabeçalho específico está presente
proxy_cache_bypass $http_x_nocache;

# Ou ignora por meio de um parâmetro específico
proxy_cache_bypass $arg_nocache;

Quando for necessário atualizar o cache, basta adicionar ?nocache=1 ou o cabeçalho X-Nocache: 1.

2.4 Estratégia de fallback: continue atendendo mesmo se o backend falhar

proxy_cache_use_stale é uma configuração muito útil. Se o backend apresentar erro ou timeout, o Nginx pode devolver conteúdo expirado do cache em vez de retornar um erro imediatamente.

proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

No Dia dos Solteiros do ano passado, tivemos um problema ao ampliar a capacidade do backend, e o serviço de API passou a responder 502 de forma intermitente. Felizmente, o cache tinha essa estratégia de fallback. O acesso dos usuários praticamente não foi afetado: embora o conteúdo em cache tivesse expirado havia alguns minutos, ele ainda pôde ser entregue normalmente. Quando o backend se recuperou, o cache foi atualizado de forma automática.

Comparação dos dados medidos:

MétricaSem cacheAcerto do cacheModo de fallback
Tempo de resposta150-200 ms5-10 ms5-10 ms
QPS do backend1000500
Experiência do usuárioNormalNormalUm pouco mais lenta
95%
Taxa de acerto do cache
Source: Medições em produção

Quando há acerto do cache, o tempo de resposta cai de 200 ms para 5 a 10 ms, uma melhoria de quase 20 vezes. É um ganho muito fácil de perceber.

Capítulo 3: pools de conexões — essenciais para alta simultaneidade

gzip e cache resolvem “como transferir mais rápido”. Os pools de conexões resolvem “como atender mais requisições”. Com a configuração padrão, cada worker do Nginx aceita no máximo 1024 conexões simultâneas. Assim que o tráfego aumenta, esse limite deixa de ser suficiente.

3.1 worker_connections: calcule corretamente o limite

A fórmula para o número máximo de conexões simultâneas é:

Máximo de conexões simultâneas = worker_processes × worker_connections

Suponha que o servidor tenha uma CPU de 8 núcleos, worker_processes esteja definido como 8 — ou como auto, para ajuste automático — e worker_connections seja 4096:

Máximo de conexões simultâneas = 8 × 4096 = 32768

O número parece alto, mas lembre-se de que cada requisição costuma usar duas conexões: uma do cliente até o Nginx e outra do Nginx até o backend. Portanto, a quantidade real de requisições simultâneas que o servidor consegue processar fica aproximadamente pela metade.

A configuração fica no bloco events:

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

use epoll é o padrão no Linux e não precisa ser declarado, mas escrevê-lo deixa a intenção mais clara. multi_accept on permite que o worker aceite várias conexões novas de uma vez e reduz a fila em situações de alta simultaneidade.

3.2 keepalive do cliente: reutilize conexões e reduza o custo

Estabelecer uma conexão TCP exige um handshake de três vias, que tem seu custo. O keepalive permite reutilizar a conexão entre o cliente e o Nginx, sem precisar abrir outra a cada requisição.

http {
    keepalive_timeout 65;
    keepalive_requests 1000;
}

keepalive_timeout 65: mantém a conexão aberta por 65 segundos e depois a fecha. O valor não deve ser alto demais, pois consumiria recursos excessivos no servidor, nem baixo demais, pois reduziria a reutilização. Um intervalo de 60 a 75 segundos é razoável.

keepalive_requests 1000: permite processar até 1000 requisições em uma conexão. Um valor baixo demais causa desconexões frequentes; um valor alto demais pode provocar vazamento de recursos. Nos meus testes, 1000 foi um valor estável.

3.3 keepalive do upstream: pool de conexões com o backend

Muita gente não conhece essa configuração, mas o efeito é evidente. O Nginx também pode reutilizar conexões com os serviços de backend, reduzindo o custo de estabelecer conexões TCP repetidamente.

upstream backend {
    server 127.0.0.1:8080;
    server 127.0.0.1:8081;

    keepalive 64;
    keepalive_timeout 60s;
    keepalive_requests 1000;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

keepalive 64: mantém um pool de 64 conexões ociosas. Ajuste o valor conforme o número de serviços de backend; em geral, use de quatro a oito vezes a quantidade de servidores.

As linhas proxy_http_version 1.1 e proxy_set_header Connection "" são obrigatórias. O HTTP/1.1 oferece keepalive por padrão, e é preciso limpar o cabeçalho Connection para reutilizar a conexão. Sem essas duas linhas, o keepalive do upstream não funciona.

Comparação dos resultados medidos:

ConfiguraçãoAberturas de conexão por minutoConsumo de CPUCenário recomendado
Sem keepalive do upstream6000AltoBaixo tráfego
keepalive 323000MédioTráfego médio
keepalive 641500BaixoAlto tráfego

Depois de ativar o keepalive do upstream, o custo de abertura das conexões caiu 50%. Essa configuração é especialmente importante em cenários de alta simultaneidade.

3.4 Tabela de referência dos parâmetros recomendados

Organizei uma configuração recomendada para diferentes cenários:

ParâmetroBaixo tráfego (<1000 QPS)Tráfego médio (1000-5000 QPS)Alto tráfego (>5000 QPS)
worker_processesautoautoauto
worker_connections102420484096
keepalive_timeout606575
keepalive_requests1005001000
upstream keepalive163264

Esses números são apenas um ponto de partida. O ajuste real precisa considerar os dados dos testes de carga. Normalmente uso wrk ou ab para medir as curvas de conexões e tempo de resposta e encontrar os melhores valores.

Capítulo 4: modelo completo de configuração — pronto para produção

Os três capítulos anteriores explicaram os princípios. Agora segue um modelo de configuração integrado. Você pode ajustar os parâmetros ao cenário real, mas a estrutura é reutilizável.

# Modelo de nginx.conf para produção

user nginx;
worker_processes auto;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    # Configuração de compactação gzip
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1000;
    gzip_types text/plain text/css text/xml text/javascript
               application/json application/javascript application/xml
               application/xml+rss application/xhtml+xml;
    gzip_disable "msie6";

    # Configuração do caminho do cache
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=my_cache:10m
                     max_size=10g
                     inactive=60m
                     use_temp_path=off;

    # Configuração das conexões dos clientes
    keepalive_timeout 65;
    keepalive_requests 1000;

    # Grupo de servidores de backend
    upstream backend {
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        keepalive 64;
        keepalive_timeout 60s;
        keepalive_requests 1000;
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";

            # Configuração de cache
            proxy_cache my_cache;
            proxy_cache_valid 200 302 10m;
            proxy_cache_valid 404 1m;
            proxy_cache_key $scheme$request_method$host$request_uri;
            proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

            # Cabeçalho de resposta para depuração
            add_header X-Cache-Status $upstream_cache_status;
        }
    }
}

Configurações específicas para três cenários

E-commerce: a página inicial e as páginas de produto mudam com frequência, então use um cache mais curto; de 10 a 15 minutos é suficiente. O keepalive do upstream deve ser maior, pois há muitas consultas ao banco de dados e a carga do backend é alta.

Serviços de API: os dados precisam ser mais atuais, então proxy_cache_valid pode ficar entre 1 e 5 minutos. gzip funciona muito bem com JSON e não deve ser esquecido.

Sites estáticos: HTML, CSS e JavaScript quase não mudam, então o cache pode durar 1 hora ou mais. Como os arquivos estáticos contêm bastante texto, o ganho com gzip é maior.

Capítulo 5: problemas comuns e solução de falhas

Aqui estão soluções diretas para alguns problemas frequentes que já encontrei.

P: a compactação gzip não funciona, e a resposta não contém Content-Encoding: gzip

Confira três pontos:

  1. se gzip on está no nível correto da configuração, no bloco http
  2. se gzip_types inclui o tipo MIME da resposta
  3. se o corpo da resposta é maior que gzip_min_length

Teste com curl: curl -H "Accept-Encoding: gzip" -I http://your-site.com

P: a taxa de acerto do cache está baixa, e X-Cache-Status quase sempre mostra MISS

Causas comuns:

  • a chave do cache foi mal projetada, e cada requisição é considerada “diferente”
  • proxy_cache_valid é curto demais, e o cache expira antes de ser aproveitado
  • o backend devolve Cache-Control: no-cache ou Set-Cookie nos cabeçalhos de resposta

Verifique os cabeçalhos e confirme que não há instruções que impeçam o cache.

P: worker_connections é insuficiente, e aparecem erros 502

Consulte o log de erros do Nginx. Se houver a mensagem worker_connections are not enough, o número de conexões simultâneas ultrapassou o limite.

Soluções:

  1. aumente o valor de worker_connections
  2. verifique se há vazamento de conexões causado por uma configuração inadequada de keepalive
  3. considere adicionar servidores e distribuir a carga

P: o uso de memória está alto, e o servidor apresenta OOM com frequência

Possíveis causas:

  • a keys_zone de proxy_cache_path está grande demais
  • há arquivos demais no cache, elevando o uso de memória mapeada
  • o pool de conexões keepalive é grande demais, e conexões ociosas consomem recursos

Reduza esses parâmetros conforme necessário ou aumente a memória do servidor.

Para concluir

Os três pilares da otimização de desempenho do Nginx são compactação gzip, estratégia de cache e configuração dos pools de conexões. Quando funcionam juntos, eles podem dobrar a velocidade de carregamento do site e multiplicar por três ou quatro a capacidade simultânea.

A otimização não termina depois de uma única configuração. Recomendo seguir esta ordem:

  1. Ative primeiro o gzip: é a menor mudança e oferece o ganho mais direto; leva cerca de dez minutos
  2. Configure o cache em seguida: desenhe a estratégia conforme o cenário do negócio; dá para concluir em um dia
  3. Ajuste por último os pools de conexões: exige validação com testes de carga e faz mais sentido quando o tráfego já está estável

Depois de cada mudança, faça testes de carga e confirme o resultado. wrk e ab servem para acompanhar as variações do tempo de resposta, QPS e taxa de erros. Não ajuste por intuição: deixe os dados orientarem a decisão.

Use esta lista final de verificação:

  • gzip está ativo, com todos os tipos MIME necessários
  • gzip_comp_level está entre 4 e 6 para equilibrar CPU e taxa de compactação
  • proxy_cache_path está configurado, com tamanho de cache adequado
  • proxy_cache_valid está ajustado ao cenário do negócio
  • a estratégia de fallback proxy_cache_use_stale está configurada
  • worker_connections está definido como 4096 ou mais
  • keepalive_timeout está entre 60 e 75 segundos
  • o keepalive do upstream está configurado, incluindo HTTP/1.1 e o cabeçalho Connection
  • o cabeçalho de resposta inclui X-Cache-Status para depuração

É isso. Se tiver alguma dúvida, deixe um comentário; tentarei responder.

Processo de configuração para otimizar o desempenho do Nginx

Configuração de produção em três etapas: compactação gzip, estratégia de cache e otimização dos pools de conexões

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Ativar a compactação gzip

    Adicione a configuração ao bloco http:

    • gzip on; ativa a compactação
    • gzip_vary on; adiciona o cabeçalho de resposta Vary
    • gzip_comp_level 6; define o nível de compactação (recomendado: 4 a 6)
    • gzip_min_length 1000; não compacta arquivos menores que 1 KB
    • gzip_types especifica os tipos MIME: text/plain text/css application/json application/javascript
  2. 2

    Step 2: Configurar o cache proxy_cache

    Faça a configuração em duas etapas:

    Etapa 1: defina o caminho do cache
    • proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m

    Etapa 2: ative o cache em location
    • proxy_cache my_cache;
    • proxy_cache_valid 200 10m; mantém respostas normais em cache por 10 minutos
    • proxy_cache_use_stale error timeout http_502; configura a estratégia de fallback
  3. 3

    Step 3: Otimizar os parâmetros dos pools de conexões

    Três configurações essenciais:

    • worker_connections 4096; definida no bloco events
    • keepalive_timeout 65; keepalive_requests 1000; definidas no bloco http
    • upstream keepalive 64; proxy_http_version 1.1; proxy_set_header Connection ""; configuração do pool de conexões com o backend

FAQ

Qual nível de compactação gzip devo escolher?
Recomendo os níveis 4 a 6. O nível 4 oferece taxa de compactação de 72% e baixo consumo de CPU, por isso funciona bem quando a CPU é limitada; o nível 6 chega a 75% e economiza mais largura de banda, sendo indicado quando ela é o gargalo. O nível 9 dobra o consumo de CPU, mas melhora pouco a compactação, então não o recomendo.
Como escolher entre proxy_cache e fastcgi_cache?
Depende da tecnologia do backend: use proxy_cache com servidores de aplicação em Node.js, Python, Go e semelhantes; use fastcgi_cache com PHP-FPM. A lógica de configuração é praticamente igual nos dois casos, e os principais parâmetros são o caminho, a validade e o desenho da chave do cache.
Qual valor usar em worker_connections?
Estime com base no número de núcleos da CPU e no tráfego: use 1024 para baixo tráfego (&lt;1000 QPS), 2048 para tráfego médio (1000-5000 QPS) e 4096 para alto tráfego (&gt;5000 QPS). A capacidade simultânea real é worker_processes × worker_connections ÷ 2.
Como investigar uma taxa de acerto de cache baixa?
Verifique três pontos: 1. se a chave do cache é adequada e não torna cada requisição diferente; 2. se a duração definida em proxy_cache_valid é curta demais; 3. se os cabeçalhos de resposta do backend incluem Cache-Control: no-cache ou Set-Cookie, que impedem o cache.
Por que o keepalive do upstream exige HTTP/1.1?
O HTTP/1.0 não oferece keepalive por padrão; é preciso usar HTTP/1.1 para reutilizar conexões com o backend. Também é necessário definir proxy_set_header Connection "" para limpar o cabeçalho Connection. Caso contrário, o Nginx fecha a conexão e o keepalive do upstream não funciona.
Como definir a duração do cache para diferentes cenários de negócio?
A página inicial e as páginas de produto de um e-commerce mudam com frequência, então use de 10 a 15 minutos; APIs exigem dados mais atuais, então use de 1 a 5 minutos; HTML, CSS e JavaScript de sites estáticos quase não mudam e podem ficar em cache por pelo menos 1 hora. O princípio é simples: quanto mais rápido o negócio muda, menor deve ser a duração do cache.

15 min de leitura · Publicado em: 11 abr 2026 · Atualizado em: 4 set 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog