An app built quickly with an AI assistant tends to have two kinds of pages: the public ones and the ones behind login. The second kind is often protected only by the fact that the interface redirects you when there is no session, on the client, after the page has loaded. Googlebot does not run your redirect. It gets the page.
What does this check detect?
Answer. Routes that look like application surfaces (dashboard, settings, admin, account, generate, projects, and their siblings) that return HTTP 200 to a request with no cookies and carry no noindex.
Moonleap discovers routes from the sitemap, the navigation, the JavaScript bundle's route table and common path names, then requests each one as an anonymous visitor. A route that returns a full page with a 200 status, no noindex meta tag and no X-Robots-Tag header is listed in the finding. On the site in this case there were seven on 4 September 2026; on 7 September, after the fix, each of them returned a 303 to /login.
Why is this two problems, not one?
Answer. An indexable app shell is thin, duplicate content Google may rank for your brand; a 200 to an anonymous request is a surface an attacker can enumerate without logging in.
On the SEO side, /dashboard renders a heading, a spinner and a menu. If Google indexes it, that page competes with your real pages for brand queries and occasionally outranks them (a bare "Dashboard | Brand" title is short and exact). We have seen a login page rank second for a company's own name.
On the security side, a route that answers 200 without a session tells you the server did not check. Whatever data it fetches afterwards may be protected by the API, or may not; the point is that the protection is not where it belongs. Robots files that list these routes make it worse: Disallow: /admin is not protection, it is a directory.
What is the fix?
Answer. Check the session on the server before rendering, redirect anonymous requests, and mark the routes noindex for the case where a page does render.
SvelteKit, in src/hooks.server.ts, before any page loads:
const PRIVATE = ['/dashboard', '/settings', '/admin', '/projects', '/generate', '/labs'];
export const handle = async ({ event, resolve }) => {
const { user } = await event.locals.safeGetSession();
if (!user && PRIVATE.some((p) => event.url.pathname.startsWith(p))) {
throw redirect(303, `/login?next=${encodeURIComponent(event.url.pathname)}`);
}
return resolve(event);
};
Next.js does the same in middleware.ts with a matcher for the private paths and a NextResponse.redirect when the session cookie is absent. In both frameworks the private layout should also emit <meta name="robots" content="noindex">, so that a route that renders for a logged-in user (and is fetched by a browser extension or a shared link) still does not get indexed.
Then take the routes out of robots.txt and the sitemap. A redirect cannot be indexed; listing the path only advertises it.
How do you verify it?
Answer. Request each route with no cookies and expect a redirect; then check the Pages report for the app URLs after a recrawl.
for p in /dashboard /settings /admin /projects; do
printf "%s → " "$p"; curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" "https://<your-domain>$p"
done
Every line should show a 302 or 303 and the login URL. In Search Console, Indexing, Pages, any app route already indexed will move to "Page with redirect" after the next crawl; use the URL Inspection tool to request a recrawl if one is ranking for your brand.
FAQ
Is client-side redirect enough if the API is protected?
No. The shell still renders and can be indexed, and a route that returns 200 to a crawler returns 200 to anyone. Check on the server.
Should I put the private routes in robots.txt as well?
No. A redirect cannot be indexed, and the Disallow line publishes the path. Use the server check plus noindex.
What about preview or experimental routes?
Same rule. /api/experimental/… and /test routes that answer in production are the most common leftover from building fast; remove them or gate them.
Sources
- Google, "Block Search indexing with noindex" https://developers.google.com/search/docs/crawling-indexing/block-indexing
- SvelteKit, hooks and `handle` https://svelte.dev/docs/kit/hooks
- Next.js, middleware https://nextjs.org/docs/app/building-your-application/routing/middleware
- Case, omniroom.ai, 7 routes on 4 September 2026 and re-check on 7 September (Moonleap link discovery)