Skip to content

Point campaigns SDK redirects at real sections, not the site root - #54

Merged
next-devin merged 7 commits into
mainfrom
seo-sdk-redirect-targets
Sep 8, 2026
Merged

Point campaigns SDK redirects at real sections, not the site root#54
next-devin merged 7 commits into
mainfrom
seo-sdk-redirect-targets

Conversation

@next-devin

@next-devin next-devin commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Why

Search Console flagged 38 URLs as Blocked by robots.txt on 2026-09-07. 35 were developers.nextcommerce.com/docs/campaigns/* pages redirecting to https://cart-sdk.nextcommerce.com/latest/, and cart-sdk served Disallow: /. Google follows the redirect, hits the disallowed host, and attributes the block to the source URL.

The root cause is fixed separately: campaign-cart's DOCS_BASE_URL repo variable had never been set, so CI regenerated a disallow-all robots.txt on every deploy. That is now set and verified live — 37 of the 38 flagged URLs return 200 to Googlebot.

This PR fixes the other half. All 35 URLs still redirect to the site root, which Google reads as a soft 404. Without this they move from "Blocked" to "Page with redirect / not indexed" rather than recovering.

What changed

One destination per section — 29 rules covering all 134 historical campaign doc paths. Each URL now reaches the page that absorbed its content instead of the root.

Both netlify.toml and public/_redirects are generated from one table and verified identical. public/_redirects is what actually serves; the site is on Cloudflare Workers static assets.

Two constraints this design satisfies

Cloudflare's 100 dynamic-rule cap. Cloudflare counts every cross-host redirect as dynamic, not just splats and placeholders. An intermediate version of this branch enumerated one rule per page and was rejected at deploy: Maximum number of dynamic _redirects rules limit of 100 exceeded. Measured correctly, 6de16d3 had 83 dynamic rules and deployed; 02d721c had 142 and did not. This version has 33. Do not expand the block back to one rule per page.

Rule precedence. An earlier version listed specific paths above their parent wildcards and relied on first-match-wins. On the preview deploy the parent wildcard won anyway — /checkout/payment landed on the generic checkout page, and three other rules silently lost their anchors. Cloudflare's docs say first-match applies, so this is unexplained. The section shape makes it moot: each section's exact rule and its splat share one destination, so which matches first cannot change the result.

Trade-off

Anchor granularity. /checkout/payment lands on the checkout page rather than #payment-methods. Section-level anchors are kept where a whole section maps to one (cart-summary#cart-summary, package-toggle#order-bumps). Reaching the right page is what resolves the soft 404; the anchor was a nicety, and buying it back would breach the rule cap.

Deliberate gaps

  • FOMO 404s. The feature is gone from the SDK — zero matches for fomo in src/. There is deliberately no /utilities/* catch-all that would swallow it; the three surviving utilities are listed individually. Cloudflare's _redirects cannot express 410, so a real 404 via not_found_handling is the cleanest signal.
  • campaign-cart/cart-system is unlisted — the existing campaign-cart/:path rule already rewrites it into /docs/campaigns/, which these rules then resolve. Verified end-to-end below.

Verification

Static, before pushing: TOML parses, no duplicate from, both files identical across all 29 rules, no rule catching a still-live page, no section with conflicting destinations, all 134 historical paths covered, npm run check passes.

Live, against the Cloudflare preview deploy:

  • 13 representative redirects all resolve to their intended section, anchors intact.
  • All three FOMO paths return 404.
  • All six still-live pages (/docs/campaigns, api, api/carts/cartsCreate, page-kit, templates, admin-api) return 200.
  • End-to-end chases to a final 200, including the two-hop campaign-cart/cart-system/docs/campaigns/cart-system#cart-operations.

Not covered here

  • guides.nextcommerce.com is also flagged; confirmed it should stay unindexed. Mark as intentional in Search Console.
  • cart-sdk.nextcommerce.com/latest/documents/Reference_URL_Parameters 404s — a stale inbound link, now at /latest/reference/url-parameters/.

🤖 Generated with Claude Code

All 43 rules moving /docs/campaigns/* to cart-sdk.nextcommerce.com pointed at
https://cart-sdk.nextcommerce.com/latest/. Search Console flagged 38 URLs as
"Blocked by robots.txt" on 2026-09-07: Google follows the redirect, finds
cart-sdk's Disallow-all robots.txt, and attributes the block to the source URL.

Unblocking cart-sdk (its DOCS_BASE_URL is being set separately) fixes the robots
half but not this half — 35 distinct pages collapsing onto one destination is a
soft-404 pattern, so the URLs would move from "Blocked" to "not indexed" rather
than recover.

Each rule now targets the section that absorbed the old page. The SDK site
consolidated ~160 narrative pages into six reference pages plus the page-type
guides, so the mapping is many-to-one; every destination page and every anchor
was verified against the built site.

Two deliberate gaps:

  - FOMO gets no rule, and the /docs/campaigns/utilities/* catch-all is dropped
    so it cannot swallow those paths. The feature is gone from the SDK (no
    matches in src/), so a 404 is the honest answer.
  - /docs/campaigns/campaign-cart/cart-system is unlisted; the generic
    campaign-cart/:path rule already rewrites it into /docs/campaigns/, which
    these rules then resolve.

netlify.toml and public/_redirects are generated from one table and verified
identical. public/_redirects is what Cloudflare actually serves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 7, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
developer-docs 38be9f9 Commit Preview URL

Branch Preview URL
Sep 07 2026, 09:24 AM

@next-devin
next-devin marked this pull request as ready for review September 7, 2026 07:54
Comment thread netlify.toml Outdated
Comment thread netlify.toml Outdated
Comment thread netlify.toml Outdated
Comment thread netlify.toml
Comment thread netlify.toml
Comment thread netlify.toml
Comment thread netlify.toml
@kilo-code-bot

kilo-code-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

The previous SUGGESTION about ambiguous campaign-cart/:path references has been resolved in commit 38be9f9. Both comments in netlify.toml and public/_redirects now name the full /docs/campaigns/campaign-cart/:path pattern, eliminating the ambiguity. No new issues found in the incremental diff.

Files Reviewed (2 files)
  • netlify.toml - 0 issues (previous SUGGESTION resolved)
  • public/_redirects - 0 issues (previous SUGGESTION resolved)
Previous Review Summaries (6 snapshots, latest commit 98da688)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 98da688)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
netlify.toml 175 "the campaign-cart/:path rule above" is ambiguous — there are two :path rules above (lines 57 and 62). Same wording exists in public/_redirects.
Files Reviewed (2 files)
  • netlify.toml - 1 issue
  • public/_redirects - 0 issues (same wording as netlify.toml but the suggestion is anchored on the netlify.toml hunk)

Fix these issues in Kilo Cloud

Previous review (commit ebe8716)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
netlify.toml 322 Comment cites campaign-cart/:path as the reason the bare index 404s; :path is Netlify-only and never executes in public/_redirects (the file Cloudflare actually serves). The rule is correct, but the rationale only holds on Netlify.
public/_redirects 90 Same misleading-rationale issue: :path doesn't exist in Cloudflare's _redirects syntax. On Cloudflare the bare index 404s because no rule matched the bare path, not because a :path rewrite skipped it.
Files Reviewed (2 files)
  • netlify.toml - 1 issue
  • public/_redirects - 1 issue

Fix these issues in Kilo Cloud

Previous review (commit d6d2a48)

Status: No Issues Found | Recommendation: Merge

The new commit (d6d2a4 Fit redirects under Cloudflare's 100 dynamic-rule cap) refactors the cart-sdk redirect block in netlify.toml and public/_redirects. Every enumerated exact-path rule from 02d721c/123caba has been collapsed into an from = "/docs/campaigns/<section>" exact plus from = "/docs/campaigns/<section>/*" splat pair, with the exact and splat sharing the same destination. The three /docs/campaigns/utilities/{debugger,exit-intent,loading-states} exact+splat pairs were moved to the top of the cart-sdk block, and the autoloading placeholder rules above now number 4 (down from 9), keeping cross-host dynamic redirects well under Cloudflare's 100-rule cap. The placeholder block in public/_redirects and netlify.toml match rule-for-rule.

All 14 prior inline comments are stale on the current diff: every comment referenced a now-deleted enumerated exact rule whose specific anchor (#payment-methods, #required-settings, #manual-events, #manual-tracking, #enabling-analytics, #attribution-and-metadata, #checkout-form, etc.) is intentionally dropped by this refactor. The author's comment block at the top of the cart-sdk block documents the trade-off ("the cost is anchor granularity … /checkout/payment lands on the checkout page rather than #payment-methods") and the FOMO gap, so the previously flagged defects are explicitly accepted rather than unresolved. Re-adding splats per old anchor targets would re-introduce the precedence bug the Cloudflare preview deploy caught.

No new changed-code issues. The same-destination exact+splat pairs eliminate the "which match wins" question the previous review asked, and the new section-level splats are intentional behavior, not a regression.

Files Reviewed (2 files)
  • netlify.toml
  • public/_redirects

Previous review (commit 123caba)

Status: No Issues Found | Recommendation: Merge

The new commit (123caba Retrigger Cloudflare Workers build) is a build-retriage no-op: git diff 02d721c..123caba is empty for both netlify.toml and public/_redirects, so there are no new changed lines to review. The rationale in the commit message (config-only, local npm run build succeeds, byte-identical out/_redirects, well below Cloudflare's static/dynamic limits at 167/4) is consistent with no content changes since the previous review.

All 14 existing inline comments remain active and accurate against the current HEAD; none were resolved or rendered stale by this retrigger. No new findings.

Files Reviewed (2 files)
  • netlify.toml
  • public/_redirects

Previous review (commit 02d721c)

Status: No Issues Found | Recommendation: Merge

The new commit (02d721c Enumerate exact paths; Cloudflare ignored first-match order) reworks the previous PR so every campaigns rule is an exact path with no wildcards. This eliminates the catch-all that the previous review's seven WARNINGs depended on, so all of those findings are now stale:

  • netlify.toml / public/_redirects no longer carry any * splat under /docs/campaigns/*, so there is no longer a /analytics/*, /attribution-metadata/*, /checkout/*, /configuration/*, /payment/*, or /form/* rule that the previous findings assumed would swallow sub-paths.
  • Where a previous exact-path rule plus its splat were both removed and replaced with a single exact path, that exact path itself points at the correct anchored destination (e.g. /docs/campaigns/analytics/custom-events#manual-events, /docs/campaigns/checkout/payment#payment-methods).
  • The new rule list also covers the three pre-history URLs Search Console had indexed (rudderstack, google-tag-manager, campaign-cart/cart-system) as direct one-hop redirects, removing the two-hop rewrite that the previous PR description relied on.
  • No duplicate from paths, no shadowing concerns (exact paths only), no cross-host ordering issue.

Re-verified the full current diff (6de16d3..HEAD): 79 cart-sdk rules in each file, destination hosts restricted to cart-sdk.nextcommerce.com, anchors are present, no new defects introduced by the enumeration.

Files Reviewed (2 files)
  • netlify.toml
  • public/_redirects

Previous review (commit 6de16d3)

Status: 7 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 7
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
netlify.toml 350 /docs/campaigns/analytics/custom-events has no custom-events/* splat; sub-paths fall through to /analytics/* catch-all and lose the #manual-events anchor.
netlify.toml 354 /docs/campaigns/analytics/tracking-api has no tracking-api/* splat; sub-paths miss #manual-tracking.
netlify.toml 359 /docs/campaigns/analytics/configuration has no configuration/* splat; sub-paths miss #required-settings.
netlify.toml 364 /docs/campaigns/analytics/debugging has no debugging/* splat; sub-paths miss #enabling-analytics.
netlify.toml 224 /docs/campaigns/javascript-api/attribution-metadata has no attribution-metadata/* splat; sibling attribution and events rules do, so this is an inconsistency.
netlify.toml 504 /docs/campaigns/checkout/payment has no payment/* splat; sub-paths miss #payment-methods.
netlify.toml 519 /docs/campaigns/checkout/form has no form/* splat; sub-paths miss #checkout-form.

The same gaps exist row-for-row in public/_redirects (the file Cloudflare actually serves). Each issue points at a single new redirect that added an exact path but no splat variant while siblings in the same group added both — sub-paths under that prefix now land on the section's index page rather than the per-page anchor this PR was written to give them.

The remaining changes (no-shadow ordering, deliberate fomo 404, the campaign-cart/cart-system two-hop rewrite, anchor and 200-status verification per the PR description) all check out against the diff.

Files Reviewed (2 files)
  • netlify.toml - 7 issues
  • public/_redirects - 7 mirror issues (not duplicated inline; same defect per row)

Fix these issues in Kilo Cloud


Reviewed by minimax-m3 · Input: 23.3K · Output: 1.2K · Cached: 198.9K

Verified the previous commit against the Cloudflare preview deploy and found the
rules were not resolving as written. /docs/campaigns/checkout/payment went to the
generic checkout page rather than the payment section, /docs/campaigns/data-attributes/state
lost its anchor, and bundle-selector/vouchers landed on #bundle-selector instead
of #voucher-codes. In every case the parent wildcard beat a more specific rule
listed above it, so Cloudflare is not applying first-match-wins the way Netlify
documents it.

Rather than reverse-engineer the precedence and burn deploy cycles on it, this
removes the ambiguity: every rule is now an exact path, one per page that
actually existed, with no wildcards in the block. No two rules can match the same
URL, so precedence stops mattering on either host.

The path list is generated from the pages deleted in this repo's own history,
plus three URLs Search Console has indexed that predate it — the rudderstack and
google-tag-manager analytics examples, and campaign-cart/cart-system. Enumeration
from git alone would have missed those, which is worth remembering if more
turn up.

Verified against the built SDK site: 10 destination pages all 200, 32 anchors all
present as real element ids, both files identical across 138 rules, no duplicate
`from`, no still-live page (api/, page-kit, templates, admin-api) caught by a
rule. Replaying the Search Console export gives 33 of 35 mapped, the other two
being the FOMO paths that are meant to 404.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
next-devin and others added 3 commits September 7, 2026 15:13
The build for 02d721c failed with no log text exposed on the check run. The
change is config-only, `npm run build` succeeds locally, `out/_redirects` is
byte-identical to `public/_redirects`, every line is well-formed, and the file
is far inside Cloudflare's documented 2,000 static / 100 dynamic limits at 167
and 4. Retriggering to separate a transient deploy failure from a real one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The deploy for 02d721c was rejected by the Cloudflare API:

  Invalid _redirects configuration:
  Line 121: Maximum number of dynamic _redirects rules limit of 100 exceeded

Cloudflare counts every cross-host redirect as dynamic, not just those with a
splat or placeholder. The earlier "167 static / 4 dynamic" reading used the wrong
definition. Measured properly:

  6de16d3   79 external + 4 splat  =  83   deployed
  02d721c  138 external + 4 splat  = 142   rejected

Enumerating one rule per historical page bought precision at the cost of a hard
platform limit. This replaces it with one destination per section: 29 rules, 33
dynamic in total, comfortably inside the cap.

The section shape also removes the precedence problem that motivated the
enumeration. Each section's exact rule and its splat point at the same target, so
it no longer matters which one Cloudflare matches first — the ordering question
that produced the wrong destinations on the earlier preview simply cannot arise.

The cost is anchor granularity: /checkout/payment now lands on the checkout page
rather than #payment-methods. What matters for indexing is that every URL reaches
the page that absorbed its content instead of the site root, which Google reads
as a soft 404. Section anchors are kept where a whole section maps to one.

FOMO still has no rule and there is deliberately no /utilities/* catch-all to
swallow it, so its five paths 404. The three surviving utilities are listed
individually.

Verified: both files in sync across all 29 rules, no duplicate `from`, no rule
catching a still-live page, no section with conflicting destinations, all 134
historical paths covered, and 32 of 35 flagged Search Console URLs mapped — the
rest being the two FOMO paths and campaign-cart/cart-system, which the existing
campaign-cart/:path rule already handles. `npm run check` passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A full sweep of all 140 historical paths against the preview deploy found
/docs/campaigns/campaign-cart returning 404. The rule was left out on the
assumption that the existing campaign-cart/:path rule covered it, but :path
only matches sub-paths — campaign-cart/cart-system resolves correctly while the
section index itself falls through to a 404. It was a real published page
(campaign-cart/index.mdx), so it needs its own rule.

Takes the block to 30 rules, 34 dynamic against Cloudflare's cap of 100.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread netlify.toml Outdated
Comment thread public/_redirects
The previous commit added an exact rule for the bare campaign-cart index but
left the block comment above claiming campaign-cart is handled entirely by the
campaign-cart/:path rule. Sub-paths are; the index has its own rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread netlify.toml Outdated
Two rules above end in campaign-cart/:path — /docs/campaign-cart/:path and
/docs/campaigns/campaign-cart/:path. Both comments referring to "the
campaign-cart/:path rule above" now name the full pattern, in each file.

Comments only; no rule changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@next-devin
next-devin merged commit 3f43d6d into main Sep 8, 2026
3 checks passed
@next-devin
next-devin deleted the seo-sdk-redirect-targets branch September 8, 2026 04:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants