Skip to Content

Redirect mapping

StagingRedirect is the simplest staging shape — two fields. Every output row creates a Shopify URL redirect.

You usually don’t need to map this by hand. Graftport can create redirects for you from the address changes between your old store and Shopify. See 301 redirects. Map the redirect resource only when you need redirects that aren’t derived from product or collection address changes.

Quick reference — minimum loadable redirect

{ "original_id": $string(redirect_id), "path": "/" & url_key & ".html", "target": "/products/" & handle }

The two fields

pathstringrequired

The source path — where the customer lands. Must start with / and be relative to the destination shop’s domain.

"path": "/" & old_url_key & ".html"
targetstringrequired

The destination path or full URL. Two flavors:

  • Relative path on the same shop: /products/the-new-handle
  • Absolute URL to anywhere: https://help.example.com/migrated
"target": "/products/" & $lowercase(handle)

Patterns

Source product URL → Shopify product handle

{ "original_id": $string(product_id), "path": "/" & url_key & ".html", "target": "/products/" & $lowercase(url_key) }

Source category URL → Shopify collection

{ "original_id": $string(category_id), "path": category_url_path, "target": "/collections/" & collection_handle }

Source CMS page → Shopify page

{ "original_id": $string(page_id), "path": "/" & identifier, "target": "/pages/" & identifier }

Drop identity redirects

If your source URL and Shopify URL happen to match, don’t create a self-loop:

$lowercase(url_key) != handle ? { "original_id": $string(product_id), "path": "/" & url_key, "target": "/products/" & handle } : null

Returning null from a mapping skips the row — no redirect created.

External redirects (discontinued products → manufacturer)

{ "original_id": $string(product_id), "path": "/discontinued/" & old_slug, "target": "https://manufacturer.example.com/" & $lowercase(name) }

Bulk redirects from a sitemap dump

If you’ve extracted the source’s sitemap into a flat list of URLs, use source_url_path from the product/collection/article migrations as the source side:

{ "original_id": $string(product_id), "path": source_url_path, "target": "/products/" & handle }

This is the cleanest pattern — every product/collection/article already carries its source_url_path from extract; the redirect mapping just turns those into redirect rows.

Gotchas

Run product / collection / article loads first. The target paths typically reference Shopify handles created by those loads. Loading redirects against missing targets produces 404 chains.

  • path must start with /. Without the leading slash the redirect is rejected.
  • path and target can’t be equal — Shopify rejects self-loops. Use the conditional null pattern to skip identity redirects.
  • Wildcards aren’t supported. Each redirect is one specific path. To handle a pattern, expand it in the mapping (one row per matched path).
  • Query strings are stripped. A redirect for /old?ref=email matches /old regardless of query string. Don’t try to encode query-string variations as separate redirects.
  • Volume. Shopify allows up to ~100,000 URL redirects per shop. For larger catalogs, prioritize high-traffic URLs (use the source platform’s analytics to rank).
  • Case sensitivity. Shopify normalizes paths to lowercase before matching. Mixed-case path values are folded automatically.
  • original_id should be a string. Source platforms often use integer IDs for redirect rules — wrap with $string() to satisfy the staging model.
Last updated on