← Voltar ao Blog
· 25 min leitura ·

Case Study SEO: Do zero ao topo do Google — Parte 1: Diagnóstico, stack técnica e primeiros passos

Série documentando em tempo real o processo de posicionamento das palavras-chave 'Rafael Cavalcanti da Silva', 'rafael cavalcanti', 'rafael' e 'root' no Google — usando Astro, Nginx, Google Analytics 4, Search Console e técnicas avançadas de SEO técnico e de conteúdo.

#seo#google#astro#nginx#analytics#case-study#posicionamento
Compartilhar

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”

SERP do Google para "rafael cavalcanti" em 08/03/2026 — nenhum resultado é meu

PosiçãoResultadoDomínio
1Rafael Cavalcanti — Bradesco (LinkedIn)linkedin.com
2Rafael Cavalcanti (@rafaelcavalcantig)instagram.com
3Rafael Cavalcanti Garcia de Castro Alvesescavador.com
4Rafael Cavalcanti — Processosjusbrasil.com.br
5Rafael Cavalcanti, diretor do Bradesco…tiinside.com.br
6Rafael Cavalcanti de Souza — FAPESPbv.fapesp.br
7Rafael 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”

SERP do Google para "rafael cavalcanti da silva" em 08/03/2026 — dominada por homonimos e processos

PosiçãoResultadoDomínio
1Rafael Cavalcanti da Silva — Processosjusbrasil.com.br
27 perfis com “Rafael Cavalcanti Da Silva”br.linkedin.com
3Quem é Rafael Chocolate, influencer…g1.globo.com
4RAFAEL CAVALCANTI DA SILVA SOCIEDADE…empresas.serasaexperian.com.br
5O influenciador Rafael Francisco Cavalcanti da Silva…instagram.com
6Influenciador 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:

FrameworkJS BundleLCP típicoSEO Score
Astro (SSG)0 KB< 1s100
Next.js (SSR)~80 KB1.5-2.5s90-95
Gatsby~70 KB1.5-3s85-95
Create React App~200 KB3-5s50-70
WordPress~150 KB2-4s60-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:

DiretivaImpacto SEO
http2Multiplexação → menor TTFB → melhor LCP
brotli~20% menor que gzip → transfer size menor
expires 1y + immutableElimina re-downloads → melhor FCP
HSTSGoogle favorece HTTPS → ranking signal
Cache-Control: must-revalidateHTML sempre fresco → conteúdo atualizado indexado

Google Search Console: setup completo ✅

Passo 1 — Verificar propriedade do domínio

  1. Acessar Google Search Console
  2. Adicionar propriedade → Domíniorafaelroot.com
  3. Verificar via registro DNS TXT
  4. Adicionar o registro TXT no DNS do domínio

Status: ✅ Propriedade verificada com sucesso.

Passo 2 — Submeter o sitemap

Google Search Console — sitemap submetido com sucesso em 08/03/2026, 16 URLs descobertas

URL do Sitemap: https://rafaelroot.com/sitemap-index.xml

Status: ✅ Sitemap processado com sucesso em 08/03/2026.

MétricaValor
Sitemap indexsitemap-index.xmlsitemap-0.xml
Última leitura08/03/2026
StatusSuccess
URLs descobertas16
Vídeos descobertos0

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

  1. Acessar Google Analytics
  2. Admin → Criar propriedade → rafaelroot.com
  3. 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étricaFonteMeta
Impressões orgânicasSearch Console> 1.000/mês em 60 dias
Cliques orgânicosSearch Console> 100/mês em 60 dias
Posição médiaSearch ConsoleTop 10 para “rafael cavalcanti da silva”
CTR orgânicoSearch Console> 5%
SessõesGA4> 500/mês
Tempo médio na páginaGA4> 3 minutos (artigos)
Bounce rateGA4< 60%
Core Web VitalsPageSpeed InsightsTudo “Bom”

Estratégia de palavras-chave

Análise de dificuldade e volume

