Este é o primeiro artigo de uma série de 3 onde documento em tempo real o processo de posicionar meu nome — Rafael Cavalcanti da Silva — no Google. Não é teoria. É um case study ao vivo, com screenshots, métricas reais, cronograma e resultados medidos semana a semana.
Por que isso importa? Personal branding para profissionais de tecnologia não é vaidade — é estratégia. Quando um recrutador, cliente ou parceiro pesquisa seu nome, o que ele encontra define sua primeira impressão. Controlar essa narrativa é uma skill técnica mensurável.
Situação atual: o diagnóstico (8 de março de 2026)
Antes de qualquer otimização, documentei exatamente o que o Google retorna hoje para cada palavra-chave alvo. Este é nosso baseline — o ponto zero contra o qual vamos medir todo progresso.
Palavra-chave: “rafael cavalcanti”

| Posição | Resultado | Domínio |
|---|---|---|
| 1 | Rafael Cavalcanti — Bradesco (LinkedIn) | linkedin.com |
| 2 | Rafael Cavalcanti (@rafaelcavalcantig) | instagram.com |
| 3 | Rafael Cavalcanti Garcia de Castro Alves | escavador.com |
| 4 | Rafael Cavalcanti — Processos | jusbrasil.com.br |
| 5 | Rafael Cavalcanti, diretor do Bradesco… | tiinside.com.br |
| 6 | Rafael Cavalcanti de Souza — FAPESP | bv.fapesp.br |
| 7 | Rafael Cavalcanti Neto (Google Scholar) | scholar.google.com.br |
Diagnóstico: Nenhum desses resultados sou eu. A SERP é dominada por homônimos com presença forte em plataformas de alta autoridade (LinkedIn, Instagram, Jusbrasil). Não existe nenhuma menção a rafaelroot.com.
Palavra-chave: “rafael cavalcanti da silva”

