Astro sites are static — cached forever at the edge for speed. But the moment you want to A/B test hero copy or pricing display, you hit a wall: rebuild the whole site to show variant B? Cache-buster nightmare. The answer: assign variants at the edge with a Netlify Edge Function, store ramp control in GrowthBook, and serve different HTML without rebuilding. Test faster than your marketing team can write new copy.
The Static Site A/B Testing Paradox
Static sites are blazingly fast because they live on a CDN and never talk to a server. Variants require sending different content to different users. That sounds like you need dynamic SSR, which kills caching, kills speed, kills the entire point of going static. For years, the workaround was "cache-bust the whole thing" — blow away edge cache, rebuild, redeploy. Slow. Risky. Nobody A/B tested on static sites.
Netlify Edge Functions change the game. They sit between the CDN and your origin, invisible to the browser. An Edge Function can read a request (which user is this?), assign a variant (cookie or ID-based), intercept the cached HTML, rewrite parts of the response, and send it back — all in <100ms. The static site stays static. The cache stays warm. The test runs live.
Architecture: The Three Layers
Layer 1: Edge Function (variant assignment). Netlify Edge Functions intercept every request to /. If the user has no variant cookie, assign one (50/50 random, or biased by targeting rules). If they have one, reuse it. Store the choice in a cookie so they see the same variant forever. This is ~20 lines of code and happens in <5ms.
{`export default async (request) => {
const url = new URL(request.url);
let response = await context.next();
// Check for existing variant
let variant = getCookie(request, 'test_variant');
if (!variant) {
variant = Math.random() > 0.5 ? 'a' : 'b';
response.headers.set('Set-Cookie', \`test_variant=\${variant}; path=/\`);
}
// Rewrite HTML based on variant
if (variant === 'b') {
return rewriteHtmlForVariant(response, 'b');
}
return response;
};`}
Layer 2: HTML Rewriting (content substitution). Once you know the variant, rewrite the cached HTML. Swap hero headline text, change a button label, hide a pricing tier, inject tracking pixels. Use a streaming HTML transform so you don't load the entire response into memory. Target elements by CSS selector or data attribute — set data-test="hero-headline" on elements you want to swap, then rewrite at the edge.
Layer 3: GrowthBook (ramp control and analytics). Edge Functions are "fire and forget" — they assign variants but don't know if 5% or 50% of traffic should see B. GrowthBook is the control layer. You define experiments (which URL, which variant, what percentage of traffic, targeting rules, hypothesis). Update the ramp from 5% to 25% to 100% without touching code. GrowthBook also tracks conversions: connect your analytics (Plausible, Mixpanel, custom events), and it calculates whether variant B wins or loses.
The Money Pattern
In your Astro build, wrap variant-target elements with a data attribute:
{`
Traditional: "Ship Faster With Velocity"
But GrowthBook will swap this for variant B
`}
In your Edge Function, after assigning the variant, transform the HTML:
{`async function rewriteHtmlForVariant(response, variant) {
let html = await response.text();
if (variant === 'b') {
// Use regex or a DOM parser to find and replace
html = html.replace(
/data-test="hero-headline">.*?<\/h1>/s,
'data-test="hero-headline">Ship 10x Faster With Velocity<\/h1>'
);
html = html.replace(
/data-test="cta-button">.*?<\/button>/s,
'data-test="cta-button">Get Started Now<\/button>'
);
}
return new Response(html, {
status: response.status,
headers: response.headers,
});
}`}
Build deploys once. GrowthBook controls which users see which variant. Analytics flow back into GrowthBook. No rebuilds. No cache-busting. Just experimentation.
Real-World Use Cases
Hero copy testing. Velocity X tests three headline variations on the home page. One is research-backed, one is benefit-focused, one is urgency-driven. All three variants are built once; Edge Functions route users. After 2 weeks, GrowthBook shows variant C lifts signups by 18%. Ship it to 100% without a deploy.
Pricing table reordering. Does showing the Pro tier first outsell showing Starter first? Swap the HTML at the edge. No rebuild, no database change, just reordering a table in the response stream. GrowthBook tracks which tier converts better per variant.
CTA button text. "Start Free Trial" vs. "Get Started Now" vs. "Launch My Dashboard". Test all three without a rebuild. Netlify Edge runs the rewrite in <5ms; the user never knows.
Six FAQs
Does this hurt SEO?
No. Google fetches the page as variant A (or whichever variant the crawler sees first). As long as the variants are semantically equivalent (same H1 structure, same keyword density, same internal links), you're fine. If variant B has different links or structure, you risk a soft crawl penalty. Keep variants content-swaps, not structure-swaps.
What if the Edge Function is slower than the CDN cache?
Edge Functions run at Netlify's 200+ edge locations — latency is typically <5ms. If you're doing expensive work (database queries, third-party API calls), that kills the whole idea. Keep Edge Function work fast: regex rewrites, cookie checks, simple conditional logic. Move heavy lifting off the critical path.
Can I A/B test logged-in dashboard pages?
Yes, but variants must be user-id-based, not random. Cookies work for marketing sites; for app dashboards, assign variants based on user ID hashed to A or B. Store the decision in a lookup table (Redis or Supabase) so the variant is stable across devices.
How do I measure statistical significance?
That's GrowthBook's job. Connect your analytics platform (or custom event tracking), tell GrowthBook the sample size and duration, and it calculates p-values and confidence intervals. Most A/B tests need 500–2,000 conversions per variant to reach 95% confidence. Small traffic sites need longer test windows.
What if I want to test a whole new page design, not just text swaps?
Edge Functions rewrite HTML, not replace entire pages. If variant B is a completely different layout, you'd need to build both layouts into the static site (as separate routes) and route users with a 301 or Edge redirect. That's cleaner than trying to swap 80% of the HTML at the edge.
Can I run multiple experiments at once?
Yes. Assign each experiment its own cookie (e.g., test_pricing_variant, test_hero_variant). Each Edge Function checks its cookie and applies its rewrites independently. With careful design, you can stack 2–3 experiments. More than that and you risk confounding variables — one experiment interferes with another.
The Bottom Line
A/B testing on static sites used to mean choosing between speed (static) and experimentation (SSR). Edge Functions + GrowthBook let you have both. Assign variants at the edge in <5ms, rewrite cached HTML per user, ramp experiments from 5% to 100% without code. For Velocity X sites, this is table-stakes: test hero copy, pricing, button text, and ship the winner without a deploy. If you're building a marketing site and NOT running tests, you're leaving 20–40% conversion upside on the table.
Ready to start? See the Velocity X pricing page — it runs this pattern live. For deeper patterns on conversion optimization, check CRO Conversion Optimization Patterns.