I Ranked on Google's First Page in 6 Weeks β Here's Every SEO Tactic I Used (Part 2)
<p><em>This is Part 2 of my SEO case study. <a href="https://dev.to/rafaelroot/seo-case-study-from-zero-to-google-in-12-weeks-part-1">Part 1 covered the technical foundation</a>: 9 fixes, PageSpeed 58β87, and the Astro stack setup.</em></p> <p>In Part 1, I documented the baseline of <a href="https://rafaelroot.com" rel="noopener noreferrer">rafaelroot.com</a>: zero indexation, zero impressions, zero clicks. <strong>Astro SSG</strong>, strict technical SEO, mobile PageSpeed from 58 to 87.</p> <p>Now for the growth phase. This article covers weeks 3 through 6: building authority, deploying to production, and reaching <strong>position 3 on Google's first page</strong>.</p> <h2> π TL;DR β Weeks 3 to 6 </h2> <div class="table-wrapper-paragraph"><table> <thead> <tr> <th>Metric</th> <th>Start</t
This is Part 2 of my SEO case study. Part 1 covered the technical foundation: 9 fixes, PageSpeed 58β87, and the Astro stack setup.
In Part 1, I documented the baseline of rafaelroot.com: zero indexation, zero impressions, zero clicks. Astro SSG, strict technical SEO, mobile PageSpeed from 58 to 87.
Now for the growth phase. This article covers weeks 3 through 6: building authority, deploying to production, and reaching position 3 on Google's first page.
π TL;DR β Weeks 3 to 6
Metric Start Week 6
Indexed pages 0/16
16/16 (100%)
Primary query position 100+
#3 (page 1)
Weekly impressions 0 847
CTR (main query) β 12.4%
Backlinks 0
7 high-quality
Lighthouse scores 87/99/99/99 100/100/100/100
β‘ 10-Day Indexation Checkpoint
Ten days after submitting the sitemap, Google indexed all 16 URLs. Complete coverage.
π― CHECKPOINT β Week 2 (03/14/2026)
Indexed Pages: 16/16
Impressions: 23 | Clicks: 2 | Position: 47.3
Why so fast?
-
Clean sitemap β 16 URLs, zero 404s, no redirect chains
-
Semantic HTML β Hierarchical headings + valid JSON-LD schemas
-
~50ms TTFB β Googlebot parses static files instantly
ποΈ Production Deploy: Nginx Tuned for SEO
Brotli + Gzip compression
Brotli compresses ~8-9% better than gzip for HTML. Automatic gzip fallback for older clients:
brotli on; brotli_comp_level 6; brotli_static on; brotli_min_length 256; brotli_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml font/woff2;brotli on; brotli_comp_level 6; brotli_static on; brotli_min_length 256; brotli_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml font/woff2;gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 4;`
Enter fullscreen mode
Exit fullscreen mode
Measured results:
Encoding Size Reduction
Uncompressed 18,408 bytes β
Gzip 5,372 bytes -70.8%
Brotli 4,897 bytes -73.4%
π‘ 475 bytes less than gzip per request. On a 3G connection, that's the difference between passing or failing the LCP Core Web Vital.
HTTPS + HTTP/2 + Security headers
Full server block with Let's Encrypt SSL, HSTS preload, CSP, and all 6 security headers. Two permanent 301 redirects: HTTPβHTTPS and wwwβnon-www (prevents duplicate content).
Layered cache strategy
Type Cache Why
CSS, JS, fonts, images
1 year, immutable
Hash in filename = never changes
HTML
1 day, must-revalidate
Content may update
XML (sitemap) 1 hour Crawlers need fresh data
Returning visitors download zero assets β only HTML is revalidated.
Production TTFB
Homepage: 94ms | Blog post: 73ms | Protocol: HTTP/2 | Encoding: Brotli
π GA4 Dashboard for SEO
Rankings mean nothing if users bounce. I configured GA4 with custom events:
// Scroll depth tracking β zero dependencies const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { gtag('event', 'scroll_depth', { section: entry.target.id, percent: entry.target.dataset.depth }); } }); }, { threshold: 0.5 });// Scroll depth tracking β zero dependencies const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { gtag('event', 'scroll_depth', { section: entry.target.id, percent: entry.target.dataset.depth }); } }); }, { threshold: 0.5 });document.querySelectorAll('section[data-depth]').forEach(s => observer.observe(s));`
Enter fullscreen mode
Exit fullscreen mode
The SEO dashboard correlates Search Console (impressions, CTR, position) with GA4 (scroll depth, time-on-page, bounce rate).
π Link Building: Authority Footholds
No guest post spam. Instead, I built contextual presence on platforms Google already trusts:
Platform Type DA Context
1
dev.to
Original article
61
Full case study with canonical_url
2 GitHub Profile + README 96 Bio + pinned repo with contextual link
3 Stack Overflow Profile 82 Website field
4 LinkedIn Profile + article 98 Featured section + native post
5 npm Published package 75 Homepage field
6 Twitter/X Profile + thread 94 Bio and tweet with link
π₯ DEV.TO canonical hack: Using canonical_url in dev.to frontmatter transfers DA 61 authority to your blog without triggering duplicate content penalties. This article does exactly that.
The key insight: each link has context β it's not a naked URL in an empty bio.
π Multilingual Content: The i18n Multiplier
5 languages (pt-BR, pt-PT, en, es, ru) β each article natively written, not machine-translated.
Why this matters:
-
Google detects auto-translated content as low quality
-
Each version ranks independently for queries in that language
-
hreflang tags mathematically link all versions
`
`
Enter fullscreen mode
Exit fullscreen mode
π Results: 6 Weeks of Data
Week Impressions Clicks CTR Avg. Position
1 0 0 β β
2 23 2 8.7% 47.3
3 89 7 7.9% 28.1
4 234 21 9.0% 14.7
5 512 48 9.4% 7.2
6 847 105 12.4% 3.8
Top queries
Query Position CTR
rafael cavalcanti da silva #3 14.2%
rafaelroot #1 28.6%
rafael cavalcanti developer #5 8.9%
SERP comparison β "rafael cavalcanti da silva"
Position Baseline Week 6
1 jusbrasil.com.br jusbrasil.com.br
2 br.linkedin.com br.linkedin.com
3 g1.globo.com
rafaelroot.com β
Separating technical vs. content impact
Technical fixes (weeks 1-2): complete indexation in 10 days, PageSpeed 87, zero Search Console errors.
Content + authority (weeks 3-6): impressions 0β847, ranking 100+β#3, CTR above market average.
The technical foundation let Google index quickly. But it was content and backlinks that moved the ranking.
π‘ Lesson Learned: Don't Shorten Your Brand Name
I tested using the shorter "Rafael Cavalcanti" in titles. The logic: it's a substring, so coverage should be additive.
It wasn't. Within days, I lost ranking for the full name query. Google re-evaluated the page's primary entity and the diluted signal hurt more than the broader match helped.
Fix: Reverted all , meta descriptions, og:site_name back to "Rafael Cavalcanti da Silva". The short form stays only in alternateName in structured data.
Takeaway: For personal brand SEO, consistency > coverage. If you rank for "firstname lastname suffix," don't dilute it by removing the suffix.
π Post-Launch Optimizations (Updates 2-4)
These updates happened after the initial 6-week period and are fully documented in the canonical article.
Lighthouse: 100/100/100/100
Three targeted fixes to reach perfect scores:
Issue Before After Fix
LCP render delay
920ms
0
Replaced JS font injection with CSS media="print" onload pattern
Missing og:image:alt
SEO 99
100
Added dynamic og:image:alt + twitter:image:alt on all pages
CSS critical chain 922ms 0
build.inlineStylesheets: 'always' β zero external CSS
Forced reflow
39ms
0
Batched getBoundingClientRect() reads before DOM writes
Sitemap: from basic to fully optimized
Feature Before After
`` β β All 35 URLs
`` β β Per-page-type
`` β β Hierarchy (1.0β0.5)
Blog hreflang β β Cross-language links (5 locales)
Redirect URLs Leaking β Filtered β
`` β β 15 image entries
The trickiest part: @astrojs/sitemap doesn't support blog posts with different slugs per locale. Solution: a blogTranslations map in serialize() that links all 5 versions:
const blogTranslations = { 'seo-case-study-part-1': { 'pt-BR': '/blog/case-study-seo-do-zero-ao-google-parte-1/', 'en': '/en/blog/seo-case-study-google-ranking-part-1/', 'es': '/es/blog/caso-estudio-seo-de-cero-a-google-parte-1/', } };const blogTranslations = { 'seo-case-study-part-1': { 'pt-BR': '/blog/case-study-seo-do-zero-ao-google-parte-1/', 'en': '/en/blog/seo-case-study-google-ranking-part-1/', 'es': '/es/blog/caso-estudio-seo-de-cero-a-google-parte-1/', } };Enter fullscreen mode
Exit fullscreen mode
Image SEO + sitemap indexing
-
Renamed image.png β rafael-cavalcanti-da-silva-fullstack-developer.png
-
Created a custom Astro integration (sitemapImageInjector) that injects tags into sitemap XML post-build
-
15 image entries enabling Google Image Search discovery
Custom 404 page + i18n coverage
Multilingual 404 page with contextual navigation. Added 55+ translation keys and the missing Russian "TrajetΓ³ria" page.
π Full Checklist β Weeks 3-6
-
β 16/16 URLs indexed (100% coverage)
-
β GA4 with scroll depth + time-on-page tracking
-
β 7 high-quality backlinks built
-
β dev.to article with canonical link
-
β 5 languages, natively written
-
β hreflang validated across all versions
-
β Nginx: Brotli + HTTP/2 + HSTS + 6 security headers
-
β TTFB: 73-94ms in production
-
β Lighthouse: 100/100/100/100
-
β Sitemap: 35 URLs + priorities + image indexing
-
β "rafael cavalcanti da silva" β position 3
-
β "rafaelroot" β position 1
-
β CTR 12.4% (above market average)
What's Next (Part 3)
-
A/B titles and meta descriptions for CTR optimization
-
Real Core Web Vitals field data (CrUX) vs. lab (Lighthouse)
-
Cannibalization analysis across multilingual versions
-
Final goal: position 1
Community Feedback
The Part 1 article on dev.to generated valuable feedback:
@apogeewatcher suggested separating results into categories (indexation, ranking, CTR, on-page performance) instead of attributing everything to a single change. That suggestion directly influenced the "Separating technical vs. content impact" section above.
@apex_stack shared experience running a 100k+ page Astro site across 12 languages, validating the translationKey pattern and warning about crawl budget as a "silent variable."
@kritika_barod confirmed the pattern: Google crawled thousands of pages but didn't index them until authority signals improved.
Full technical details with Nginx configs, schema code, and all 4 updates: rafaelroot.com/en/blog/seo-case-study-google-ranking-part-2/
Found this useful? Drop a π¦ and follow for Part 3 β or check out rafaelroot.com to see everything in action.
DEV Community
https://dev.to/rafaelroot/i-ranked-on-googles-first-page-in-6-weeks-heres-every-seo-tactic-i-used-part-2-f6fSign in to highlight and annotate this article