| Posição | Resultado | Domínio |
|---|---|---|
| 1 | Rafael Cavalcanti da Silva — Processos | jusbrasil.com.br |
| 2 | 7 perfis com “Rafael Cavalcanti Da Silva” | br.linkedin.com |
| 3 | Quem é Rafael Chocolate, influencer… | g1.globo.com |
| 4 | RAFAEL CAVALCANTI DA SILVA SOCIEDADE… | empresas.serasaexperian.com.br |
| 5 | O influenciador Rafael Francisco Cavalcanti da Silva… | instagram.com |
| 6 | Influenciador condenado a pagar R$ 50 mil… | istoedinheiro.com.br |
Diagnóstico: Resultados dominados por processos jurídicos (Jusbrasil), o influenciador “Rafael Chocolate” e registros empresariais. Zero presença minha. O nome completo tem competição moderada — mas majoritariamente de conteúdo negativo (processos, condenações), o que abre oportunidade.
Palavra-chave: “rafael”
Diagnóstico: Palavra extremamente genérica. AI Overview do Google mostra o significado do nome. Resultados incluem Wikipedia, Spotify, Instagram de celebridades. Meta realista: não ranquear para essa palavra isolada — mas usar como parte de long-tail keywords.
Palavra-chave: “root”
Diagnóstico: Dominada por Root App, jogo Root (Steam/BoardGameGeek), e o conceito técnico de “root” em celulares. Meta realista: ranquear para “rafaelroot” como brandable keyword, não para “root” isolado.
O plano: 3 artigos, 3 fases
Esta série é dividida em 3 partes com cronograma definido:
📋 Parte 1 — Diagnóstico e Setup (este artigo)
Quando: Semana 1 (8-14 de março de 2026)
- Documentar o baseline (screenshots + tabelas)
- Configurar stack técnica completa
- Google Search Console — submeter sitemap
- Google Analytics 4 — componente implementado (aguardando Measurement ID)
- SEO on-page (meta tags, structured data, Open Graph, Twitter Cards)
- Nginx — template de configuração otimizada documentado
- Palavras-chave primárias e secundárias definidas
- schema.org Person + WebSite + WebPage + Article + Breadcrumbs
📋 Parte 2 — Execução e Otimização
Quando: Semanas 2-6 (15 de março — 18 de abril de 2026)
- Criar conteúdo otimizado para as palavras-chave alvo
- Link building orgânico (GitHub, Stack Overflow, dev.to)
- Otimizar Core Web Vitals (LCP, FID, CLS)
- Implementar FAQ Schema e HowTo Schema
- Submeter artigos para indexação manual
- Monitorar Search Console: impressões, cliques, posição média
- A/B testing de title tags e meta descriptions
- Crosslink entre artigos do blog
📋 Parte 3 — Resultados e Análise
Quando: Semana 8-12 (maio-junho de 2026)
- Comparar SERPs com o baseline documentado
- Analisar métricas: CTR, impressões, posições alcançadas
- Documentar o que funcionou e o que não funcionou
- ROI do tempo investido vs. resultados
- Checklist replicável para outros profissionais
Stack técnica
Por que Astro?
O site rafaelroot.com é construído com Astro 5.17 — um framework SSG (Static Site Generator) que gera HTML puro sem JavaScript desnecessário. Isso é crítico para SEO porque:
Astro SSG → HTML estático → 0kb JS por padrão
├── LCP (Largest Contentful Paint): < 1.0s
├── FID (First Input Delay): 0ms (sem JS = sem blocking)
├── CLS (Cumulative Layout Shift): 0 (sem reflow)
└── TTFB: ~50ms (arquivo estático via Nginx)
Comparação com outros frameworks:
| Framework | JS Bundle | LCP típico | SEO Score |
|---|---|---|---|
| Astro (SSG) | 0 KB | < 1s | 100 |
| Next.js (SSR) | ~80 KB | 1.5-2.5s | 90-95 |
| Gatsby | ~70 KB | 1.5-3s | 85-95 |
| Create React App | ~200 KB | 3-5s | 50-70 |
| WordPress | ~150 KB | 2-4s | 60-80 |
Configuração do Astro para SEO
O astro.config.mjs já tem sitemap automático com i18n e prioridades granulares:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://rafaelroot.com',
integrations: [
sitemap({
i18n: {
defaultLocale: 'pt-br',
locales: {
'pt-br': 'pt-BR',
pt: 'pt-PT',
es: 'es',
en: 'en',
ru: 'ru',
},
},
serialize(item) {
const url = item.url;
if (url === 'https://rafaelroot.com/') {
item.priority = 1.0; // Homepage = máxima prioridade
} else if (url.match(/\/blog\/.+\//)) {
item.priority = 0.9; // Artigos = alta prioridade
item.changefreq = 'monthly';
} else if (url.match(/\/blog\/$/)) {
item.priority = 0.8; // Índice do blog
}
return item;
},
}),
],
});
Nginx: configuração otimizada para SEO
Performance do servidor afeta diretamente o ranking. Aqui está a configuração Nginx que uso:
# /etc/nginx/sites-available/rafaelroot.com
server {
listen 443 ssl http2;
server_name rafaelroot.com www.rafaelroot.com;
# SSL via Let's Encrypt
ssl_certificate /etc/letsencrypt/live/rafaelroot.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rafaelroot.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS — força HTTPS por 1 ano
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
root /var/www/rafaelroot.com/dist;
index index.html;
# Compressão Brotli + Gzip
brotli on;
brotli_types text/html text/css application/javascript application/json image/svg+xml;
brotli_comp_level 6;
gzip on;
gzip_types text/html text/css application/javascript application/json image/svg+xml;
gzip_min_length 256;
# Cache agressivo para assets estáticos
location ~* \.(css|js|woff2|woff|ttf|svg|png|jpg|webp|avif|ico)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Cache moderado para HTML
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
# Sitemap e robots — sem cache para atualizações rápidas
location ~* (sitemap.*\.xml|robots\.txt)$ {
expires 1d;
add_header Cache-Control "public";
}
# SPA fallback — Astro gera uma página por rota, sem fallback necessário
location / {
try_files $uri $uri/ $uri.html =404;
}
# Bloqueia acessos a arquivos sensíveis
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
# Redirect HTTP → HTTPS
server {
listen 80;
server_name rafaelroot.com www.rafaelroot.com;
return 301 https://rafaelroot.com$request_uri;
}
# Redirect www → non-www
server {
listen 443 ssl http2;
server_name www.rafaelroot.com;
ssl_certificate /etc/letsencrypt/live/rafaelroot.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rafaelroot.com/privkey.pem;
return 301 https://rafaelroot.com$request_uri;
}
Por que cada diretiva importa para SEO:
| Diretiva | Impacto SEO |
|---|---|
http2 | Multiplexação → menor TTFB → melhor LCP |
brotli | ~20% menor que gzip → transfer size menor |
expires 1y + immutable | Elimina re-downloads → melhor FCP |
HSTS | Google favorece HTTPS → ranking signal |
Cache-Control: must-revalidate | HTML sempre fresco → conteúdo atualizado indexado |
Google Search Console: setup completo ✅
Passo 1 — Verificar propriedade do domínio
- Acessar Google Search Console
- Adicionar propriedade → Domínio →
rafaelroot.com - Verificar via registro DNS TXT
- Adicionar o registro TXT no DNS do domínio
Status: ✅ Propriedade verificada com sucesso.
Passo 2 — Submeter o sitemap

URL do Sitemap: https://rafaelroot.com/sitemap-index.xml
Status: ✅ Sitemap processado com sucesso em 08/03/2026.
| Métrica | Valor |
|---|---|
| Sitemap index | sitemap-index.xml → sitemap-0.xml |
| Última leitura | 08/03/2026 |
| Status | Success |
| URLs descobertas | 16 |
| Vídeos descobertos | 0 |
No Search Console → Sitemaps → o sitemap já foi processado e todas as 16 URLs foram descobertas.
O sitemap indexa 16 páginas:
- 5 homepages (1 padrão + 4 locales)
- 5 índices de blog (1 padrão + 4 locales)
- 6 artigos de blog (3 PT-BR + 3 EN)
Passo 3 — Solicitar indexação manual
Para cada URL importante, usar a ferramenta Inspeção de URL e clicar em “Solicitar Indexação”:
Prioridade 1 (indexar imediatamente):
https://rafaelroot.com/
https://rafaelroot.com/blog/
Prioridade 2 (indexar na sequência):
https://rafaelroot.com/en/
https://rafaelroot.com/en/blog/
Prioridade 3 (artigos individuais):
https://rafaelroot.com/blog/engenharia-reversa-android-frida/
https://rafaelroot.com/blog/hardening-linux-seguranca-defensiva/
https://rafaelroot.com/blog/nginx-reverse-proxy-go-producao/
https://rafaelroot.com/en/blog/reverse-engineering-android-frida/
https://rafaelroot.com/en/blog/linux-server-hardening-checklist/
https://rafaelroot.com/en/blog/nginx-reverse-proxy-go-production/
Google Analytics 4: implementação
Passo 1 — Criar propriedade GA4
- Acessar Google Analytics
- Admin → Criar propriedade → rafaelroot.com
- Copiar o Measurement ID (formato:
G-XXXXXXXXXX)
Passo 2 — Adicionar o script no Astro
Criar um componente de analytics:
<!-- src/components/Analytics.astro -->
---
const GA_ID = import.meta.env.PUBLIC_GA_ID || 'G-XXXXXXXXXX';
const isProd = import.meta.env.PROD;
---
{isProd && (
<script async src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`}></script>
<script define:vars={{ GA_ID }}>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', GA_ID, {
send_page_view: true,
cookie_flags: 'SameSite=None;Secure',
});
</script>
)}
Passo 3 — Eventos customizados para SEO
Trackear interações que indicam engajamento (afetam bounce rate indiretamente):
// Scroll depth tracking
let scrollMarks = [25, 50, 75, 100];
window.addEventListener('scroll', () => {
const scrollPercent = Math.round(
(window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100
);
scrollMarks = scrollMarks.filter(mark => {
if (scrollPercent >= mark) {
gtag('event', 'scroll_depth', {
event_category: 'engagement',
event_label: `${mark}%`,
value: mark,
});
return false;
}
return true;
});
});
// Time on page
setTimeout(() => {
gtag('event', 'time_on_page', {
event_category: 'engagement',
event_label: '30s',
value: 30,
});
}, 30000);
// Code block copy tracking
document.querySelectorAll('.code-copy-btn').forEach(btn => {
btn.addEventListener('click', () => {
gtag('event', 'code_copy', {
event_category: 'engagement',
event_label: btn.closest('pre')?.querySelector('code')?.className || 'unknown',
});
});
});
Métricas que vamos rastrear
| Métrica | Fonte | Meta |
|---|---|---|
| Impressões orgânicas | Search Console | > 1.000/mês em 60 dias |
| Cliques orgânicos | Search Console | > 100/mês em 60 dias |
| Posição média | Search Console | Top 10 para “rafael cavalcanti da silva” |
| CTR orgânico | Search Console | > 5% |
| Sessões | GA4 | > 500/mês |
| Tempo médio na página | GA4 | > 3 minutos (artigos) |
| Bounce rate | GA4 | < 60% |
| Core Web Vitals | PageSpeed Insights | Tudo “Bom” |
Estratégia de palavras-chave
Análise de dificuldade e volume
| Palavra-chave | Volume estimado | Dificuldade | Estratégia |
|---|---|---|---|
rafael cavalcanti da silva | Baixo (~50/mês) | Média | Target primário — nome completo, competição de homônimos |
rafael cavalcanti | Médio (~500/mês) | Alta | Target secundário — muitos homônimos com presença forte |
rafaelroot | Zero (brand new) | Baixíssima | Branded keyword — criar do zero, dominar rapidamente |
rafael | Altíssimo | Impossível | Não targetar — usar apenas em long-tail |
root | Altíssimo | Impossível | Não targetar — usar apenas em contexto técnico |
rafael cavalcanti desenvolvedor | Baixo | Baixa | Long-tail — alta conversão |
rafael cavalcanti segurança | Baixo | Baixa | Long-tail — nicho específico |
rafael root engenharia reversa | Zero | Zero | Long-tail brandable — criar demanda |
Estratégia de conteúdo por keyword
rafael cavalcanti da silva
├── Homepage: Title tag otimizado
├── About section: Bio completa com o nome
├── Schema.org Person: Nome completo estruturado
├── Blog author: Byline em todos os artigos
└── Meta tags: description com nome completo
rafaelroot
├── Domínio: rafaelroot.com (já ativo)
├── GitHub: github.com/rafaelroot (criar/vincular)
├── Brand mention: usar em todos os profiles sociais
└── Anchor text: link building com "rafaelroot"
rafael cavalcanti desenvolvedor / segurança
├── Blog posts: conteúdo técnico demonstrando expertise
├── Structured data: Person → jobTitle, knowsAbout
└── Backlinks: contribuições em repositórios open source
SEO On-Page: o que já está implementado
Tudo abaixo já está live no código (não é aspiração — são implementações reais que você pode verificar no source).
✅ Title Tags otimizados
<!-- Homepage (real, extraído de BaseLayout.astro via i18n) -->
<title>Rafael Cavalcanti da Silva — Desenvolvedor Fullstack & Segurança</title>
<!-- Blog articles (real, extraído de BlogLayout.astro) -->
<title>{article.title}</title>
Regras aplicadas:
- Nome completo
Rafael Cavalcanti da Silvano início da title - Nome do site (
rafaelroot.com) NÃO está no<title>— o Google mostra separadamente via structured data WebSite - Dentro do limite de ~60 chars para evitar truncamento na SERP
- Delimitador único
—(sem misturar|e—)
✅ Meta Descriptions
<!-- Real, extraído de i18n/pt-br.ts -->
<meta name="description" content="Rafael Cavalcanti da Silva — Desenvolvedor Fullstack,
Especialista em Segurança e SEO. Fundador e mentor do xpusher.net e wsocket.io.
Projetos para MEC, CNJ, UNICEF, Norte Energia e GM. 16+ linguagens, 16 SDKs
e plataformas SaaS." />
✅ Structured Data (Schema.org) — 4 schemas na homepage
O BaseLayout.astro injeta 4 blocos JSON-LD:
- Person — nome completo, jobTitle, knowsAbout, sameAs, founder
- WebSite — nome do site, inLanguage multi-locale
- ProfilePage — página de perfil com mainEntity referenciando Person
- BreadcrumbList — Home → [locale]
O BlogLayout.astro adiciona:
5. Article — headline, datePublished, author, keywords
6. BreadcrumbList — Home → Blog → Artigo (3 níveis)
// Person (resumo — código real em BaseLayout.astro)
{
"@type": "Person",
"@id": "https://rafaelroot.com/#person",
"name": "Rafael Cavalcanti da Silva",
"alternateName": "rafaelroot",
"jobTitle": "Fullstack Developer & Security Specialist",
"knowsAbout": ["Reverse Engineering", "DevOps", "Frida", "Go", "Python", ...],
"sameAs": ["https://github.com/rafael4root"]
}
✅ Open Graph + Twitter Cards
<!-- Real, em BaseLayout.astro -->
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Rafael Cavalcanti — rafaelroot.com" />
<meta property="og:locale" content="pt_BR" />
<meta property="og:locale:alternate" content="en" />
<meta property="og:title" content="Rafael Cavalcanti da Silva | ..." />
<meta property="og:image" content="https://rafaelroot.com/og-image.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:creator" content="@rafael4root" />
✅ hreflang (5 idiomas + x-default)
<link rel="alternate" hreflang="pt-BR" href="https://rafaelroot.com/" />
<link rel="alternate" hreflang="pt-PT" href="https://rafaelroot.com/pt/" />
<link rel="alternate" hreflang="es" href="https://rafaelroot.com/es/" />
<link rel="alternate" hreflang="en" href="https://rafaelroot.com/en/" />
<link rel="alternate" hreflang="ru" href="https://rafaelroot.com/ru/" />
<link rel="alternate" hreflang="x-default" href="https://rafaelroot.com" />
✅ Extras já implementados
- SEO geográfico:
geo.region,geo.placename,geo.position,ICBM - PWA:
manifest.webmanifest,theme-color,apple-mobile-web-app - Canonical URLs em todas as páginas
- robots.txt com rules para Googlebot e sitemap
- Microdata nos artigos (itemscope, itemprop)
- Google Analytics 4 componente criado (
Analytics.astro), aguardandoPUBLIC_GA_ID - hreflang nos artigos do blog — cada tradução aponta para todas as outras versões
Auditoria SEO: verificando contra o guia oficial do Google
Depois de implementar o básico, comparei toda a nossa configuração com o guia oficial de SEO do Google e como o Google Search funciona. Essa auditoria revelou 3 problemas críticos que a maioria dos devs não perceberia.
🚨 Fix 1: robots.txt estava bloqueando CSS do Googlebot ✅ CORRIGIDO
O problema: Nosso robots.txt tinha Disallow: /_astro/ — que bloqueia o Googlebot de acessar os arquivos CSS bundlados pelo Astro (index.*.css, _slug_.*.css).
A documentação oficial do Google diz:
“Quando o Google rastreia uma página, ele deve idealmente ver a página da mesma forma que um usuário médio. Para isso, o Google precisa ter acesso aos mesmos recursos que o navegador do usuário. Se seu site está escondendo componentes importantes (como CSS e JavaScript), o Google pode não conseguir entender suas páginas, o que significa que elas podem não aparecer nos resultados de busca ou rankear bem.”
Isso significa que o Google estava vendo nosso site como HTML sem estilo — incapaz de avaliar layout, legibilidade ou estrutura visual. Isso pode prejudicar silenciosamente o ranking sem nenhum aviso no Search Console.
Correção:
# robots.txt — ANTES (bloqueando CSS)
User-agent: *
Allow: /
Disallow: /_astro/ ← BLOQUEIA CSS BUNDLADO!
# robots.txt — DEPOIS (correto)
User-agent: *
Allow: /
# Removido Disallow: /_astro/ — Googlebot precisa de acesso ao CSS/JS
Verificação: Use a URL Inspection Tool no Search Console → clique em “Testar URL ativo” → “Ver página testada” → “Screenshot”. Se o Google não consegue carregar seu CSS, o screenshot vai mostrar conteúdo sem estilo.
🚨 Fix 2: Removido <meta name="keywords"> ✅ CORRIGIDO
O problema: Tanto o BaseLayout.astro quanto o BlogLayout.astro incluíam <meta name="keywords">.
A documentação do Google é inequívoca:
“Google Search não usa a meta tag keywords.”
Essa tag é um resquício dos anos 2000. O Google a ignora desde pelo menos 2009. Incluí-la:
- Desperdiça bytes
- Revela sua estratégia de keywords para competidores
- Fornece zero benefício SEO
Correção: Removida de ambos os layouts. O array tags continua existindo no frontmatter para categorização do blog e article:tag no Open Graph — que plataformas sociais usam.
✅ Fix 3: Adicionado hreflang nos artigos do blog ✅ CORRIGIDO
O problema: A homepage (BaseLayout.astro) tinha hreflang alternativo para todos os 5 idiomas, mas os artigos do blog (BlogLayout.astro) não tinham nenhum. Isso significa que o Google não conseguia descobrir que nossos artigos PT-BR e EN são traduções um do outro.
Sem hreflang nos artigos, o Google pode:
- Indexar apenas uma versão e ignorar a outra
- Tratar como conteúdo duplicado
- Mostrar a versão no idioma errado para os usuários
Correção: Implementado sistema translationKey:
# No frontmatter de cada artigo:
translationKey: "reverse-engineering-android-frida"
Artigos com o mesmo translationKey são automaticamente cross-linkados com hreflang no build:
<!-- Output gerado para o artigo PT-BR -->
<link rel="alternate" hreflang="pt-BR" href="https://rafaelroot.com/blog/engenharia-reversa-android-frida/" />
<link rel="alternate" hreflang="en" href="https://rafaelroot.com/en/blog/reverse-engineering-android-frida/" />
<link rel="alternate" hreflang="x-default" href="https://rafaelroot.com/blog/engenharia-reversa-android-frida/" />
✅ Fix 4: Melhorado alt text das imagens ✅ CORRIGIDO
O Google diz:
“Alt text é uma peça de texto curta, mas descritiva, que explica a relação entre a imagem e seu conteúdo.”
As imagens dos cases tinham alt text mínimo (só o nome do produto). Corrigido para incluir descrições contextuais:
<!-- ANTES -->
<img alt="wsocket.io" />
<!-- DEPOIS -->
<img alt="wsocket.io — Realtime WebSocket Infrastructure" />
✅ Fix 5: Structured data do nome do site ✅ CORRIGIDO
O Google usa dados estruturados WebSite para determinar como o nome do site aparece nos resultados de busca. Nossa implementação tinha três problemas:
Problema 1: O campo name era uma string composta "Rafael Cavalcanti — rafaelroot.com" — muito longa e não é um nome de site limpo. O Google prefere um nome conciso e reconhecível.
Problema 2: Não havia propriedade alternateName — o Google usa isso para associar buscas alternativas ao seu site.
Problema 3: O campo url estava sem barra final (trailing slash). A especificação do Google mostra explicitamente "url": "https://example.com/".
// ANTES
{
"@type": "WebSite",
"name": "Rafael Cavalcanti — rafaelroot.com",
"url": "https://rafaelroot.com"
}
// DEPOIS
{
"@type": "WebSite",
"name": "rafaelroot.com",
"alternateName": [
"Rafael Cavalcanti",
"Rafael Cavalcanti da Silva",
"rafaelroot"
],
"url": "https://rafaelroot.com/"
}
A documentação do Google diz:
“O nome não deve ser excessivamente longo. Uma boa regra é mantê-lo abaixo de ~50 caracteres.”
Também atualizamos og:site_name para ficar consistente (rafaelroot.com em vez da string composta) em ambos BaseLayout.astro e BlogLayout.astro.
✅ Fix 6: Otimização para sitelinks ✅ CORRIGIDO
Sitelinks são os links indentados que o Google às vezes mostra abaixo do resultado principal de busca. Eles são totalmente automatizados — o algoritmo do Google decide quais páginas mostrar baseado na estrutura do seu site.
A documentação do Google diz:
“Nós só mostramos sitelinks para resultados quando achamos que são úteis para o usuário. Se a estrutura do seu site não permite que nossos algoritmos encontrem bons sitelinks, ou não achamos que os sitelinks são relevantes para a consulta do usuário, não vamos mostrá-los.”
O que corrigimos: A navegação do footer só tinha links #anchor (ex: #about, #skills, #contact) — todos apontam para seções na mesma página e não são URLs rastreáveis reais. O Google não consegue gerar sitelinks a partir de links âncora para a mesma página.
Adicionamos um link real rastreável para o Blog no nav do footer, dando ao Google pelo menos uma URL interna distinta para potencialmente exibir como sitelink:
<!-- ANTES: nav do footer só tinha #anchors -->
<a href="#about">Sobre</a>
<a href="#skills">Skills</a>
...
<!-- DEPOIS: adicionado URL real antes dos anchors -->
<a href="/blog">Blog</a>
<a href="#about">Sobre</a>
<a href="#skills">Skills</a>
...
Por que isso importa para um portfólio single-page: Como nossa homepage é essencialmente uma página longa com seções, o Blog é a única hierarquia de página separada real. Ao linká-lo no footer (que aparece em toda página), maximizamos as chances dele aparecer como sitelink.
Nota: Você não pode definir sitelinks manualmente. O algoritmo do Google os determina automaticamente. Tudo que podemos fazer é tornar nossa estrutura de links internos o mais clara possível.
✅ Fix 7: ProfilePage structured data ✅ CORRIGIDO
A documentação de ProfilePage do Google é projetada para páginas onde o foco principal é uma única pessoa ou organização. Os casos de uso válidos incluem explicitamente:
“Uma página ‘Sobre Mim’ em um site de blog” “Uma página de perfil de usuário em um fórum ou rede social”
Nossa homepage é literalmente um portfólio pessoal — uma página “Sobre Mim”. É um match perfeito.
O que mudamos: Atualizamos o JSON-LD da homepage de WebPage para ProfilePage (que é um subtipo de WebPage), e adicionamos mainEntity referenciando o schema Person existente e dateCreated:
// ANTES
{
"@type": "WebPage",
"name": "Rafael Cavalcanti da Silva — Fullstack Developer",
"about": { "@id": "https://rafaelroot.com/#person" }
}
// DEPOIS
{
"@type": "ProfilePage",
"name": "Rafael Cavalcanti da Silva — Fullstack Developer",
"mainEntity": { "@id": "https://rafaelroot.com/#person" },
"about": { "@id": "https://rafaelroot.com/#person" },
"dateCreated": "2026-02-01T00:00:00-03:00",
"dateModified": "2026-03-08T..."
}
Por que ProfilePage vs WebPage: Como ProfilePage estende WebPage, todas as propriedades existentes são preservadas. Mas agora o Google sabe que não é apenas qualquer página web — é especificamente uma página de perfil sobre uma pessoa, o que pode melhorar como a página aparece nas Features de Discussions and Forums e ajuda o Google a entender o criador por trás do conteúdo.
O schema Person já era rico (name, alternateName, sameAs, image, knowsAbout, founder) — o ProfilePage apenas fornece o wrapper correto que diz ao Google “esta página É sobre esta pessoa.”
✅ Fix 8: Otimização de title links ✅ CORRIGIDO
A documentação de title links do Google explica como os títulos dos resultados de busca são criados. Diretrizes-chave que verificamos:
Problema 1 — Título longo demais (truncamento): Nossos títulos tinham ~87-100 caracteres. O Google trunca para caber na largura do dispositivo (~55-60 chars visíveis). Exemplo:
ANTES (100 chars — truncado na SERP):
Rafael Cavalcanti da Silva | Desenvolvedor Fullstack & Especialista em Segurança — rafaelroot.com
DEPOIS (56 chars — totalmente visível):
Rafael Cavalcanti da Silva — Desenvolvedor Fullstack & Segurança
Problema 2 — Nome do site redundante: O Google diz:
“O Google pode omitir o nome do site do title link, se for repetitivo com o nome do site já mostrado no resultado de busca.”
Como temos dados estruturados WebSite com name: 'rafaelroot.com', o Google já mostra o nome do site como componente separado nas SERPs. Ter no <title> era redundante — desperdiçando 18 caracteres de espaço.
Problema 3 — Delimitadores misturados: O título usava tanto | quanto —. O Google recomenda um único delimitador consistente.
Problema 4 — Títulos do blog: {title} — rafaelroot.com em todo post. O Google diz:
“Mostrar esse texto no
<title>de cada página do seu site parecerá repetitivo se várias páginas aparecerem para a mesma consulta.”
Títulos do blog agora usam apenas {title} — o nome do site é mostrado pelo Google via structured data.
Mudanças aplicadas em todos os 5 idiomas:
| Locale | Antes | Depois |
|---|---|---|
| pt-br | ...| Desenvolvedor Fullstack & Especialista em Segurança — rafaelroot.com | ...— Desenvolvedor Fullstack & Segurança |
| en | ...| Fullstack Developer & Security Specialist — rafaelroot.com | ...— Fullstack Developer & Security |
| es | ...| Desarrollador Fullstack & Especialista en Seguridad — rafaelroot.com | ...— Desarrollador Fullstack & Seguridad |
| ru | ...| Fullstack-разработчик и специалист... — rafaelroot.com | ...— Fullstack & Безопасность |
O que o Google diz para NÃO se preocupar
Essas são “melhores práticas” de SEO amplamente difundidas que a documentação oficial do Google explicitamente desmente. Saber o que ignorar economiza tempo e previne sobre-otimização.
| Mito | Posição real do Google |
|---|---|
| Meta tag keywords | ”Google Search não usa a meta tag keywords.” |
| Keyword stuffing | Contra as políticas de spam. Escreva naturalmente. |
| Keywords no nome do domínio | ”Do ponto de vista de ranking, as keywords no nome do domínio quase não têm efeito além de aparecer nos breadcrumbs.” |
| Tamanho do conteúdo | ”O tamanho do conteúdo sozinho não importa para fins de ranking (não existe número mágico de palavras).” |
| Ordem dos headings | ”Do ponto de vista do Google Search, não importa se você está usando fora de ordem.” |
| E-E-A-T é fator de ranking | ”Não, não é.” (É um framework de avaliação de qualidade, não um sinal de ranking) |
| “Penalidade” por conteúdo duplicado | ”É ineficiente, mas não é algo que cause uma ação manual.” |
| Subdomínios vs subdiretórios | Sem diferença de ranking — faça o que faz sentido pro seu negócio. |
Timeline realista segundo o Google
Da documentação oficial do Google:
“Lembre-se que levará tempo para ver resultados: tipicamente de quatro meses a um ano desde que você começa a fazer mudanças até começar a ver os benefícios.”
Nosso cronograma de 12 semanas é ambicioso. Expectativas realistas:
- Semanas 1-4: Páginas são indexadas, primeiras impressões aparecem no Search Console
- Semanas 4-8: Posições começam a estabilizar, keywords de marca (
rafaelroot) devem rankear - Meses 3-6: Keywords competitivas (
rafael cavalcanti da silva) começam a subir - Meses 6-12: Autoridade completa construída, long-tail keywords rankeando organicamente
O que realmente importa (segundo o Google)
Baseado na documentação oficial, estes são os sinais que genuinamente afetam o ranking:
- Qualidade do conteúdo — Conteúdo útil, confiável e people-first
- Links de outros sites — “O Google encontra páginas principalmente através de links de outras páginas que já rastreou”
- Experiência da página — Core Web Vitals (LCP, FID, CLS)
- HTTPS — Sinal de ranking
- Mobile-friendliness — Mobile-First Indexing
- Dados estruturados — Não é sinal de ranking, mas habilita rich results (estrelas, carrosséis, FAQs)
- URLs, títulos e meta descriptions descritivos — Afetam CTR, que indiretamente afeta ranking
Core Web Vitals: PageSpeed Insights (8 de março de 2026)
Primeira análise rodada no site ativo. Desktop e mobile testados para ter um panorama completo.
Scores Desktop

| Categoria | Score | Status |
|---|---|---|
| Performance | 90/100 | 🟢 Verde |
| Accessibility | 96/100 | 🟢 Verde |
| Best Practices | 100/100 | 🟢 Perfeito |
| SEO | 100/100 | 🟢 Perfeito |
Métricas Desktop
| Métrica | Valor | Avaliação |
|---|---|---|
| First Contentful Paint (FCP) | 1.3s | 🟢 |
| Largest Contentful Paint (LCP) | 1.3s | 🟢 |
| Total Blocking Time (TBT) | 0 ms | 🟢 Perfeito |
| Cumulative Layout Shift (CLS) | 0 | 🟢 Perfeito |
| Speed Index (SI) | 1.8s | 🟡 |
TBT = 0ms e CLS = 0 — zero bloqueio, zero shift de layout. Isso é o que se obtém com SSG estático + JavaScript mínimo.
Scores Mobile — o desafio real
Mobile é onde o Google realmente te ranqueia. O Google usa Mobile-First Indexing, ou seja, a versão mobile do site é o que determina seu ranking. O teste mobile simula um Moto G Power em 4G lento — um ambiente muito mais severo que o desktop.
| Categoria | Desktop | Mobile | Delta |
|---|---|---|---|
| Performance | 90 | 58 | -32 ⚠️ |
| Accessibility | 96 | 96 | 0 |
| Best Practices | 100 | 100 | 0 |
| SEO | 100 | 100 | 0 |
Métricas Mobile (baseline pré-correção)
| Métrica | Desktop | Mobile | Avaliação |
|---|---|---|---|
| FCP | 1.3s | 6.7s | 🔴 |
| LCP | 1.3s | 6.7s | 🔴 |
| TBT | 0 ms | 0 ms | 🟢 |
| CLS | 0 | 0 | 🟢 |
| Speed Index | 1.8s | 9.4s | 🔴 |
Por que mobile é 32 pontos mais baixo?
O teste Lighthouse mobile usa throttling 4G lento (1.6 Mbps download, 750ms RTT) e CPU de celular mid-range. Isso amplifica todos os problemas:
- CSS render-blocking: 4.570ms de economia potencial — O CSS síncrono do Google Fonts + devicon bloqueia completamente o primeiro paint. No desktop custa ~980ms, mas no 4G lento os 3 round-trips se tornam devastadores
- Main-thread work: 3.8s — Parsing de CSS, recalculação de estilos, e computação de layout para a página inteira. Numa CPU throttled, essas operações levam 4x mais tempo
- 9 long tasks encontradas — Principalmente do parsing do CSS completo numa CPU lenta e recalculações de estilo para efeitos de backdrop-filter
Problemas identificados e corrigidos

1. Recursos render-blocking (Est. economia: 980ms) ✅ CORRIGIDO
Problema: CSS do Google Fonts e devicon.min.css era carregado de forma síncrona no <head>, bloqueando o First Contentful Paint.
Correção aplicada: Troca de <link rel="stylesheet"> para carregamento assíncrono:
<!-- ANTES (render-blocking) -->
<link href="https://fonts.googleapis.com/css2?..." rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/.../devicon.min.css" />
<!-- DEPOIS (non-blocking) -->
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?..."
onload="this.onload=null;this.rel='stylesheet'" />
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?..." /></noscript>
<link rel="preload" as="style" href="https://cdn.jsdelivr.net/.../devicon.min.css"
onload="this.onload=null;this.rel='stylesheet'" />
<noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/.../devicon.min.css" /></noscript>
Isso permite que o browser renderize imediatamente com system fonts, e depois faça swap para as custom fonts — economizando até 980ms de tempo bloqueado.
2. Font display (Est. economia: 60ms) ✅ CORRIGIDO
O parâmetro display=swap já estava na URL do Google Fonts, mas o <link rel="stylesheet"> síncrono ainda bloqueava a renderização. O padrão async preload acima elimina isso completamente.
3. CSS não utilizado (Est. economia: 18 KiB) ✅ MITIGADO
Problema: devicon.min.css contém ícones para 200+ tecnologias mas usamos apenas ~16.
Correção: Carregar de forma assíncrona significa que não bloqueia mais a renderização. O browser baixa em paralelo sem atrasar o FCP. Uma otimização futura pode inlinar apenas os 16 ícones que usamos.
4. Contraste de acessibilidade ✅ CORRIGIDO
Problema: Cores de foreground/background falharam no teste de contraste WCAG AA. A cor --muted (#5a7a9a) contra o fundo escuro #040a12 tinha ratio de apenas 4.45:1 (mínimo é 4.5:1).
Correção: Ajuste de --muted de #5a7a9a → #6889a6, alcançando ratio de 5.4:1 — bem acima do threshold AA mantendo o mesmo tom azul-cinza.
/* ANTES */
--muted: #5a7a9a; /* 4.45:1 contraste — FALHA AA */
/* DEPOIS */
--muted: #6889a6; /* 5.4:1 contraste — PASSA AA ✅ */
5. Cache lifetimes (Est. economia: 120 KiB) ⏳ LADO DO SERVIDOR
Requer headers Cache-Control no Nginx — já configurado no template Nginx acima. Será aplicado no deploy.
6. Long main-thread tasks ✅ CORRIGIDO (específico para mobile)
Desktop: Apenas 1 long task detectada — aceitável. Mobile: 9 long tasks, 3.8s de trabalho na main-thread — problemático.
Causas-raiz no mobile:
- Parsing de CSS do layout completo para seções fora da tela (a página tem 9 seções mas só o hero é visível no carregamento)
backdrop-filter: blur()dispara camadas de compositing em cada elemento que usa — caro para GPUs mobile- Computação de layout para conteúdo complexo de grid/flex abaixo da dobra
Correções aplicadas:
/* 1. content-visibility: auto — pular renderização de seções abaixo da dobra */
section {
content-visibility: auto;
contain-intrinsic-size: auto 600px;
}
/* Isso diz ao browser: "não se preocupe em computar layout, paint
ou estilo para seções que não estão visíveis ainda."
Economia estimada: 40-60% do trabalho de renderização inicial. */
/* 2. Desativar efeitos caros no mobile */
@media (max-width: 768px) {
.nav-outer { backdrop-filter: blur(8px); } /* reduzido de 16px */
.btn { backdrop-filter: none; }
.hero-badge {
backdrop-filter: none;
animation: none; /* remove animação flutuante */
}
.bio-grid { backdrop-filter: none; }
}
content-visibility: auto é um game-changer para páginas longas — o browser só renderiza cada seção quando o usuário scrolla perto dela. Combinado com desativar efeitos GPU caros no mobile, isso deve reduzir drasticamente o trabalho da main-thread.

✅ Fix 9: Refinamentos pós-deploy — CLS & contraste ✅ CORRIGIDO
Após deploy de todas as correções e re-execução do PageSpeed, dois problemas remanescentes surgiram:
Problema 1 — CLS 0.113 no mobile (acima do threshold “bom” de 0.1):
font-display: swap na URL do Google Fonts causa um layout shift quando as web fonts (Inter, JetBrains Mono) substituem as fallbacks do sistema. O texto reflui porque as métricas das fontes diferem entre famílias.
Correção: Declarações CSS @font-face override com size-adjust que alinham as métricas das web fonts com os fallbacks, para que o swap cause zero reflow:
/* Override de métricas de font — prevenir CLS do font swap */
@font-face {
font-family: 'JetBrains Mono Fallback';
src: local('Courier New');
size-adjust: 108%;
ascent-override: normal;
descent-override: normal;
line-gap-override: normal;
}
@font-face {
font-family: 'Inter Fallback';
src: local('Arial');
size-adjust: 100%;
ascent-override: normal;
descent-override: normal;
line-gap-override: normal;
}
/* Font stacks atualizados com fallbacks de métricas */
--mono: 'JetBrains Mono', 'JetBrains Mono Fallback', 'Fira Code', monospace;
--sans: 'Inter', 'Inter Fallback', system-ui, -apple-system, sans-serif;
O browser usa o @font-face fallback (ex: Courier New a 108% de tamanho) enquanto a web font carrega. Quando o swap ocorre, o texto ocupa o mesmo espaço — sem layout shift.
Problema 2 — Accessibility desktop ainda em 96 (contraste em backgrounds glass):
A cor --muted (#6889a6) alcançava 5.4:1 no background puro (#040a12), mas o backdrop-filter: blur() da nav cria um background efetivo mais claro que #040a12. Nessas superfícies glass, o contraste caía abaixo de 4.5:1.
Correção: Bumped --muted de #6889a6 → #7090b0 (5.96:1 no --bg, 5.44:1 em superfícies glass):
/* ANTES */
--muted: #6889a6; /* 5.4:1 no bg, mas falha no glass */
/* DEPOIS */
--muted: #7090b0; /* 5.96:1 no bg, 5.44:1 no glass ✅ */
Resultados pós-deploy (8 de março de 2026)
Resultados reais após deploy de todas as 8 correções:
| Métrica | Desktop (antes) | Desktop (após) | Mobile (antes) | Mobile (após) |
|---|---|---|---|---|
| Performance | 90 | 90 | 58 | 87 (+29) |
| Accessibility | 96 | 96 | 96 | 100 (+4) |
| FCP | 1.3s | 1.4s | 6.7s | 2.8s |
| LCP | 1.3s | 1.4s | 6.7s | 2.8s |
| Speed Index | 1.8s | 1.4s | 9.4s | 2.9s |
| TBT | 0ms | 0ms | 0ms | 0ms |
| CLS | 0 | 0.076 | 0 | 0.113 |
| Best Practices | 100 | 100 | 100 | 100 |
| SEO | 100 | 100 | 100 | 100 |
Grandes vitórias:
- Performance Mobile: 58 → 87 (+29 pontos) — o maior impacto para SEO (Mobile-First Indexing)
- Accessibility Mobile: 96 → 100 — score perfeito
- FCP/LCP Mobile: 6.7s → 2.8s — 58% mais rápido no primeiro paint
- Speed Index Mobile: 9.4s → 2.9s — 69% mais rápido na percepção de carregamento
Restante após Fix 9 (CLS + contraste):
- CLS deve cair abaixo de 0.1 com os fallbacks
size-adjust - Accessibility desktop deve atingir 100 com o bump de
--mutedpara#7090b0 - Cache lifetimes (Fix 5) ainda pendente — requer deploy Nginx
Por que mobile importa mais: O Google usa Mobile-First Indexing — o score mobile é o que realmente afeta seu ranking. Ir de 58 para 87 é a melhoria de maior impacto que fizemos para SEO.
Cronograma detalhado: próximas 12 semanas
Fase 1 — Foundation (Semanas 1-2)
| Dia | Tarefa | Status |
|---|---|---|
| D1 (08/mar) | Documentar baseline das SERPs | ✅ |
| D1 (08/mar) | Escrever artigo Parte 1 (este) | ✅ |
| D1 (08/mar) | Otimizar title tags (nome completo) | ✅ |
| D1 (08/mar) | Implementar componente Analytics.astro (GA4) | ✅ |
| D1 (08/mar) | Verificar schema.org Person na homepage | ✅ já existia |
| D1 (08/mar) | Rodar PageSpeed Insights — primeira análise | ✅ Desktop 90/96/100/100, Mobile 58/96/100/100 |
| D1 (08/mar) | Corrigir CSS render-blocking (fonts + devicon async) | ✅ |
| D1 (08/mar) | Corrigir contraste de acessibilidade (cor —muted) | ✅ |
| D1 (08/mar) | Corrigir perf mobile: content-visibility + reduzir efeitos GPU | ✅ |
| D1 (08/mar) | Auditar contra guia oficial de SEO do Google | ✅ |
| D1 (08/mar) | Corrigir robots.txt — desbloquear /_astro/ CSS do Googlebot | ✅ |
| D1 (08/mar) | Remover <meta name="keywords"> (Google ignora) | ✅ |
| D1 (08/mar) | Adicionar hreflang nos artigos do blog (sistema translationKey) | ✅ |
| D1 (08/mar) | Melhorar alt text das imagens (contexto descritivo) | ✅ |
| D1 (08/mar) | Corrigir structured data do nome do site (WebSite name + alternateName + trailing slash) | ✅ |
| D1 (08/mar) | Otimizar para sitelinks (adicionar link Blog rastreável no footer) | ✅ |
| D1 (08/mar) | Adicionar ProfilePage structured data (upgrade de WebPage) | ✅ |
| D1 (08/mar) | Otimizar title links (remover nome do site redundante, corrigir tamanho, delimitador único) | ✅ |
| D1 (08/mar) | Deploy de todas as correções + re-rodar PageSpeed: Mobile 58→87, Accessibility 96→100 | ✅ |
| D1 (08/mar) | Corrigir CLS (font size-adjust fallbacks) + contraste (#7090b0 para backgrounds glass) | ✅ |
| D2 (09/mar) | Configurar Google Search Console | ✅ |
| D2 (09/mar) | Submeter sitemap-index.xml | ✅ |
| D3 (10/mar) | Criar propriedade Google Analytics 4 | ⬜ |
| D3 (10/mar) | Configurar PUBLIC_GA_ID no .env | ⬜ |
| D5 (12/mar) | Otimizar Nginx (brotli, headers, cache) | ⬜ |
| D7 (14/mar) | Checkpoint Semana 1: verificar indexação | ⬜ |
Fase 2 — Content & Authority (Semanas 3-6)
| Semana | Tarefa |
|---|---|
| S3 | Criar/otimizar perfis: GitHub, LinkedIn, dev.to |
| S3 | Publicar artigo Parte 2 da série |
| S4 | Primeiro blog post otimizado para “rafael cavalcanti desenvolvedor” |
| S4 | Submeter site para diretórios dev (awesome lists, etc.) |
| S5 | Crosslinks entre artigos existentes |
| S5 | Criar página /about com schema.org Person completo |
| S6 | Guest post ou contribuição open source significativa |
| S6 | Checkpoint mês 1: verificar posições no Search Console |
Fase 3 — Measurement & Optimization (Semanas 7-12)
| Semana | Tarefa |
|---|---|
| S7 | Analisar dados de Search Console (impressões, cliques, CTR) |
| S7 | A/B test de title tags nos artigos com menor CTR |
| S8 | Publicar artigo Parte 3 com resultados medidos |
| S9 | Otimizar pages com base em Core Web Vitals reais |
| S10 | Expandir FAQ Schema em artigos técnicos |
| S11 | Revisão de backlinks e oportunidades de link building |
| S12 | Relatório final: comparar SERPs com baseline |
Métricas baseline (8 de março de 2026)
Registro formal do ponto zero para comparação futura:
╔══════════════════════════════════════════════════════════════╗
║ BASELINE — 08/03/2026 ║
╠══════════════════════════════════════════════════════════════╣
║ ║
║ "rafael cavalcanti da silva" ║
║ → Posição no Google: NÃO APARECE (fora do top 100) ║
║ → Resultado mais próximo: homônimos no Jusbrasil ║
║ ║
║ "rafael cavalcanti" ║
║ → Posição no Google: NÃO APARECE (fora do top 100) ║
║ → SERP dominada por: LinkedIn, Instagram, Escavador ║
║ ║
║ "rafaelroot" ║
║ → Posição no Google: NÃO TESTADO (keyword inexistente) ║
║ → Volume de buscas: 0 ║
║ ║
║ "rafael root engenharia reversa" ║
║ → Posição no Google: NÃO TESTADO ║
║ → Volume de buscas: 0 ║
║ ║
║ Páginas indexadas: 0 ║
║ Impressões Search Console: 0 ║
║ Cliques orgânicos: 0 ║
║ Backlinks conhecidos: 0 ║
║ ║
╚══════════════════════════════════════════════════════════════╝
O que vem na Parte 2
No próximo artigo (previsto para semana 3-4), vamos documentar:
- Resultados das primeiras 2 semanas — quantas páginas o Google indexou? Alguma impressão já apareceu?
- Implementação real do GA4 — eventos customizados, dashboard de SEO, relatórios automáticos
- Link building técnico — como usar GitHub, Stack Overflow e contribuições open source para gerar autoridade
- Core Web Vitals na prática — resultado do PageSpeed Insights antes/depois das otimizações Nginx
- Análise do Search Console — primeiros dados reais de impressões e posição média
Checklist resumo — o que fizemos hoje
- Baseline documentado: screenshots e tabelas de todas as keywords alvo
- Stack técnica definida: Astro 5.17 + Nginx + GA4 + Search Console
- Configuração da Astro para SEO: sitemap com prioridades, i18n, drafts
- Template de configuração Nginx com brotli, HSTS, cache otimizado
- Componente Analytics.astro criado com GA4 + eventos de engagement
- Title tags corrigidos: nome completo em todos os 5 idiomas
- schema.org: Person + WebSite + ProfilePage + Article + Breadcrumbs
- Open Graph + Twitter Cards + hreflang + canonical (já existiam)
- Estratégia de palavras-chave com análise de dificuldade
- Cronograma de 12 semanas dividido em 3 fases
- Métricas baseline registradas formalmente
- Google Search Console configurado + sitemap submetido (08/mar — 16 URLs descobertas, status: Success)
- PageSpeed Insights: Desktop 90/96/100/100 — Mobile 58/96/100/100
- Corrigido CSS render-blocking: fonts + devicon carregados async (desktop: 980ms, mobile: 4.570ms economia)
- Corrigido contraste de acessibilidade:
--muted→ ratio 5.4:1 (WCAG AA) - Corrigida performance mobile:
content-visibility: auto+ reduzidobackdrop-filterno mobile - Auditado contra guia oficial de SEO do Google
- Corrigido robots.txt: removido
Disallow: /_astro/— Googlebot estava bloqueado de renderizar CSS - Removido
<meta name="keywords">de ambos layouts — Google explicitamente ignora essa tag - Adicionado hreflang nos artigos do blog via sistema
translationKey— cross-links PT-BR ↔ EN - Melhorado alt text das imagens com contexto descritivo (não apenas nomes de produtos)
- Corrigido structured data do nome do site: WebSite
name: 'rafaelroot.com'+ arrayalternateName+ trailing slash naurl - Otimizado para sitelinks: adicionado link Blog rastreável no nav do footer (URL real, não #anchor)
- Adicionado ProfilePage structured data: upgrade da homepage de
WebPageparaProfilePagecommainEntityreferenciando Person - Otimizado title links: removido
rafaelroot.comredundante de todos os títulos (nome do site mostrado separadamente via schema WebSite), tamanho <60 chars, delimitador único— - PageSpeed pós-deploy: Mobile 58→87 (+29 perf), 96→100 accessibility | Desktop 90/96/100/100
- Corrigido CLS:
@font-facefallbacks comsize-adjustpara JetBrains Mono (108%) e Inter (100%) — previne layout shift dofont-display: swap - Corrigido contraste em backgrounds glass:
--mutedbumped de#6889a6→#7090b0(5.96:1 no bg, 5.44:1 em superfícies glass) - Pendente: Criar propriedade GA4 + definir
PUBLIC_GA_ID - Pendente: Deploy com Nginx otimizado (cache headers)
- Pendente: Re-rodar PageSpeed após deploy do Fix 9 (esperar CLS < 0.1, Desktop Accessibility 100)
- Pendente: Verificar se o Google renderiza a página corretamente (URL Inspection → Screenshot)
Próximo passo imediato: Deploy dos refinamentos de CLS + contraste (Fix 9), depois re-rodar PageSpeed para verificar que CLS cai abaixo de 0.1 e Desktop Accessibility atinge 100. Após isso, configurar GA4 e otimizar headers de cache do Nginx.
Este artigo faz parte da série “Do zero ao topo do Google”. Acompanhe a Parte 2 (em breve) e a Parte 3 (em breve) para ver os resultados em tempo real.
Última atualização: 8 de março de 2026