SmartEcomSEO
Migrations

Shopify Redirects at Scale: Bulk CSVs, Limits and Redirect Chains

Shopify URL redirect rules for big catalogs and migrations: the 100,000 and 20,000,000 limits, CSV imports, why redirects fail when the old URL still loads, query strings and chains.

Illustration for “Shopify Redirects at Scale: Bulk CSVs, Limits and Redirect Chains”
Key takeaways
  • Shopify allows 100,000 URL redirects per store, or 20,000,000 on Shopify Plus, according to its help center.
  • A Shopify redirect only fires when the old URL would 404. If a product, collection or page still loads there, the redirect is ignored.
  • Making a hidden collection visible again automatically deletes the redirect that pointed away from it.
  • Redirect chains form quietly on Shopify when handles change more than once; export the list and flatten every chain to one hop.
  • Redirect old URLs to the closest relevant page, not the home page: Google warns mass home-page redirects may be treated as soft 404s.

Shopify redirects look simple: a "Redirect from" path, a "Redirect to" path, done. At scale they're where migrations lose traffic. A redirect that silently doesn't fire because the old URL still loads, a CSV that imports /Products/Blue-Sofa when the live URL is lowercase, three years of handle changes stacked into chains.

This guide covers the rules as Shopify documents them in September 2026, how to bulk-import thousands of redirects with a CSV, and how to find and flatten chains. We work on these files constantly during Shopify migrations, so the checks below are the ones we actually run.

The rules, in one table#