Palavra-chaveVolume estimadoDificuldadeEstratégia
rafael cavalcanti da silvaBaixo (~50/mês)MédiaTarget primário — nome completo, competição de homônimos
rafael cavalcantiMédio (~500/mês)AltaTarget secundário — muitos homônimos com presença forte
rafaelrootZero (brand new)BaixíssimaBranded keyword — criar do zero, dominar rapidamente
rafaelAltíssimoImpossívelNão targetar — usar apenas em long-tail
rootAltíssimoImpossívelNão targetar — usar apenas em contexto técnico
rafael cavalcanti desenvolvedorBaixoBaixaLong-tail — alta conversão
rafael cavalcanti segurançaBaixoBaixaLong-tail — nicho específico
rafael root engenharia reversaZeroZeroLong-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 Silva no 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:

  1. Person — nome completo, jobTitle, knowsAbout, sameAs, founder
  2. WebSite — nome do site, inLanguage multi-locale
  3. ProfilePage — página de perfil com mainEntity referenciando Person
  4. 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), aguardando PUBLIC_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.

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.”

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:

LocaleAntesDepois
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.

MitoPosição real do Google
Meta tag keywords”Google Search não usa a meta tag keywords.”
Keyword stuffingContra 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óriosSem 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:

  1. Qualidade do conteúdo — Conteúdo útil, confiável e people-first
  2. Links de outros sites“O Google encontra páginas principalmente através de links de outras páginas que já rastreou”
  3. Experiência da página — Core Web Vitals (LCP, FID, CLS)
  4. HTTPS — Sinal de ranking
  5. Mobile-friendliness — Mobile-First Indexing
  6. Dados estruturados — Não é sinal de ranking, mas habilita rich results (estrelas, carrosséis, FAQs)
  7. 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

PageSpeed Insights — Performance 90, Accessibility 96, Best Practices 100, SEO 100

CategoriaScoreStatus
Performance90/100🟢 Verde
Accessibility96/100🟢 Verde
Best Practices100/100🟢 Perfeito
SEO100/100🟢 Perfeito

Métricas Desktop

MétricaValorAvaliaçã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.

CategoriaDesktopMobileDelta
Performance9058-32 ⚠️
Accessibility96960
Best Practices1001000
SEO1001000

Métricas Mobile (baseline pré-correção)

MétricaDesktopMobileAvaliação
FCP1.3s6.7s🔴
LCP1.3s6.7s🔴
TBT0 ms0 ms🟢
CLS00🟢
Speed Index1.8s9.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:

  1. 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
  2. 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
  3. 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

PageSpeed diagnósticos — render blocking, cache, font display, unused CSS

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.

PageSpeed — Best Practices 100, SEO 100, 17 auditorias aprovadas

✅ 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étricaDesktop (antes)Desktop (após)Mobile (antes)Mobile (após)
Performance90905887 (+29)
Accessibility969696100 (+4)
FCP1.3s1.4s6.7s2.8s
LCP1.3s1.4s6.7s2.8s
Speed Index1.8s1.4s9.4s2.9s
TBT0ms0ms0ms0ms
CLS00.07600.113
Best Practices100100100100
SEO100100100100

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 --muted para #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)

DiaTarefaStatus
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)

SemanaTarefa
S3Criar/otimizar perfis: GitHub, LinkedIn, dev.to
S3Publicar artigo Parte 2 da série
S4Primeiro blog post otimizado para “rafael cavalcanti desenvolvedor”
S4Submeter site para diretórios dev (awesome lists, etc.)
S5Crosslinks entre artigos existentes
S5Criar página /about com schema.org Person completo
S6Guest post ou contribuição open source significativa
S6Checkpoint mês 1: verificar posições no Search Console

Fase 3 — Measurement & Optimization (Semanas 7-12)

SemanaTarefa
S7Analisar dados de Search Console (impressões, cliques, CTR)
S7A/B test de title tags nos artigos com menor CTR
S8Publicar artigo Parte 3 com resultados medidos
S9Otimizar pages com base em Core Web Vitals reais
S10Expandir FAQ Schema em artigos técnicos
S11Revisão de backlinks e oportunidades de link building
S12Relató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:

  1. Resultados das primeiras 2 semanas — quantas páginas o Google indexou? Alguma impressão já apareceu?
  2. Implementação real do GA4 — eventos customizados, dashboard de SEO, relatórios automáticos
  3. Link building técnico — como usar GitHub, Stack Overflow e contribuições open source para gerar autoridade
  4. Core Web Vitals na prática — resultado do PageSpeed Insights antes/depois das otimizações Nginx
  5. 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 + reduzido backdrop-filter no 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' + array alternateName + trailing slash na url
  • 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 WebPage para ProfilePage com mainEntity referenciando Person
  • Otimizado title links: removido rafaelroot.com redundante 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-face fallbacks com size-adjust para JetBrains Mono (108%) e Inter (100%) — previne layout shift do font-display: swap
  • Corrigido contraste em backgrounds glass: --muted bumped 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