Conversation starters
Daily AI Digest
Get the top 5 AI stories delivered to your inbox every morning.
More about
launchversionupdateExclusive | The Sudden Fall of OpenAIβs Most Hyped Product Since ChatGPT - WSJ
<a href="https://news.google.com/rss/articles/CBMiogNBVV95cUxNVXJPcUFQbnkwYXJRQzNDVHFhTE5ZdUZrLUNPUjB3b05TV2tPTG9rdTUwV0J2WncxdE1KR2Z1c0x4aXFQdUlscGtZTDRwRG5yTkpmVzlHckxPaUdZcUZSZk1TOGdRSUdIVVBURlM2dG8xZHNKS2JqcXhJSmw4UllDZS1hUHd3bnlxWDhUa0ZFVzZZTXdTSk93eS1xSzllYkJ1WWxHTVNjZ3ROYWdnWTdxRmRyU0x3NTk0bnpQdnFDQVB6aXh2U01sX3BobHNFZGlhRFpVbXVnRkNkQlFBbDk5Q3E4dzRDUE9NT01HQm1NMUlMWFFaa1U1djhWdm15X1FZUGhJMXNPX05aX2w2UTI4MjdoNXgweHdzWTRkYkdSeUxEdnZPb09nZDFXX1pmZ2NGckZmVC1UV3YxSjhFamRXTzlRb3ZlWElBb3Vna0FPR09DRm5kWjFIVzhQRE1RVUtKS3hVWkFDcHZiU0x3SjY0OTRVRS0yR2VlRWtYMG8wb2V2YW1wdTlic2hwZ2wzTW9Uc0FqWGJsU3NGQ1pYdkJFeW1B?oc=5" target="_blank">Exclusive | The Sudden Fall of OpenAIβs Most Hyped Product Since ChatGPT</a> <font color="#6f6f6f">WSJ</font>
Exclusive | The Sudden Fall of OpenAIβs Most Hyped Product Since ChatGPT - WSJ
<a href="https://news.google.com/rss/articles/CBMiogNBVV95cUxPb1N4U0FYNGxVQlJ1eHNBbHE2d2p2NlBwS3RuTUFCeHZXY3B5U0NmZlRIVm9TVC10NGpSNlROQkQ2SENlRkhJRzFXWWdYUWFEdkh2Q081NnFCcXB4cmtkaEF1UjV1b1ZsOXBWdi0yV0tfTkM1TUZvV1NmeUVmc1V4N0luZ1RVT0hfYU5WMFo0MTBadVdsZDBNQkQ0cE1DVFNPOUc0TDAyaVU3STJXSDBCem1Pb1VfeHJqZzd5UVlCZVlDUFNQTjlVNU1pcjJZYnVnbWhORzRBQl8wQXZEWmEtOW9fUjFsX2t5ZkNINmIwR1dpcE5WYWtBdFNjLTNPUkltOU83ZS1qU1lod0JVdlpxeW1HNk5SMGRPa1IxMXp3eTFlbTdIckhhYjMyQ0x4VmpPWlI4VDR5M0NTZGVFMGxUQnBBVTBvUlB4UlNaZ3dDaEg3R0VIVGdRZDNCZGgtcFVDcEttbVJxSzkxMVQyWVo0Rk9vcTFKWWpUTEJsTloxVXdhX1FzdkE4M0ozZXpqZ0tYT0pZLTdCMnBlMzU5U05XUDJB?oc=5" target="_blank">Exclusive | The Sudden Fall of OpenAIβs Most Hyped Product Since ChatGPT</a> <font color="#6f6f6f">WSJ</font>

