Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions data/gsc-known-urls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
{
"_note": "Every URL Google Search Console showed in search results (Performance export, 23 Oct 2026, host + path). tests/gsc-known-urls.test.js holds each one to a single redirect onto a sitemap page. Add rows from future exports; never remove one.",
"urls": [
{
"host": "audit.stackarchitect.xyz",
"path": "/"
},
{
"host": "stackarchitect.xyz",
"path": "/"
},
{
"host": "stackarchitect.xyz",
"path": "/analyzify-alternative/"
},
{
"host": "stackarchitect.xyz",
"path": "/app-audit/"
},
{
"host": "stackarchitect.xyz",
"path": "/elevar-alternative/"
},
{
"host": "stackarchitect.xyz",
"path": "/klaviyo-vs-systeme-io/"
},
{
"host": "stackarchitect.xyz",
"path": "/northbeam-alternative/"
},
{
"host": "stackarchitect.xyz",
"path": "/pro/"
},
{
"host": "stackarchitect.xyz",
"path": "/stack"
},
{
"host": "stackarchitect.xyz",
"path": "/stack/"
},
{
"host": "stackarchitect.xyz",
"path": "/tools/"
},
{
"host": "stackarchitect.xyz",
"path": "/triple-whale-alternative/"
},
{
"host": "tools.stackarchitect.xyz",
"path": "/"
},
{
"host": "tools.stackarchitect.xyz",
"path": "/make-vs-zapier-cost-calculator"
},
{
"host": "tools.stackarchitect.xyz",
"path": "/meta-emq-score-estimator"
},
{
"host": "tools.stackarchitect.xyz",
"path": "/shopify-agentic-storefront-readiness-checker"
},
{
"host": "tools.stackarchitect.xyz",
"path": "/stocky-migration-risk-scorer"
},
{
"host": "www.stackarchitect.xyz",
"path": "/"
},
{
"host": "www.stackarchitect.xyz",
"path": "/autocrat-quota-fix"
},
{
"host": "www.stackarchitect.xyz",
"path": "/best-ai-tools-shopify"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/automate-shopify-inventory-operations-without-apps"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/best-alternatives-to-zapier-for-shopify"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/google-apps-script-quotas-explained-how-to-avoid-limits-and-scale-your-automations"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/how-much-shopify-apps-really-cost-and-how-to-cut-your-app-bill-in-half"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/how-to-bypass-ios-privacy-restrictions-in-shopify-ads"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/how-to-fix-service-invoked-too-many-times-in-google-apps-script"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/how-to-fix-shopify-conversion-tracking-after-ios-14"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/how-to-fix-shopify-conversion-tracking-after-ios-updates"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/how-to-reduce-shopify-apps-"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/how-to-track-shopify-inventory-in-google-sheets"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/klaviyo-vs-getresponse-shopify-2026"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/make-com-shopify-automation-guide"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/recover-lost-shopify-conversions-capi-shield"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/recover-lost-shopify-conversions-with-capi-shield"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/replace-shopify-stocky-with-a-0-inventory-system"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/scalable-google-sheets-automation-for-high-volume-workflows"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/shopify-agentic-storefronts-setup-guide-2026"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/shopify-ai-playbook-2026"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/shopify-apps-that-are-a-waste-of-money"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/shopify-automation-stack-for-small-stores"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/shopify-inventory-management-without-apps-automate-your-workflow-for-0"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/shopify-meta-roas-dropped-2026-fix"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/shopify-server-side-tracking-complete-setup-guide"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/the-complete-guide-to-reliable-shopify-automations"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/the-lean-shopify-tech-stack-2026"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/the-ultimate-guide-to-shopify-inventory-management"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/tidio-for-shopify-complete-setup-guide"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/tidio-vs-gorgias-shopify"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/why-meta-ads-show-fewer-purchases-than-shopify"
},
{
"host": "www.stackarchitect.xyz",
"path": "/blog/zapier-vs-shopify-flow-vs-make"
},
{
"host": "www.stackarchitect.xyz",
"path": "/capi-shield"
},
{
"host": "www.stackarchitect.xyz",
"path": "/shopify-app-cost-calculator"
},
{
"host": "www.stackarchitect.xyz",
"path": "/shopify-automation-guides"
},
{
"host": "www.stackarchitect.xyz",
"path": "/shopify-automation-scanner"
},
{
"host": "www.stackarchitect.xyz",
"path": "/shopify-optimization-guides"
},
{
"host": "www.stackarchitect.xyz",
"path": "/shopify-profit-loss-automation"
},
{
"host": "www.stackarchitect.xyz",
"path": "/sitemap"
},
{
"host": "www.stackarchitect.xyz",
"path": "/stocky-swap"
},
{
"host": "www.stackarchitect.xyz",
"path": "/tiktok-events-api-shopify"
},
{
"host": "www.stackarchitect.xyz",
"path": "/tools"
},
{
"host": "www.stackarchitect.xyz",
"path": "/ultimate-shopify-automation-guide"
}
]
}
10 changes: 10 additions & 0 deletions functions/_legacy-redirects.js
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,16 @@ export const LEGACY_RULES = [
"to": "/best-free-shopify-apps-2026/",
"status": 301
},
{
"from": "/blog/how-to-reduce-shopify-apps-",
"to": "/shopify-app-stack-kill-or-keep-auditor/",
"status": 301
},
{
"from": "/blog/how-to-reduce-shopify-apps-/",
"to": "/shopify-app-stack-kill-or-keep-auditor/",
"status": 301
},
{
"from": "/blog/free-shopify-apps-replace-paid-2026",
"to": "/best-free-shopify-apps-2026/",
Expand Down
4 changes: 4 additions & 0 deletions public/_redirects
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@
/blog/how-much-shopify-apps-really-cost-and-how-to-cut-your-app-bill-in-half/ /best-free-shopify-apps-2026/ 301
/blog/shopify-apps-that-are-a-waste-of-money /best-free-shopify-apps-2026/ 301
/blog/shopify-apps-that-are-a-waste-of-money/ /best-free-shopify-apps-2026/ 301
# Truncated pre-April URL (trailing hyphen). GSC Performance, 23 Oct 2026: 48 impressions,
# and it 404ed. Sent to the tool that answers "which apps can I remove?".
/blog/how-to-reduce-shopify-apps- /shopify-app-stack-kill-or-keep-auditor/ 301
/blog/how-to-reduce-shopify-apps-/ /shopify-app-stack-kill-or-keep-auditor/ 301
/blog/free-shopify-apps-replace-paid-2026 /best-free-shopify-apps-2026/ 301
/blog/free-shopify-apps-replace-paid-2026/ /best-free-shopify-apps-2026/ 301

Expand Down
43 changes: 43 additions & 0 deletions tests/gsc-known-urls.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// tests/gsc-known-urls.test.js: every URL Google has shown in search lands on
// a live page.
//
// GSC's Performance export of 23 Oct 2026 listed 61 URLs with impressions,
// most of them www, slashless or retired paths. One, a truncated
// "/blog/how-to-reduce-shopify-apps-" with 48 impressions, resolved to a 404.
// This replays the middleware's order (host to apex, legacy map first, then
// the trailing slash) for every URL in data/gsc-known-urls.json and fails
// unless it ends on a sitemap URL. Subdomains tools./audit. are dashboard
// redirect rules; for those only the path is checked, as on the apex.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { resolveLegacy } from '../functions/_legacy-redirects.js';

const KNOWN = JSON.parse(fs.readFileSync('data/gsc-known-urls.json', 'utf8')).urls;
const SITEMAP = fs.existsSync('dist/sitemap-0.xml')
? new Set([...fs.readFileSync('dist/sitemap-0.xml', 'utf8').matchAll(/<loc>https:\/\/stackarchitect\.xyz([^<]*)<\/loc>/g)].map((m) => m[1]))
: null;
// Deliberately indexable pages outside the sitemap (see CLAUDE.md).
const ALSO_LIVE = new Set(['/sitemap-page/']);

function land(path) {
const hit = resolveLegacy(path);
if (hit && hit.to !== path) return { to: hit.to, hops: 1 };
if (!path.endsWith('/') && !/\.[a-z0-9]+$/i.test(path)) return { to: path + '/', hops: 1 };
return { to: path, hops: 0 };
}

test('every URL Google has shown lands on a live page in at most one redirect', { skip: !SITEMAP && 'needs a build' }, () => {
const bad = [];
for (const { host, path } of KNOWN) {
const { to } = land(path);
const settled = land(to);
if (settled.hops) bad.push(`${host}${path} -> ${to} -> ${settled.to} (chain)`);
else if (!SITEMAP.has(to) && !ALSO_LIVE.has(to)) bad.push(`${host}${path} -> ${to} (not a live page)`);
}
assert.deepEqual(bad, [], bad.join('\n'));
});

test('the 23 Oct 2026 404 is covered', () => {
assert.equal(resolveLegacy('/blog/how-to-reduce-shopify-apps-')?.to, '/shopify-app-stack-kill-or-keep-auditor/');
});
Loading