Rebuilding agency website in 2026 – Lessons learned

· · 7 min read
eltexsoft.com software development

I run a software engineering studio. We've built apps for Fortune 500s, scaled marketplaces, shipped iOS apps that Apple featured at WWDC. Our own website was running on Nuxt + Contentful and had zero organic traffic.

Zero. For years.

Not because the site was bad. The design was fine. The content was fine. The problem was that Nuxt ships JavaScript, and AI search bots don't execute JavaScript. GPTBot, ClaudeBot, PerplexityBot — they fetch your HTML and move on. If your content lives inside a Vue component that renders client-side, it doesn't exist to them.

So we rebuilt the whole thing. Here's what happened.

Why Astro

I looked at Next.js first. Everyone does. But for a marketing site — services, case studies, blog — Next.js is overkill. You're shipping 85-180 KB of React runtime to render what is fundamentally static text and images. The App Router adds complexity we'd never use. And the CVE list in 2025 was not confidence-inspiring.

Astro ships zero JavaScript by default. A typical page is 5-15 KB of HTML and CSS. That's it. The framework is purpose-built for content-heavy sites: static site generation, Content Collections with Zod validation, built-in sitemap and RSS. State of JS 2025 ranked it #1 by developer satisfaction, 39 points ahead of Next.js.

Cloudflare acquired the Astro Technology Company in January 2026. Framework stays MIT-licensed. If anything, the backing makes the bet safer.

The deciding factor was simpler than all of that: AI bots can read our content now. Every word on every page is in the HTML. No hydration, no client-side rendering, no hoping that Googlebot's renderer feels like executing your JavaScript today.

Why Sanity (and why we left Contentful)

Contentful's April 2025 free-plan crackdown sealed it. 25 content type cap, 100K API calls. Their Premium tier starts at $300/month. We're a 35-50 person studio, not a media company. That math doesn't work.

Sanity's free tier gives us 20 seats, 2 datasets, 10K documents, 1M CDN requests. The Studio is React-based — same skills our team uses for client work. The official contentful-to-sanity CLI migrated our content in one command. And Sanity's structured content model is genuinely better for a site with services, case studies, industries, staffing models, and tech skill pages that all cross-reference each other.

We kept marketing pages (services, industries, tech stacks) as Markdown in the repo using Astro Content Collections. Blog posts live in Sanity where I can publish without touching code. Best of both worlds: engineers own the structured pages, I own the editorial.

The SEO play that actually worked

We started at Domain Rating 21 with zero organic traffic. Zero indexed keywords. The Nuxt site had been invisible to search for years.

The strategy came from running 75+ keywords through Ahrefs Keywords Explorer. What we found was extraordinary: 41 keywords with KD 0-3 representing 25,000+ monthly searches. Several high-CPC keywords ($15-30) had literally zero competition. "Generative AI development services" — 3,000 monthly searches, KD 1, $20 CPC. Nobody had a dedicated page for it.

We built 60 pages targeting those keywords. Services, industries, tech stacks, staffing models, blog posts — each one targeting a specific keyword cluster with FAQ sections and JSON-LD schema.

The content architecture follows what we learned from studying competitors. Vention (DR 71, 17.5K traffic) wins by having a dedicated page for every keyword variant. Their MVP page alone captures 5 keyword variants totaling 1,420 monthly traffic from one URL. That's the model. One page per intent, not five intents crammed onto one page.

Ahrefs MCP changed how I work

This is the part that surprised me most. Ahrefs has an MCP (Model Context Protocol) integration that connects directly to Claude. I can ask Claude to check our site audit, pull keyword data, analyze competitor backlinks — and it queries Ahrefs in real time without me opening the dashboard.

I'd run a site audit, Claude would parse the results, identify the issues, and generate the exact code fixes — meta descriptions that were too long, title tags over 65 characters, broken image references, redirect chains. We went from a health score of 73 to 99 in three sessions. The meta description audit alone covered 31 pages that needed trimming under 155 characters.

The workflow is: Ahrefs finds the problem through MCP → Claude generates the fix → Claude Code applies it to the codebase → push to GitHub → Coolify auto-deploys. The entire cycle from "Ahrefs flagged this" to "fix is live in production" takes minutes, not hours.

Sanity MCP for content operations

Same pattern with Sanity's MCP integration. When we discovered that pasting blog content from Claude's chat interface into Sanity Studio was silently converting relative links (/blog/...) to https://claude.ai/blog/... — 146 links across 13 posts — we wrote a Node script that patched all of them through Sanity's API in one run.

But the MCP connection meant I could verify the fix instantly. "Query all posts for any href containing claude.ai." Zero results. Done. No need to open the Studio, no manual spot-checking.

We also use Sanity MCP to create blog posts, patch SEO fields, publish batches of documents, and query content for audits. I published 36 blog posts through a combination of MCP creation and Studio editing. All with full body content, proper author references, FAQ sections, and internal cross-links.

Every website needs Terms of Service, Privacy Policy, and Cookie Policy. Every founder hates writing them. Most people use a generator that produces generic boilerplate or pay a lawyer $3-5K.

I found Raj Jha's mill-deterrent-pack — an open-source legal template specifically designed to deter mass-arbitration mills and serial litigants. Three tiers of protection:

  • Tier 1: Pre-dispute notice gate with a 12-item disclosure requirement, class-action waiver, AAA arbitration, specific governing law and venue.
  • Tier 2: 60-day informal resolution period, two mandatory principal-level meetings, fee-arrangement disclosure, 24-month prior-claims history.
  • Tier 3: Pre-merits good-faith review by the arbitrator, claimant-pays cost allocation, bad-faith dismissal with fee-shifting.

It's serious legal engineering. The Terms include an ML training prohibition, a $100 liability cap, and indemnification. The Privacy Policy covers GDPR (with CNPD as lead authority since we're HQ'd in Lisbon), CCPA/CPRA, explicit tracking disclosures, and a "what we don't use" section. The Cookie Policy documents every cookie by name, purpose, and duration.

We adapted it for our entity (Icemint LLC d/b/a EltexSoft, Wyoming LLC) and our specific stack (GA4, Cloudflare, Google Fonts). Took an afternoon. The result is more thorough than what most $5K legal reviews produce, and it's open source.

Ditching GA4 for Cloudflare Web Analytics

While we were at it, we set up Cloudflare Web Analytics alongside GA4. Server-side, no client JavaScript, no cookies. GDPR-friendly by design — there's nothing to consent to because there's no tracking pixel in the browser.

GA4 is still running for now because Search Console integration requires it. But for actual traffic understanding — which pages get visited, where people come from, what devices they use — Cloudflare's server-side analytics gives cleaner data without the privacy overhead.

The Cookie Policy got simpler too. Two Cloudflare operational cookies (__cf_bm for bot management, _cfuvid for rate limiting) plus GA4's _ga cookies. That's the complete list. No pixels. No session replay. No heat maps. The privacy policy says what we don't use, and the list of things we don't use is longer than the list of things we do.

Infrastructure: $8.60/month for everything

The whole stack runs on a Hetzner Cloud VPS (CPX21, $8.60/month). Coolify manages the deployment — Nixpacks builds the Astro site, outputs static HTML to an nginx container. Cloudflare sits in front for CDN, WAF, and DDoS protection on the free tier. Sanity's free tier handles the CMS. GitHub Actions is free. Domain was already paid for.

Total marginal cost: $8.60/month. Down from whatever Contentful was going to start charging us.

SSL is Cloudflare Flexible — they terminate TLS at the edge and send HTTP to the origin. One less thing to manage on the VPS.

What I'd do differently

Start with the SEO strategy, not the design. We built pages and then optimized them for keywords. It should be the other way around. The keyword research should dictate the page architecture. Which pages to build, what to call them, how to structure the URLs — all of that should come from the data.

Verify your content proofread by LLMs before it reaches the CMS. The relative-to-absolute URL resolution bug cost us a full debugging session and a script to fix 146 links. Write content in Markdown, paste Markdown.

Set up Ahrefs site audit on day one. We caught 57 errors on the first crawl that were trivially fixable but had been silently hurting us. Broken favicon references, missing meta descriptions, redirect chains — all invisible unless you crawl.

Nginx redirects need absolute HTTPS URLs when Cloudflare Flexible SSL is in front. Relative redirects resolve to http:// at the origin because Cloudflare connects to your server over HTTP. Every rewrite ... permanent needs the full https://yourdomain.com/path/ target. We had 25+ redirects silently double-hopping (301 → http → https) before catching this.

Disk space on small VPS instances fills up fast with Docker. Coolify's Nixpacks builds accumulate overlay layers. We hit 100% disk on a 40GB instance after 16 deployments. Weekly docker system prune -a --volumes -f via cron is mandatory.

The numbers so far

  • 116 static HTML pages
  • 36 blog posts with full body content
  • 13 case studies
  • 10 industry pages
  • 9 tech stack pages
  • 22 course chapters (web edition of my book, "42: The AI Builder's Stack")
  • Lighthouse mobile: 95+
  • Ahrefs health score: 99/100
  • Page weight: 5-15 KB HTML + CSS per page
  • Build time: 2.66 seconds for 116 pages
  • Deploy: push to GitHub → Coolify auto-build → live in under 3 minutes

We're still at DR 21. The content has been live for less than two weeks. Position data starts appearing at 60-90 days. But the foundation is there: 41 keywords with KD 0-3 targeting 25,000+ monthly searches, pure HTML that every bot on the internet can read, and a publishing workflow where I write in Sanity and it's live in minutes.

Ask me again in 90 days.


The stack: Astro + Sanity + Hetzner + Coolify + Cloudflare. Legal templates: mill-deterrent-pack by Raj Jha. SEO: Ahrefs. Code: Claude Code. The site: eltexsoft.com