SHORT ANSWER
A Magento store moving to Shopify needs a 301 for every product, category and cms page url, and Magento produces more urls per page than most platforms: the .html suffix, an optional category path that gives one product several addresses, and an optional store code in front of everything. Shopify takes none of that. Products go to /products/handle, categories to /collections/handle, pages to /pages/handle, and because Shopify has no wildcards, every old url is its own row in the csv. The work is finding all of those urls, pairing each with its Shopify target, and flattening the rewrites Magento already has.
What do Magento urls look like?
Four settings decide the shape, and an older store may have changed some of them over the years. Adobe’s documentation gives html as the default suffix for both products and categories, and says the category path is not included in product urls by default (Adobe Commerce catalog urls).
/product-key.html — a product with the default suffix and no category path. The most common form.
/category/sub-category/product-key.html — the same product with Use Categories Path for Product URLs switched on. A product in three categories has three of these, plus the short one.
/category.html · /category/sub-category.html — category pages, nested as deep as the tree.
/en/… · /de/… — the store code in front of every url when Add Store Code to Urls is on, one prefix per store view.
/index.php/… — what every url looks like when web server rewrites are off. Rare on a live store, common in old links.
/about-us · /catalog/product/view/id/123 — cms pages (no suffix by default), and the internal system urls that turn up in logs and Search Console when something once linked past the rewrite.
Which pattern becomes what on Shopify?
| magento pattern | on shopify | how |
|---|---|---|
/product-key.html | /products/handle | one row each, matched on slug and title |
/category/…/product-key.html | /products/handle | one row per category path, same target |
/category.html | /collections/handle | one row each; nested categories flatten |
/about-us | /pages/about-us | one row each |
/de/product-key.html | the locale folder’s product url | one row per store view |
/index.php/product-key.html | /products/handle | only if it still gets traffic |
/checkout/cart/ · /customer/account/ | /cart · /account | shopify routes, usually no row |
?color=blue&size=m | — | query strings, ignored |
/catalogsearch/result/?q=… | /search | no row; search pages should not be indexed |
On another platform you would strip the suffix with one rewrite rule. Shopify’s url redirects are exact path pairs with no pattern of any kind, so that shortcut does not exist: /linen-shirt.html and /products/linen-shirt are two different strings, and the only way to connect them is a row (Shopify url redirects).
EXAMPLE
a store with 900 products, category paths switched on and products in an average of two categories: roughly 2 700 product urls, 120 category urls and 20 cms pages. Every one of them is a row; Shopify’s documented limit of 100,000 redirects per store is far away.
Matching .html urls to Shopify handles
The suffix is not an obstacle for the matcher. Silentfrog compares the last path segment, the page title and the h1 of each old page with every page on the new store, and it drops a file extension before comparing slugs, so linen-shirt.html and linen-shirt count as the same slug. When the migration kept the product name, title and h1 agree too. Products usually come out as fuzzy matches at high confidence. Categories are where the review work is: Magento’s /men/shirts.html and Shopify’s /collections/mens-shirts share a word, not a slug.
A Magento product page with category path carries a canonical tag pointing at its preferred url. The matcher uses the canonical path when it pairs pages, so every category-path variant of a product lands on the same Shopify target, and each variant still gets its own row in the export.
8 pages · 2 exact · 3 fuzzy · 1 need review · 1 unmatched · 2 verified
| ✓ | Match | |||||
|---|---|---|---|---|---|---|
| /kampagne/sommer-2023Sommeraktion 2023 — 20 % auf alle Zangen | unmatched | 940 | 31% | 200 | ||
| /produkte/seitenschneiderSeitenschneider 160 mm | review | 612 | 72% | 200 | ||
| /blog/relaunch-checklisteDie Relaunch-Checkliste | fuzzy | 421 | 88% | 200 | ||
| /blog/ladezeitLadezeit verbessern | fuzzy | 143 | 91% | 200 | ||
| /blog/301-vs-302301 oder 302? | fuzzy | 305 | 92% | 200 | ||
| /ueber-unsÜber uns | manual | 188 | 100% | 200 | ||
| /produkte/kombizangeKombizange 180 mm | exact | 274 | 100% | 200 | ||
| /kontaktKontakt | exact | 96 | 100% | 200 |
the results table: old paths on the left, matched new paths on the right, each row badged exact, fuzzy, needs review, manual or unmatched, sorted worst confidence first.
A directory rule does not help much here. It would rewrite /men/ to something else while keeping the rest, linen-shirt.html included, and no Shopify url ends in .html. The live preview would show every one of those targets as missing. Let the matcher pair the urls one by one and review what it is unsure of (how matching works).
Store views and store codes
Each store view of a multi-language Magento store is a separate set of urls, and each needs its own redirects. With store codes in the url that is visible at a glance, /en/… and /de/…; with one domain per store view it is a separate crawl per domain.
- The main language usually moves to the root of the Shopify store, so
/en/linen-shirt.htmlbecomes/products/linen-shirt. - Translated languages live in locale folders on Shopify. Which folder exactly depends on how markets and languages are set up, so read it off the new store rather than guessing, and make sure those languages are published before you crawl.
- Translated Magento url keys (
/de/leinenhemd.html) usually do not match Shopify’s handle, which stays the same across languages unless it is translated too. Expect these rows in needs review, and pick them by title.
The url rewrites Magento already has
Magento creates a permanent redirect automatically when a product’s url key changes; the box Create Permanent Redirect for old URL is ticked by default (Adobe Commerce url rewrites). A store that has been running for years holds thousands of these in the url rewrites grid under Marketing, SEO & Search, and some of the old urls still get traffic from links nobody updated.
Do not copy them into Shopify as they stand. A Magento rewrite sends /old-shirt.html to /linen-shirt.html; your new map sends /linen-shirt.html to /products/linen-shirt. Imported together, the first url takes two hops — slower for visitors, and Google advises redirecting straight to the final destination. The row you want is /old-shirt.html,/products/linen-shirt.
Adobe’s documentation describes no export for that grid; a developer can dump the url_rewrite table instead. Filter it to the rewrites that are redirects, take the source paths, keep the ones Search Console still shows, and paste them into Silentfrog’s url list. They are then crawled like every other old url and resolved against the new store directly.
EXAMPLE
the rewrite table has 6 400 redirect rows from eight years of renamed products. 310 of their sources had clicks in the last 16 months; those go into the url list, the rest stay behind.
Getting the complete list of old urls
- The sitemap. Magento generates it under Marketing, SEO & Search, Site Map, often at
/sitemap.xmlor under/pub/. Look in robots.txt for the actual location; Silentfrog reads robots.txt and every sitemap it names. - Search Console. The pages report holds the category-path variants and discontinued products that the sitemap no longer lists.
- The rewrite sources from the section above.
- Server logs, if the store has them: the fastest way to see which
/index.php/and/catalog/product/view/urls are still requested.
Filtered urls with query strings are not separate pages for the tool, so layered navigation does not inflate the crawl (finding the urls nothing links to).
Doing it in one pass
- Export the Search Console pages report and get the rewrite sources while Magento is still running.
- Take the storefront password off the new Shopify store for the duration of the crawl; a crawler cannot pass it.
- Start a run with the Magento domain as the old site and the
myshopify.comdomain as the new one, and paste the extra urls into the url list (starting a run). One run per store view domain, if each language has its own. - Sort by confidence, re-map the categories and translated urls that went to the wrong target, and verify what you accept.
- Download the redirects file with shopify picked and import it under Content, Menus, URL redirects (importing into Shopify). Delete or unpublish anything that exists at an old path: Shopify only redirects urls that would otherwise 404.
- After the domain moves, crawl the old url list against the live store. Every row: one 301, then 200.
site settings → publishing → 301 redirects, or the data api
the two export buttons — webflow csv and generic csv — next to the option that includes needs-review matches.
Try the first 50 pages free to see how the matcher handles your catalogue. A Magento store with category paths is almost always past any small limit, and the Unlimited plan at 150 € per relaunch removes the plan limit. The hosted version still crawls at most 2000 pages per site, so a bigger catalogue belongs in the desktop app, which crawls up to 100,000. Only the old site’s pages count.
In short
- Shopify has no wildcards, so no rule can strip .html or a store code: every Magento url is its own row.
- Category-path product urls are extra urls for the same product; each gets a row to the one /products/ target.
- Each store view is its own set of urls; translated languages go to the new store's locale folders.
- Resolve Magento's old url rewrites against the new store directly, or they turn into chains Shopify cannot follow.
- Use the matcher, not directory rules: slugs and titles pair products well, categories need a look.