We rebuilt our clinic’s website on Bubble earlier this year. .
Then our organic traffic fell off a cliff.
If you’re a Bubble developer who’s hit the same wall, this post is for you. Here’s exactly what was wrong, why it’s easy to miss, and how we fixed it without Bubble’s dedicated-server plan.
The symptom: “no outgoing links,” “orphan page,” on hundreds of pages at once, ahrefs shows bad health across the whole site.
Running an Ahrefs Site Audit, we saw the same handful of issues repeated across roughly 360 pages simultaneously:
- H1 tag missing or empty
- Low word count
- Page has no outgoing links
- Orphan page (no incoming internal links)
The confusing part: these were all different checks, on completely different topics (headings, word count, outbound links, inbound links) — yet they were failing on the exact same set of pages, every time. That pattern was the clue, though it took us a while to read it correctly.
We knew our internal linking was solid. We could see it, click through it, use it every day. So why did every crawler we tested — Ahrefs, and eventually confirmed via raw curl requests spoofing Googlebot and GPTBot — see a page with no headline, almost no text, and no links at all?
The actual cause: Bubble renders everything client-side
Bubble builds pages using JavaScript that runs in the browser. A human visitor’s browser executes that JavaScript and paints the full page. A crawler that doesn’t execute JavaScript — which includes most non-Google crawlers, and even Google itself on its first, faster crawl pass — receives only the raw, pre-JavaScript HTML: an empty shell of <script> tags with no visible content.
We confirmed this directly with a simple test anyone can run:
curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://yoursite.com/
If your actual page content — headlines, body text, links — doesn’t appear anywhere in that output, you have this exact problem.
A second, compounding issue: our navigation used Bubble’s Button element with “Navigate to page” workflows, rather than the actual Link element. Buttons with workflow-based navigation never produce a real <a href="..."> tag in the HTML — so even once content is visible, crawlers still can’t follow those links to discover other pages.
Dead end #1: Prerender.io + Cloudflare Worker
This is the standard, well-documented fix for this exact problem — a service that renders your page once, caches the result, and serves that cached HTML to bots while real visitors get the full interactive site. The catch: it requires proxying your domain through Cloudflare, and doing so makes Cloudflare inject IPv6 (AAAA) records. Bubble’s own SSL certificate validator flags any AAAA record as unsafe and throws a “bad DNS records” error — an unsupported configuration on anything short of Bubble’s dedicated-server tier. We spent a genuinely long, methodical session confirming this conflict from every angle — DNS, Workers Routes, live log streams, curl tests with multiple bot user-agents — before accepting it wasn’t going to work cleanly on a standard plan.
Dead end #2: a third-party “Bubble SEO” tool
We tried a new, third-party service (FastPages.live) promising to solve this exact problem via a similar Cloudflare-based approach, with genuinely impressive PageSpeed and Lighthouse improvements after connecting it. But a raw curl test — bypassing the browser entirely — showed it was never actually intercepting bot traffic; the visible speed gains were real, but came from CDN caching, not from solving the crawlability problem at all. Lighthouse and PageSpeed both execute JavaScript when they test, so neither tool ever revealed the gap. If you’re evaluating any similar tool, don’t trust the dashboard or the Lighthouse score — verify with curl and a spoofed bot user-agent directly.
What finally worked: build the snapshot pipeline ourselves, and route humans to the real thing
Once we accepted that any live, request-time interception in front of Bubble’s own domain was a losing fight, the actual fix became two separate, much simpler pieces:
1. Move the real Bubble app to its own subdomain. app.yoursite.com stays connected to Bubble exactly the normal way — plain DNS, no proxy, nothing for Bubble’s SSL validator to object to, ever.
2. Free the root domain up entirely, and put a static snapshot pipeline there instead.
- A Node.js + Playwright script reads the site’s sitemap, visits every real URL with a headless browser, waits for it to fully render, strips out all the JavaScript (bots never execute it anyway, so it’s dead weight), and saves the resulting clean HTML.
- That script runs automatically, on a schedule, for free, via GitHub Actions — no server to maintain, no minute limits on a public repo.
- The output gets committed to a GitHub repo connected to Cloudflare Pages, which auto-deploys the moment new content lands.
- The root domain’s DNS points straight at that Cloudflare Pages project.
3. A Cloudflare Worker sits in front of the root domain and makes one decision per request: bot user-agent → serve the static snapshot. Anything else → quietly proxy the request through to the real, live app. subdomain and return it under the main domain’s URL — so a human visitor never sees a subdomain, never sees a stripped-down page, and gets the exact same interactive site as always.
The result: crawlers get instant, fully-rendered HTML with zero JavaScript wait. Real visitors get the full, unmodified, interactive Bubble app. Nobody has to choose between the two.
Total cost: $0 beyond what we already had. No subscription, no proxy fighting Bubble’s infrastructure, nothing that can quietly stop working on someone else’s timeline.
The honest tradeoff
The static snapshots aren’t live. If a treatment’s price or copy changes in Bubble, it won’t reflect in what crawlers see until the next scheduled scrape (ours runs nightly). For a public content site — treatments, blog, gallery, FAQs — that’s a completely acceptable trade for guaranteed crawlability. It would be the wrong call for something like a logged-in dashboard where content is different for every user in real time — but that’s exactly the kind of page that never needed to be crawlable in the first place, and in our case, we simply left it on the Bubble subdomain, untouched.
If you’re hitting this right now
- Confirm it first —
curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://yoursite.com/and look for real visible text in the raw output. Empty shell of<script>tags = you have this problem. - Check your navigation — search the same raw output for
<a href. If your main nav doesn’t show up as real links, you likely have Bubble’s Button+workflow issue too, on top of the rendering problem. - If you’re not on a dedicated Bubble plan, go in expecting that Cloudflare-proxy-based fixes (Prerender.io included) will hit the same AAAA/SSL wall we did — it’s not a bug on your end, it’s structural.
- Verify any third-party tool’s claims with
curldirectly, not with Lighthouse or PageSpeed — those only ever show you the JS-rendered version. - The self-built snapshot-plus-Worker pipeline is more setup than a one-click SaaS tool, but it’s free, fully understood, and doesn’t depend on anyone else’s uptime or business model.
Happy to answer questions in the comments if anyone’s mid-fight with this exact problem right now — we know exactly how isolating it feels when your site looks perfect to every human visitor and invisible to everything else. It took us the better part of a week to get from “completely stuck” to “fully working.” If this saves someone else that week, it was worth writing up.
(written with assistance of ai that I implimented the fixes with)