CSV format
The same format is used for both import and export. Round-trip safe — what you export, you can re-import.
Required columns
| Column | Type | Notes |
|---|---|---|
request_path | string | Path to redirect FROM. No leading /, no host. E.g. old-page.html, shop/old-cat |
target_path | string | Path or absolute URL to redirect TO |
Optional columns
| Column | Default | Notes |
|---|---|---|
redirect_type | 301 | Must be 301 or 302 |
store_id | 0 | 0 = all stores / website-level. Otherwise specific store ID |
source | imported | manual / imported / auto_healed / oos_engine |
notes | empty | Free-form |
Path normalisation
Both request_path and target_path are normalised by RedirectManager:
- Leading
/stripped (so/old-page.htmlandold-page.htmlare equivalent) - Absolute URLs (
https://example.com/foo) reduced to path component (foo) - Whitespace trimmed
So you don't need to be pedantic about leading slashes in your CSV — both forms work.
Validation rules
A row is rejected (logged in the import report, not fatal to the run) if:
request_pathis emptytarget_pathis emptyredirect_typeis not301or302request_pathequalstarget_path(would create a loop)
Other validation errors (e.g. malformed paths) bubble up from UrlPersistInterface with their specific message.
Round-trip example
Export from the grid:
request_path,target_path,redirect_type,store_id,source,created_by,batch_id,notes,hit_count,last_hit_at,created_at
old-page.html,/new-page.html,301,1,manual,admin,,Migrated 2026-04-25,42,2026-04-24 18:32:11,2026-04-20 09:15:00
shop/old-cat,shop/new-cat,301,1,imported,admin,import-20260420-091500,,17,2026-04-23 11:08:43,2026-04-20 09:15:00
Re-import the same file → idempotent updates (existing redirects matched by request_path + store_id, sidecar metadata refreshed). Hit_count and last_hit_at columns are present in the export but ignored on import — they're populated by hit-tracking, not by user input.
Recipe — bulk migration from a previous SEO module
If you're migrating off Mageworx / Amasty / a custom redirects module:
- Export their data to a CSV with at least
request_pathandtarget_pathcolumns - Add a
notescolumn withMigrated from <vendor> on YYYY-MM-DDso you have an audit trail - Add a
sourcecolumn with valueimported - Run dry-run first to catch any malformed rows
- Run for real with a meaningful
batch_id
In the admin grid, filter by batch_id to see all your migrated redirects in one view.
Recipe — recreating from Apache rewrites
Convert .htaccess style:
RewriteRule ^old-page.html$ /new-page.html [R=301,L]
RewriteRule ^shop/old-cat/?$ /shop/new-cat [R=301,L]
To CSV:
request_path,target_path,redirect_type
old-page.html,/new-page.html,301
shop/old-cat,/shop/new-cat,301
Regex-based rules (^/products/([0-9]+)/?$ → /shop/products/$1) are NOT yet supported — roadmapped for v2.10.
Recipe — exporting from one store, importing to another
Export from store A's grid → edit the CSV to change store_id from 1 to 2 (or use 0 for website-level) → import on store B (or the same install). The importer creates new rows scoped to the new store.
Encoding & quoting
Standard CSV: comma-separated, double-quote enclosure when needed (paths with commas would need quoting), \n line endings.
Excel-saved CSVs typically work but watch for BOM characters at the start of the file — those can confuse the header-row parser. Save as "CSV (Comma delimited) (*.csv)" not "CSV UTF-8" if you hit issues.