Não sou programador. Sou corretor de planos de saúde e sócio de uma corretora, e cuido pessoalmente dos nossos sites. Nesta semana passei alguns dias mexendo na performance de quatro sites em HTML estático, e o resultado me surpreendeu: o pior deles saiu de 64 para 97 no PageSpeed Insights mobile.
Vou contar o que fiz, na ordem, com os números reais. Se você tem um site feito a partir de um template pronto (desses com Bootstrap, jQuery e uma dúzia de plugins), é bem provável que o seu tenha os mesmos problemas.
Transparência: fiz esse trabalho com ajuda de IA (Claude), que escreveu os scripts e conferiu cada etapa comigo. As decisões, os testes e os números são dos meus sites.
O incômodo: o WordPress estava mais rápido que o HTML puro
Nosso site principal, de planos da Amil Saúde, roda em WordPress com plugin de cache e marca 94 no mobile. Já os sites menores, feitos em HTML estático, que em teoria deveriam voar, estavam entre 63 e 79.
Não fazia sentido, não é mesmo? HTML puro não tem banco de dados, não tem PHP, não tem nada. O problema não era o tipo de site. Era tudo o que o template carregava sem usar.
O caso deste artigo é um site sobre a linha Amil Black (a linha topo da operadora, que substituiu o antigo Amil One em março de 2026). Ele estava assim no PageSpeed mobile:
- Desempenho: 64
- LCP (Largest Contentful Paint): 7,4 s
Sete segundos para o maior elemento aparecer na tela de um celular. Muita gente desiste antes disso.
Passo 1: descobrir o que o template carregava de graça
Abri o <head> e o fim do <body> da home. Esta era a lista de scripts, carregada em todas as páginas:
<script src="plugins/jQuery/jquery.min.js"></script> <script src="plugins/bootstrap/bootstrap.min.js"></script> <script src="plugins/magnific-popup/jquery.magnific.popup.min.js"></script> <script src="plugins/slick/slick.min.js"></script> <script src="plugins/filterizr/jquery.filterizr.min.js"></script> <script src="https://maps.googleapis.com/maps/api/js?key=..."></script> <script src="plugins/google-map/gmap.js"></script> <script src="plugins/syotimer/jquery.syotimer.js"></script> <script src="plugins/aos/aos.js"></script> <script src="plugins/swiper/swiper.min.js"></script> <script src="js/script.js"></script> <script src="js/menu-black.js"></script> O site não tem mapa, não tem carrossel, não tem contador regressivo, não tem galeria com filtro. O único script que realmente fazia alguma coisa era o último, menu-black.js, que é o menu.
No CSS, a mesma história: Bootstrap completo, Slick, Slick Theme, Themify Icons e Font Awesome 4.5 vindo de uma CDN.
Uma curiosidade: em outro dos sites, o script.js do template tinha um trecho de CSS colado no final do arquivo. Isso é erro de sintaxe, então o script nunca executou, provavelmente durante anos, e ninguém percebeu. Era peso puro.
Como tirar sem quebrar nada
Antes de apagar qualquer coisa, tirei print de página inteira de todas as páginas, no desktop (1280 px) e no celular (390 px), com o Playwright. Depois de cada mudança, tirei os prints de novo e comparei pixel a pixel.
Para comparar, usei uma tolerância pequena (diferença maior que 24 em algum canal de cor), porque gradientes de fundo às vezes variam um pouco entre uma renderização e outra e geram "diferença" que o olho não vê.
Também conferi três coisas que print não mostra:
- Erros de JavaScript no console (
pageerrorno Playwright). - O menu: o mega menu no hover do desktop e o hambúrguer no celular.
- O botão "voltar ao topo".
Esse terceiro item me pegou. O botão dependia do script.js antigo, que era jQuery. Tirei o jQuery e precisei de um substituto. Foram seis linhas de JavaScript puro, no arquivo do menu que já existia:
document.addEventListener('click', function (e) { var b = e.target.closest ? e.target.closest('.back-to-top') : null; if (!b) return; e.preventDefault(); window.scrollTo({ top: 0, behavior: 'smooth' }); }); Resultado: 11 scripts a menos por página, além dos CSS de Slick, Slick Theme e Font Awesome.
Passo 2: PurgeCSS no Bootstrap
Mesmo sem os plugins, o bootstrap.min.css tinha 141 KB, e o site usa uma fração disso: o grid, alguns utilitários e o collapse do menu.
Usei o PurgeCSS, passando como conteúdo todos os HTML e os JS que sobraram:
import { PurgeCSS } from 'purgecss'; import fs from 'fs'; const result = await new PurgeCSS().purge({ content: ['site/*.html', 'site/js/*.js'], css: ['site/plugins/bootstrap/bootstrap.min.css'], safelist: { standard: ['show', 'collapse', 'collapsing', 'fade', 'active', 'open'], greedy: [/^modal/, /^dropdown/, /^navbar/, /^nav-/] } }); fs.writeFileSync('site/plugins/bootstrap/bootstrap.min.css', result[0].css); A safelist é a parte importante. Classes que o JavaScript adiciona na hora (como show no menu aberto) não aparecem no HTML, e o PurgeCSS apagaria essas classes se você não avisar.
- Bootstrap: 141 KB → ~26 KB
- Themify Icons: 18,6 KB → ~1 KB (o site usa meia dúzia de ícones)
💡 Dica: depois de purgar, coloque um ?v=4 (ou qualquer número novo) no link do CSS. Sem isso, quem já visitou o site continua com o arquivo antigo em cache e você fica achando que algo deu errado.
Passo 3: a imagem do LCP (aqui estava o 7,4 s)
O maior elemento da primeira tela era a imagem principal da home: um PNG de 399 KB.
Convertida para WebP, com uma versão menor para o celular:
-
amil-black-hero.webp(700 px): 24,9 KB -
amil-black-hero-400.webp(400 px): 11,6 KB
<link rel="preload" as="image" href="images/amil-black-hero.webp" imagesrcset="images/amil-black-hero-400.webp 400w, images/amil-black-hero.webp 700w" imagesizes="(max-width: 575px) 88vw, 532px" fetchpriority="high"> <img src="images/amil-black-hero.webp" srcset="images/amil-black-hero-400.webp 400w, images/amil-black-hero.webp 700w" sizes="(max-width: 575px) 88vw, 532px" width="700" height="525" fetchpriority="high" decoding="async" alt="Mulher sorrindo segurando o cartão do plano Amil Black"> O width e o height evitam que a página "pule" quando a imagem chega (é o CLS). O sizes diz ao navegador que, no celular, a imagem ocupa 88% da largura da tela, então ele baixa a versão de 400 px.
E aqui encontrei o erro mais curioso de todos. Outra imagem, uma foto que fica no meio da página, estava marcada com fetchpriority="high":
<img src="images/amil-black/amil-black.jpg" alt="..." fetchpriority="high"> Ou seja: o navegador baixava com prioridade máxima uma foto que o visitante só vê depois de rolar a tela, competindo com a imagem que realmente importava. Troquei por loading="lazy" e converti essa foto também para WebP, em três tamanhos.
💡 Dica: procure fetchpriority="high" no seu site inteiro. Deve existir no máximo um por página, e só na imagem do LCP.
Aproveitei para reescrever os alt das imagens. Se a imagem tem texto escrito nela (banner, chamada), o alt deve dizer o que está escrito, não só descrever a cena.
Passo 4: as fontes do Google sem travar a página
O style.css do template começava a carregar as fontes assim:
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,600,700|Poppins:300,400,500,600,700|Work+Sans:400,500,600|Roboto:100,200,300,400,500,600,700,800,900"); Isso é ruim por dois motivos:
-
@importdentro do CSS é uma cadeia: o navegador baixa ostyle.css, só então descobre o@import, só então vai ao Google. Tudo isso antes de pintar o texto. - Quatro famílias e 20 pesos. O site usa duas famílias e três pesos.
Tirei o @import e coloquei no <head> de cada página:
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&family=Poppins:wght@400;600;700&display=swap" media="print" onload="this.media='all'"> <noscript> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&family=Poppins:wght@400;600;700&display=swap"> </noscript> O truque do media="print" faz o navegador baixar a fonte sem bloquear a renderização. Quando termina, o onload aplica a fonte na tela. O display=swap mostra o texto com a fonte do sistema enquanto isso.
Passo 5: gzip e cache no .htaccess
O servidor é Apache (hospedagem compartilhada comum). Sem nenhuma configuração, ele não comprimia nada e não dizia ao navegador por quanto tempo guardar os arquivos.
<IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml AddOutputFilterByType DEFLATE application/javascript application/json AddOutputFilterByType DEFLATE application/xml image/svg+xml font/woff2 </IfModule> <IfModule mod_expires.c> ExpiresActive On ExpiresByType image/webp "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType font/woff2 "access plus 1 year" ExpiresByType text/css "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" ExpiresByType text/html "access plus 0 seconds" </IfModule> Tudo dentro de <IfModule>: se o módulo não existir no servidor, o bloco é ignorado e o site não cai com erro 500.
HTML com cache zero é de propósito: assim, quando eu atualizo uma página, o visitante vê a versão nova na hora. Imagens e fontes quase nunca mudam e podem ficar um ano em cache. CSS e JS têm o ?v= para forçar a atualização quando eu mexo neles.
No site de Brasília, só esse passo levou a nota de 78 para 89. Foi o ajuste com melhor custo-benefício de todos.
O resultado
| Métrica (mobile) | Antes | Depois |
|---|---|---|
| Desempenho | 64 | 97 |
| First Contentful Paint | — | 1,1 s |
| Largest Contentful Paint | 7,4 s | 2,6 s |
| Total Blocking Time | — | 0 ms |
| Cumulative Layout Shift | — | 0,001 |
| Speed Index | — | 2,7 s |
| SEO | — | 100 |
Não anotei todas as métricas do "antes", só a nota e o LCP. Fica a lição: tire print completo do PageSpeed antes de começar.
Fiz o mesmo processo nos outros três sites:
O que ainda falta
- LCP de 2,6 s: está um pouco acima do verde, que vai até 2,5 s. No print do PageSpeed, o banner de cookies cobre boa parte da tela do celular, e desconfio que ele esteja sendo contado como o maior elemento. É o próximo teste.
- Acessibilidade em 85: provavelmente é o contraste do texto dourado sobre o fundo preto e a ordem dos títulos. Ajuste fino, mas importa.
Checklist para o seu site
- Liste os scripts e CSS de cada página: para cada um, pergunte "o que quebra se eu tirar?". Em template pronto, a resposta costuma ser "nada".
- Tire prints antes e depois: de todas as páginas, desktop e celular. Compare automaticamente.
- Purgue o Bootstrap: com uma
safelistpara as classes que o JavaScript adiciona. - Ataque a imagem do LCP: WebP,
srcset,preloadefetchpriority="high"só nela. - Procure
fetchpriorityerrado: um por página, no máximo. - Tire o
@importde fontes do CSS: carregue só as famílias e os pesos que você usa, sem bloquear. - Configure gzip e cache: sempre dentro de
<IfModule>. - Coloque
?v=nos arquivos alterados: para o cache não esconder a sua mudança.
Se você tem um site parado em 60 e poucos no mobile, comece pela imagem do LCP e pelo .htaccess. São os dois passos que mais mexeram na nota aqui.
Ficou alguma dúvida, ou você já passou por algo parecido com template pronto? Me conta nos comentários.