Webhook Best Practices: Retry Logic, Idempotency, and Error Handling
<h1> Webhook Best Practices: Retry Logic, Idempotency, and Error Handling </h1> <p>Most webhook integrations fail silently. A handler returns 500, the provider retries a few times, then stops. Your system never processed the event and no one knows.</p> <p>Webhooks are not guaranteed delivery by default. How reliably your integration works depends almost entirely on how you write the receiver. This guide covers the patterns that make webhook handlers production-grade: proper retry handling, idempotency, error response codes, and queue-based processing.</p> <h2> Understand the Delivery Model </h2> <p>Before building handlers, understand what you are dealing with:</p> <ul> <li>Providers send webhook events as HTTP POST requests</li> <li>They expect a 2xx response within a timeout (typically 5
Knowledge Map
Connected Articles β Knowledge Graph
This article is connected to other articles through shared AI topics and tags.
More in Products
UCL appoints Google DeepMind fellow to advance multilingual AI research - EdTech Innovation Hub
<a href="https://news.google.com/rss/articles/CBMisgFBVV95cUxQR3RqV1doQ2lCUFBMLTdSMjU1NEhDdHQ2dEhsbElyd1BLc0J6cE80VTBMYWxHdmk1a2h0NEJzckF6ZU5wN1dEUDR5aGJra1dGZUNEdExRMnFmWm1mUzFkU0tCZkpkdmNTME1JS0ZxSzlsVVNLQjFacEp1NXdJMlJfM3BQSTRlZENOWDlzQnJ1aVJ0amdZRndGYXpvN3pjaDdPMDJjcV9hdmhPTHJ5MkpEenBn?oc=5" target="_blank">UCL appoints Google DeepMind fellow to advance multilingual AI research</a> <font color="#6f6f6f">EdTech Innovation Hub</font>

Webhook Best Practices: Retry Logic, Idempotency, and Error Handling
<h1> Webhook Best Practices: Retry Logic, Idempotency, and Error Handling </h1> <p>Most webhook integrations fail silently. A handler returns 500, the provider retries a few times, then stops. Your system never processed the event and no one knows.</p> <p>Webhooks are not guaranteed delivery by default. How reliably your integration works depends almost entirely on how you write the receiver. This guide covers the patterns that make webhook handlers production-grade: proper retry handling, idempotency, error response codes, and queue-based processing.</p> <h2> Understand the Delivery Model </h2> <p>Before building handlers, understand what you are dealing with:</p> <ul> <li>Providers send webhook events as HTTP POST requests</li> <li>They expect a 2xx response within a timeout (typically 5

Building a scoring engine with pure TypeScript functions (no ML, no backend)
<p>We needed to score e-commerce products across multiple dimensions: quality, profitability, market conditions, and risk.</p> <p>The constraints:</p> <ul> <li>Scores must update in real time</li> <li>Must run entirely in the browser (Chrome extension)</li> <li>Must be explainable (not a black box)</li> </ul> <p>We almost built an ML pipeline β training data, model serving, APIs, everything.</p> <p>Then we asked a simple question:</p> <p><strong>Do we actually need machine learning for this?</strong></p> <p>The answer was no.</p> <p>We ended up building several scoring engines in pure TypeScript.<br> Each one is a single function, under 100 lines, zero dependencies, and runs in under a millisecond.</p> <h2> What "pure function" means here </h2> <p>Each scoring engine follows 3 rules:</p> <

π I Vibecoded an AI Interview Simulator in 1 Hour using Gemini + Groq
<h1> π Skilla β Your AI Interview Simulator </h1> <h2> π‘ Inspiration </h2> <p>Interviews can be intimidating, especially without proper practice or feedback. Many students and job seekers donβt have access to real interview environments where they can build confidence and improve their answers.</p> <p>Thatβs why I built <strong>Skilla</strong> β an AI-powered interview simulator that helps users practice smarter, gain confidence, and improve their communication skills in a realistic way.</p> <h2> πLive URL: <strong><a href="https://skilla-ai.streamlit.app" rel="noopener noreferrer">https://skilla-ai.streamlit.app</a></strong> </h2> <h2> π€ What It Does </h2> <p><strong>Skilla</strong> is a smart AI interview coach that:</p> <ul> <li>π€ Simulates real interview scenarios </li> <li>π§ Ask

Discussion
Sign in to join the discussion
No comments yet β be the first to share your thoughts!