RuleWhat Shopify's help center saysWhat it means in practice
Limit100,000 URL redirects; 20,000,000 on the Plus planMost non-Plus migrations fit; very large catalogs with many legacy URL formats may need Plus or prioritization
Status codeURL redirects are 301s (the help center's caching note describes them as 301 redirects)Google treats a permanent redirect as a signal that the target should be canonical
Only broken URLs"You can redirect only from broken URLs"If the old URL still loads a page, the redirect is ignored
Fixed pathsYou can't redirect /products, /collections or /collections/all themselves, or paths starting with /apps, /application, /cart, /carts, /orders, /services or /shopPlan legacy URLs that collide with these
Tag pagesCollection tag URLs (/collections/x/tag) can't be redirected, even with no productsHandle them with noindex or a real collection instead
Query stringsURLs with query strings "might not work as expected"Test every parameterized legacy URL
.htmlA URL ending in .html can't be redirected to the same URL without itShopify already serves /products/x.html as /products/x
MarketsA redirect applies to all market subfolders automaticallyCreate separate redirects only for market-specific destinations

The redirect list lives at Content > Menus > URL redirects (Shopify has moved it before, so search the admin for "URL redirects" if it's not there).

Why a redirect "doesn't work": the old URL still exists#

This is the redirect problem we see most after a migration or catalog clean-up. Shopify only fires a URL redirect when the "Redirect from" path would otherwise return a 404. If a product, collection or page still lives at that URL, the page wins.

Situations where this bites:

  • Merging two products. You redirect /products/oak-table-180 to /products/oak-table, but the old product is still active. The redirect is saved and ignored. Set the old product to Draft or remove it from the Online Store channel (Shopify's help center gives both options), then test.
  • Unlisted products. Shopify's Unlisted status keeps a product reachable by direct URL. The URL still loads, so a redirect from it won't fire. Use Draft or delete it instead.
  • Hidden collections. Shopify's help center describes hiding a collection so its URL 404s and a redirect can take over. It also warns that making the collection visible again automatically deletes that redirect. Seasonal collections are where this catches people.
  • Pages recreated by apps. Some apps publish pages at predictable handles. If an app recreates a page you redirected, the redirect stops working.

Test with a HEAD request. A working Shopify redirect returns a 301 and a location header:

bashbash
curl -sI "https://yourstore.com/products/oak-table-180" | grep -iE '^HTTP|^location'
# HTTP/2 301
# location: https://yourstore.com/products/oak-table

If you get a 200, the old URL still resolves to something.

Handle changes and automatic redirects#

When you change a product, collection, page or article handle in the admin, Shopify shows a Create a URL redirect checkbox. Leave it ticked. It creates a redirect from the old handle to the new one.

Bulk handle edits are different. In Shopify's own product CSV, the handle is how Shopify identifies the product, so changing it in a CSV creates a new product rather than renaming the old one. Bulk-editing apps that rename handles usually have their own "create redirects" option. Confirm it's switched on before you run the job, then spot-check the redirect list.

Bulk importing redirects with a CSV#

For a migration or a catalog clean-up, you'll import redirects in bulk. The importer is on the URL redirects screen under Import, and Shopify provides a sample file there. The file uses two columns:

csvcsv
Redirect from,Redirect to
/shop/outdoor-kitchens/grill-islands.html,/collections/grill-islands
/shop/outdoor-kitchens/grill-islands/36-inch-island.html,/products/36-inch-grill-island
/catalog/product/view/id/4412,/products/36-inch-grill-island
/blog/how-to-choose-a-built-in-grill,/blogs/guides/how-to-choose-a-built-in-grill
/brands/acme-grills,/collections/acme-grills

Build the old-URL list from four sources#

A redirect map is only as good as its list of old URLs. A crawl of the old site misses URLs that are no longer linked but still have backlinks or bookmarks. We combine:

  1. A full crawl of the old site before it goes offline.
  2. Search Console's Pages export for the last 16 months, which shows URLs Google actually sent traffic to.
  3. Analytics landing pages for the same period, including paid and email traffic.
  4. A backlink tool's "top linked pages" report, so no linked URL is left to 404.

De-duplicate, lowercase where the old platform was case-insensitive, and map every row that had traffic, links or rankings.

Rules we follow for every import file#

  1. "Redirect from" is a path, not a full URL. Strip the domain, keep the leading slash.
  2. "Redirect to" is relative for same-domain targets. Use a full URL only when the destination is on another domain.
  3. Lowercase everything that's case-insensitive on the old platform. Mixed-case duplicates cause confusing results.
  4. Point every row at a final URL. Never at another old URL, never at something you plan to rename next month.
  5. Point at the closest match, not the home page. A discontinued sauna goes to the sauna collection or its replacement. Google's site-move guidance warns that redirecting many old URLs to one irrelevant page, such as the home page, might be treated as a soft 404.
  6. Import 20 rows first. Test them with curl, then import the rest.
  7. Export before you import. Shopify's Export button gives you a backup of the current list, and the export is your source file for chain checks.

For migrations with hundreds of thousands of legacy URLs (Magento stores with layered navigation are the usual case), don't redirect every parameter combination. Redirect every URL that has traffic, links or rankings, plus every product, category and CMS page. Let pure filter URLs 404. Our Magento to Shopify and WooCommerce to Shopify pages cover the URL patterns for each platform.

Query strings: test, don't assume#

Legacy platforms love query strings: /index.php?route=product/product&product_id=4412, /product.asp?id=88. Shopify's help center only says such redirects "might not work as expected".

Here's what we recommend:

  • Include the exact query string in "Redirect from" when the old URL depends on it, then test each one. Parameter order can matter, so test the URL exactly as it appears in your analytics and backlink data.
  • Where the path alone identifies the page (for example /sale.html?utm_source=x), redirect the clean path and test that the parameterized version follows.
  • Keep a list of failures. For a handful of important URLs that won't redirect natively, a JavaScript redirect on the 404 template is a fallback, not a replacement: Google says it processes JavaScript redirects but recommends server-side redirects where possible.

Redirect chains: how they form on Shopify#

A chain is A → B → C. On Shopify they form quietly:

  1. In 2023 you rename /products/grill-cart to /products/outdoor-grill-cart. The checkbox creates A → B.
  2. In 2025 you merge it into /products/grill-cart-stainless. You add B → C.
  3. A still points at B. Every visit and every crawl of A takes two hops.

After a migration it's worse: legacy URLs redirect to Shopify URLs that later change handle. Google's documentation says its crawlers follow up to 10 redirect hops, so a two-hop chain won't break indexing. But each hop is a chance for an error, every hop adds latency for shoppers, and a chain that ends in a 404 loses whatever links pointed at the start of it. Flatten them.

Find and flatten chains from the export#

Export all redirects, then run this against the CSV. It follows each chain to its final target, reports loops, and writes a corrected file. Third-party tests report that importing a row with an existing "Redirect from" updates that redirect; confirm it with a few rows before re-importing the whole file.

texttext
import csv
from urllib.parse import urlsplit

def norm(u):
    """Reduce a URL or path to a lowercase path+query for matching."""
    p = urlsplit(u.strip())
    path = (p.path or "/").rstrip("/") or "/"
    return (path + ("?" + p.query if p.query else "")).lower()

with open("redirects_export.csv", newline="", encoding="utf-8") as f:
    rows = list(csv.DictReader(f))

target = {norm(r["Redirect from"]): r["Redirect to"].strip() for r in rows}

fixed = []
for r in rows:
    src, dst = r["Redirect from"], r["Redirect to"].strip()
    hops, seen, loop = 1, {norm(src)}, False
    while norm(dst) in target and hops < 10:
        if norm(dst) in seen:
            loop = True
            break
        seen.add(norm(dst))
        dst = target[norm(dst)]
        hops += 1
    if loop:
        print(f"LOOP, fix by hand: {src}")
        dst = r["Redirect to"].strip()
    elif hops > 1:
        print(f"{hops} hops: {src} -> {dst}")
    fixed.append({"Redirect from": src, "Redirect to": dst})

with open("redirects_flattened.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=["Redirect from", "Redirect to"])
    w.writeheader()
    w.writerows(fixed)

Check the column names in your export first; the script assumes Shopify's "Redirect from" and "Redirect to" headers. It also matches on path only, so review any row whose target is on another domain.

Then confirm on the live site. curl reports the number of hops and the final URL:

bashbash
curl -sIL -o /dev/null -w '%{num_redirects} hops -> %{http_code} %{url_effective}\n' \
  "https://yourstore.com/products/grill-cart"

Anything above one hop, or ending in anything but 200, goes on the fix list.

Redirects are for the outside world: old backlinks, bookmarks, search results. Your own site shouldn't depend on them. After any handle change or migration, crawl the store and list every internal link that returns a 301. Usual sources:

  • Main menu and footer links typed in by hand
  • Links inside product and collection descriptions
  • Old blog posts
  • Theme sections with hard-coded URLs (banners, "shop the look")

Update them to the final URL. On big catalogs this is often hundreds of edits, which is why we fix them in bulk as part of technical SEO work.

Discontinued products: redirect or keep?#

High-ticket catalogs rotate models every year, and the default reflex (delete and redirect) isn't always right. Buyers of a $6,000 sauna or a $9,000 pool table search model numbers for manuals, reviews and parts long after the model is gone.

SituationWhat we do
Page has rankings or links, and people still search the modelKeep it live, mark it discontinued, link prominently to the successor
Direct successor exists and the old page has little search demand301 to the successor
No successor, but a relevant collection exists301 to that collection
Nothing relevantLet it 404 (Shopify has no native 410 option)

Whichever you choose, update internal links and remove the old product from collections so shoppers don't land on a dead end.

How long to keep redirects#

Google's site-move guidance says to keep redirects as long as possible, generally at least one year. On Shopify there's no hosting cost to keeping them, so our answer is: keep them. The only reason to delete a redirect is when you want the old URL to become a live page again, and then the page itself takes over anyway.

A redirect checklist for big Shopify catalogs#

  • Export the redirect list quarterly and run the chain check
  • Crawl for internal links that hit a 301
  • Check Search Console's "Page with redirect" and "Not found (404)" reports after every catalog change
  • When merging products, draft or delete the old one before adding the redirect
  • Test every query-string redirect individually
  • Watch your total against the 100,000 limit if you're not on Plus
  • Keep a changelog of handle changes so the next migration has a source of truth

For everything else on a big store, work through the Shopify SEO checklist.

People also ask

Questions people ask about this

How many redirects can a Shopify store have?

Shopify's help center says a store can create up to 100,000 URL redirects, or up to 20,000,000 on the Shopify Plus plan. For migrations, redirect every product, category, CMS page and any URL with traffic, links or rankings, but don't spend the limit on every legacy filter or parameter combination.

Why is my Shopify redirect not working?

The most common reason is that the old URL still loads a page: Shopify only redirects URLs that would otherwise return a 404. Draft or delete the old product, or remove it from the Online Store channel. Also check for typos, reserved paths like /collections/all, tag URLs, query strings, and browser caching of an earlier 301.

How do I bulk import redirects into Shopify?

Go to the URL redirects screen in the admin, choose Import and upload a CSV with two columns, Redirect from and Redirect to. Use paths without the domain in the first column and relative paths for same-domain targets. Import a small test batch first, check it with curl, then import the full file.

Does Shopify support wildcard or regex redirects?

Shopify's native URL redirects are one-to-one: each row maps one path to one target, and the help center documents no wildcard or pattern syntax. For large migrations, generate the full list of old URLs from crawls, analytics and backlink data and map each one. Some redirect apps add pattern matching, so test them carefully.

How long should I keep Shopify redirects after a migration?

Google recommends keeping redirects for as long as possible, generally at least one year. On Shopify there is no cost to keeping them, so we keep them indefinitely. The only reason to remove one is when the old URL should become a live page again, and in that case the new page takes over automatically.

Next step

Want this done on your store?

SEO migration services for brands moving to Shopify or restructuring on it: full crawl, URL-by-URL redirect map, content and schema parity, staging QA and 90 days of monitoring.

Talk to Zack directlyUsually replies within 24 hours
WeChatSearch this ID in WeChat
WhatsApp +86 186 8214 2136