[Not an ICP Problem — An SPA Problem]
> Why SPAs Are Hard for Search Engines
Caffeine builds React Single Page Applications (SPAs). When a search engine crawler visits your app, the server returns a near-empty HTML shell — a <div id="root"></div> and a JavaScript bundle. All actual content only appears after the browser executes that JavaScript.
Traditional website
Server returns full HTML content on the first request. Google reads it instantly.
React SPA (Caffeine apps)
Server returns empty HTML shell. Content appears after JavaScript runs — which crawlers may skip or delay.
Google's crawler can execute JavaScript — but it's slower, less reliable, and happens later than static HTML crawling. The result: your content may not be indexed at all, or only indexed with significant delay.
> What ICP Adds to the Picture
By default, ICP canister URLs look like: abc12-defgh-cai.icp0.io. These are unknown to Google — it has no reason to crawl them without an inbound link or sitemap.
ICP has officially supported crawlability since 2022 — real production apps like OpenChat are indexed by Google today.
A custom domain (yourapp.com) solves the discovery problem — Google treats it like any other website.
Raw canister URLs (abc12-xyz.icp0.io) are not crawled by default — Google has no reason to visit them.
> The Honest Trade-Off
YOU GAIN
- ›Permanent on-chain hosting — no server bills ever
- ›Sovereign ownership — your canister, your rules
- ›Censorship resistance — no one can take it down
- ›Data safety guarantees — no loss on upgrades
YOU GIVE UP (vs. traditional stacks)
- ›Easy out-of-the-box SEO for dynamic content
- ›Automatic Google crawling of canister URLs
- ›SSR (server-side rendering) — not natively available on ICP
> Five Actions You Can Take
Each card includes a copyable Caffeine prompt tip — paste it directly into your build.
Add a robots.txt
Tells search engine crawlers they're welcome to visit your app. Without it, some crawlers skip unfamiliar domains entirely. A single file with two lines is all it takes.
CAFFEINE PROMPT TIP
"Add a robots.txt file to the canister that allows all crawlers. Disallow nothing."
Add a sitemap.xml
Lists all the URLs in your app so crawlers know what exists. Especially important for apps with multiple pages — without a sitemap, Google has to discover them by following links.
CAFFEINE PROMPT TIP
"Add a sitemap.xml endpoint to the canister that lists all major routes in the app."
Set proper meta tags
The <title> and <meta name="description"> tags are what Google shows in search results. Open Graph tags (og:title, og:description, og:image) control what appears when someone shares your link on X or LinkedIn.
CAFFEINE PROMPT TIP
"Set proper title, meta description, and Open Graph tags in index.html for: [app name and one-line description]."
Use a custom domain
yourapp.com is immediately trusted and crawled by Google. A raw canister URL like abc12-xyz.icp0.io is not. Custom domains are covered in Section 13 of this guide — it's a 5-minute step once your app is live. As of June 10, 2026, domains bought through Caffeine include a renewal management flow right in the dashboard, so you can review upcoming renewals and keep your domain from lapsing without leaving the platform.
CAFFEINE PROMPT TIP
"Remind me to connect a custom domain when the app is ready to go live."
Add a static landing section
Your home route can include a content-rich block that renders before React's JavaScript takes over. This gives Google something to read immediately — no waiting for JS execution. Think of it as a plain HTML summary of your app baked into the first paint.
CAFFEINE PROMPT TIP
"Add a content-rich static-style landing section at the top of the homepage with: [app name], [what it does], [who it's for]. This should render as part of the initial HTML, not just as JavaScript."
> When SEO Doesn't Matter
For many Caffeine apps, Google ranking is irrelevant from day one:
- 01Your app requires login before showing any content — Google can't index it anyway
- 02Users find your app via social sharing, forum posts, or direct links — search ranking matters less
- 03You're building an internal tool or private dashboard — not relevant at all
- 04Your app is a community-first product where word-of-mouth drives growth
[ICP's Trade-Off Is Intentional]
