User-agent: *
Allow: /
# Block internal/admin/test routes
Disallow: /Admin
Disallow: /dashboard
Disallow: /TestTracking
Disallow: /TestAPI
Disallow: /TestStandardAPI
Disallow: /Diagnostics
Disallow: /VPSTest
Disallow: /Architecture
Disallow: /ComingSoon
Disallow: /auth/
Disallow: /api/
Disallow: /profile/
Disallow: /Profile
Disallow: /productsearch
Disallow: /connect-extension
Disallow: /extension/
Disallow: /stores/amazon
Disallow: /stores/temu
Disallow: /*?page=
Disallow: /*?sort=
# Googlebot executes JS and indexes the RENDERED DOM, not the HTML we ship.
# Every public page below hydrates and re-fetches its own content from these
# read-only endpoints. While they were blocked, the client-side catch branch
# overwrote good prerendered content with an error state — that is what put
#
Error
under HTTP 200 on the 12 legal pages. Prerendering fixes the
# HTML we serve; these Allow rules fix what the crawler ends up with. They are
# two different halves of the same problem, and fixing only one leaves the bug.
#
# Longest match wins, so each rule below overrides "Disallow: /api/" above.
# Everything not listed stays blocked. Two deliberate absences:
# /api/legal/consent* per-user consent state, not page content
# /api/top-products admin-only (clicks.routes.js:45 wraps it in adminAuth,
# prod answers 401) and read by components/admin/ClickAnalytics.jsx
# alone — no public page renders from it, so allowing it
# would advertise an admin path and still serve a crawler
# nothing. Every rule below was re-verified against prod
# on 2026-08-05: all answer 200 unauthenticated.
Allow: /api/legal/cookies-policy/
Allow: /api/legal/privacy-policy/
Allow: /api/legal/terms-of-service/
Allow: /api/legal/disclaimer/
Allow: /api/deals
Allow: /api/videos
Allow: /api/promo-codes
Allow: /api/featured-products
Allow: /api/hybrid-search/
# Path-based i18n: the router mounts every route under /ru and /he as well, so
# each private route above is reachable under those prefixes too. Without these
# the locale-prefixed copies stay crawlable (GSC flagged /ru/dashboard and
# /ru/Profile) — the rules above are plain prefixes and do not cover them.
Disallow: /ru/Admin
Disallow: /ru/dashboard
Disallow: /ru/TestTracking
Disallow: /ru/TestAPI
Disallow: /ru/TestStandardAPI
Disallow: /ru/Diagnostics
Disallow: /ru/VPSTest
Disallow: /ru/Architecture
Disallow: /ru/ComingSoon
Disallow: /ru/auth/
Disallow: /ru/profile/
Disallow: /ru/Profile
Disallow: /ru/productsearch
Disallow: /ru/connect-extension
Disallow: /ru/extension/
Disallow: /ru/stores/amazon
Disallow: /ru/stores/temu
Disallow: /he/Admin
Disallow: /he/dashboard
Disallow: /he/TestTracking
Disallow: /he/TestAPI
Disallow: /he/TestStandardAPI
Disallow: /he/Diagnostics
Disallow: /he/VPSTest
Disallow: /he/Architecture
Disallow: /he/ComingSoon
Disallow: /he/auth/
Disallow: /he/profile/
Disallow: /he/Profile
Disallow: /he/productsearch
Disallow: /he/connect-extension
Disallow: /he/extension/
Disallow: /he/stores/amazon
Disallow: /he/stores/temu
# /recovery/verify is allowed by nginx but not prerendered, so it falls through
# to the SPA fallback and serves the HOME page's static HTML — canonical and the
# whole hreflang cluster pointing at "/". That is what produced the "hreflang
# conflicts within page source code" findings. It is a transactional page that
# must keep working, so it is blocked from crawling rather than redirected.
# (Dead aliases in the same situation — /cookie-policy, /referral-program — are
# 301'd in nginx instead: a removed page needs a redirect, not a crawl block.)
Disallow: /recovery/verify
Disallow: /ru/recovery/verify
Disallow: /he/recovery/verify
Sitemap: https://cashbackpro.org/sitemap.xml