fix(seo): resolve the 5 GSC "Not found (404)" URLs#565
Conversation
Google Search Console flagged 5 URLs as 404 (Coverage drilldown 2026-06-12): - docs.ophis.fi/api, docs.ophis.fi/status -> ALREADY FIXED: both now 301/308 to live pages (/intent-api, /status/). Stale 404s crawled 2026-05-31 before the redirects existed; only need GSC "Validate Fix" (no code change here). - docs.ophis.fi/institutional, docs.ophis.fi/brand -> genuine 404s. These are swap-app HashRouter routes (swap.ophis.fi/#/institutional, /#/brand), never docs pages; Google found them via stale links (the docs footer already links the correct /# forms). Add 301s in the docs _redirects to their real homes. - rebates.ophis.fi/ -> the rebate-indexer API host root, returns 404 JSON. The whole host is API + per-wallet /tier pages, none of it meant for indexing. Add a robots.txt (Disallow: /) so crawlers stay off the host entirely. rebate-indexer typecheck green; _redirects rules valid (3-field). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 080d5437ae
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| app.get('/robots.txt', { | ||
| config: { | ||
| rateLimit: { max: 200, timeWindow: '1 minute' }, | ||
| }, | ||
| }, async (_req, reply) => | ||
| reply.code(200).type('text/plain; charset=utf-8').send('User-agent: *\nDisallow: /\n'), |
There was a problem hiding this comment.
Return a non-404 response for the rebates root
When Google rechecks the flagged URL https://rebates.ophis.fi/, this change only serves /robots.txt; the root path itself still falls through to the existing not-found handler and returns 404. That means the GSC “Not found (404)” item for the bare host is not actually resolved by this route (and Disallow: / can also prevent crawlers from seeing any future fix on /). Add a GET / response or redirect/noindex page so the exact reported URL stops returning 404.
Useful? React with 👍 / 👎.
| /institutional https://swap.ophis.fi/#/institutional 301 | ||
| /brand https://swap.ophis.fi/#/brand 301 |
There was a problem hiding this comment.
Redirect the trailing-slash stale paths too
If crawlers or old links request docs.ophis.fi/institutional/ or docs.ophis.fi/brand/, these exact-source rules only cover the no-slash paths, so the slash variants continue to fall through to the docs 404. The existing /api fix above handles both forms, and Cloudflare Pages treats /trailing and /trailing/ as distinct redirect sources, so add the trailing-slash rules for these two stale product routes as well.
Useful? React with 👍 / 👎.
| # docs.ophis.fi/{institutional,brand} URLs from old links and flagged them 404 | ||
| # (GSC, 2026-06). 301 them to their real homes on the swap app. (If CF drops the | ||
| # fragment, the target still resolves to the live swap home, so never a 404.) | ||
| /institutional https://swap.ophis.fi/#/institutional 301 |
There was a problem hiding this comment.
Point /institutional at the business host
The current swap app no longer owns /institutional: RoutesApp.tsx redirects that route to https://business.ophis.fi in a client-side useEffect. Sending the docs 301 to https://swap.ophis.fi/#/institutional therefore makes crawlers land on the SPA shell and only reach the real page if they execute JavaScript, while non-JS clients see the wrong destination; redirect this stale docs URL directly to the business host instead.
Useful? React with 👍 / 👎.
…lash variants (Codex P2 on #565) (#566) Three Codex P2s on #565: 1. rebates.ophis.fi/ still 404'd: robots.txt only served /robots.txt; the root fell through to the 404 handler, and Disallow:/ blocked crawlers from ever seeing a fix on /. Now: GET / -> 301 to docs.ophis.fi/affiliate (the public rebate explainer), and robots ALLOWS exactly / (Allow:/$ wins longest-match over Disallow:/) so the redirect is crawlable while API/tier paths stay out. 2. /institutional pointed at the swap SPA shell: RoutesApp's InstitutionalRedirect does window.location -> business.ophis.fi, so non-JS crawlers landed wrong. 301 docs/institutional straight to https://business.ophis.fi (crawlable host). 3. trailing-slash variants: CF Pages treats /x and /x/ as distinct sources, so add /institutional/ and /brand/ (mirrors the /api + /api/ pair). /brand keeps swap.ophis.fi/#/brand (real HashRouter page, no business equiv). rebate-indexer typecheck green; _redirects rules valid. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
GSC Coverage drilldown (2026-06-12) flagged 5 URLs as Not found (404). Diagnosed each:
docs.ophis.fi/apidocs.ophis.fi/statusdocs.ophis.fi/institutionaldocs.ophis.fi/brandrebates.ophis.fi/Disallow: /(this PR)Why
/institutional+/brandare swap-app HashRouter routes (swap.ophis.fi/#/…), never docs pages. Google indexed staledocs.ophis.fi/{institutional,brand}from old links (the docs footer already links the correct/#forms). 301-ing them inapps/docs-ophis/static/_redirectsclears the 404 and points to the real home. (If CF drops the fragment, the target still resolves to the live swap home — never a 404.)rebates.ophis.fiis the rebate-indexer API host (JSON endpoints + per-wallet/tierHTML); none of it should be indexed. Added aGET /robots.txt→Disallow: /so crawlers stay off the whole host (resolves the bare-root 404 by stopping the crawl).Verification
rebate-indexer typecheck→ clean._redirectsrules valid./institutional+/brandshould 301;rebates.ophis.fi/robots.txtshould serveDisallow: /./api+/statusalready 200 via redirect — those just need Validate Fix in GSC (your side).Deploys: docs via CF Pages (
_redirects); indexer viarebate-indexer-deploy.yml.🤖 Generated with Claude Code