How to bulk-fix broken internal links across Shopify articles
Internal links rot. You changed your domain. You restructured collections. You deleted a handful of articles last quarter. A product URL changed because the handle was renamed. Each of those events leaves a trail of dead links scattered through the rest of your blog, and the longer the archive, the more of them there are.
You can find them one at a time using a crawler and fix them one at a time in the Shopify admin — opening each article, locating the bad link in the body, editing it, saving. For a hundred broken links across an archive that’s hours of repetitive clicking. This guide covers the workflow for handling the whole sweep at once.
The common breakage patterns
A few specific situations that produce a flood of internal 404s on a Shopify store:
- Domain change. Old links written as full URLs (
https://old-domain.com/blogs/news/...) instead of relative paths (/blogs/news/...). After the cutover they all 404 — or worse, redirect through a 302. - Collection restructure. You merged
/collections/coffee-beansand/collections/whole-beaninto a single/collections/coffee, and every article that linked to one of the old collections is now broken. - Deleted articles. An article you cited from five other articles is now gone, and the links to it are dead.
- Product handle changes. A product was renamed (
/products/v60-dripper→/products/v60-pourover-dripper). Specter doesn’t read or edit products, but every article body that links to the old product URL is text in a markdown file — and that text Specter very much can edit. - Theme path changes. Pages moved (
/pages/about-us→/pages/about) and the old paths now 404.
All of these are find-and-replace problems at heart. They’re miserable in the Shopify admin because the admin has no global find-and-replace across articles. They’re trivial in a folder.
Step 1: get every article into a folder
Connect Specter to your store via the OAuth flow, pick a sync folder, and every article in every blog on the store comes down as a .md file. The links in the article body are plain markdown — [link text](url) — which means they’re searchable, greppable, and editable like any other text.
Step 2: find the dead links
Two approaches, depending on how thorough you want to be.
Quick: search for known-bad URLs. If you know the specific things that broke — old domain, old collection paths, deleted article URLs — search the folder for them. From the terminal:
grep -rn "old-domain.com" .
grep -rn "/collections/coffee-beans" .
grep -rn "/products/v60-dripper" .
That gives you a list of every file and line where the bad URL appears. It’s enough to drive a targeted replace.
Thorough: crawl, then map. Run a link checker against your live site to get a complete list of internal links that currently 404. There are many — linkchecker, wget --spider, online services. Take the resulting list of dead URLs and use it as the input to your sweep.
Either approach gives you the same shape of input: a list of “find this URL, replace with that URL” pairs.
Step 3: run the swap
For a small number of well-defined replacements, a script does the job cleanly. A handful of sed invocations or a five-line Python script that reads every .md file and runs str.replace for each pair.
For more nuanced cases — where some old URLs should redirect to different replacements depending on context, or some dead links don’t have a good replacement and should be removed entirely — hand the folder to Claude, ChatGPT, or whichever AI tool you prefer (the Claude workflow on Shopify covers the setup). A prompt that works:
For every
.mdfile in this folder, do the following:
Find every markdown link
[text](url)where the URL matches any of the following old URLs:
https://old-domain.com/...→ rewrite to the corresponding relative path (drop the domain and protocol).
/collections/coffee-beansand/collections/whole-bean→ rewrite to/collections/coffee.
/products/v60-dripper→ rewrite to/products/v60-pourover-dripper.
/blogs/news/discontinued-article-handle→ there is no replacement; remove the link but keep the link text inline.
- Do not modify any other links. Do not modify the link text (except where instructed to remove the link entirely). Do not modify the frontmatter. Do not modify any other body content.
Output the modified files in place. List, per file, which links were changed and from what to what.
The instructions to not modify anything else matter a lot here. AI tools left to their own devices will helpfully “improve” links you weren’t asking about, change anchor text, or “fix” links they think look wrong but actually work. Constrain the job tightly.
Step 4: preview the diff before it touches Shopify
Run the dry-run preview in Specter before pushing. The preview shows you, article by article, exactly which files will be updated and the body diff for each. For a link-fix sweep done right, you’ll see only the URL strings inside link syntax changing — anchor text untouched, surrounding paragraphs untouched, frontmatter untouched.
If you see anything else — rewritten sentences, edited headings, modified meta descriptions — back it out. That’s drift from the original instructions, and the whole point of running the preview is catching it before it lands.
When the diff is clean, push. Specter sends the updates to Shopify over the Admin API, batched and rate-limited.
Step 5: re-check after the push
Once the push completes, re-run your link checker. Anything that still 404s is either a link you missed in the map or a genuinely external link you can’t fix from here. The first you handle with a second sweep using the missed pairs; the second you handle by removing or replacing the link manually.
A note on Shopify-redirects vs link-rewrites
Shopify has a redirect feature (Settings → Apps → URL redirects) that lets you redirect old URLs to new ones at the server level. That’s the right answer for visitor traffic — anyone hitting an old URL ends up at the new page automatically.
But redirects don’t fix the links in your article bodies. The link still points to the old URL, still consumes a redirect hop, and still shows up as “broken” in some link checkers. For a clean archive, you want both: Shopify-level redirects to catch the inbound traffic, and link-rewrites in the article bodies so the source of truth is correct.
This guide handles the second half. The first half is a Shopify admin job and stays outside Specter’s scope — Specter only requests read_content and write_content, which covers articles, blogs, and pages, and that’s the scope this whole workflow operates inside. Full breakdown is in the permissions guide, and the broader bulk-edit toolkit is in the bulk-SEO guide.
The point of this workflow isn’t “automate link maintenance forever.” It’s removing the friction that makes you skip a sweep entirely. A clean internal-link graph helps both readers and crawlers; the only reason most blogs don’t have one is the admin makes it tedious to maintain. Operating on the folder makes it a one-evening job instead of a one-week one.