Markus Michalski FAQ

Sylius SEO Suite Pro: Redirects

Redirects in Sylius SEO Suite Pro: 301/302, chains and loops, automatic redirects on slug change, 404 monitoring, CSV import and export, console commands and settings.

Deutsche Version

Back to overview

Sylius SEO Suite Pro: Redirects

Redirects keep rankings and backlinks intact when a URL changes. A redirect answers a request for an old path with a 301 or 302 to the new one. You maintain them by hand, they are created automatically when a slug changes, and you can import and export them as CSV. On top of that, 404 monitoring counts which paths lead nowhere.

Overview

A redirect in detail

Field Description
Old URL path (source) The requested path including the shop's locale prefix, for example /en_US/products/old-name. Normalized on save: leading slash, no host, no query string, no fragment, no trailing slash (except for the root /). A pasted full URL is cut down to its path. Up to 255 characters.
Target (target) A path on the shop (starts with exactly one /) or an absolute http(s):// URL. Nothing else. Up to 2048 characters.
Redirect type (status) 301 (default) or 302. Other values are rejected.
Channel A single channel, or empty for all channels.
Active A disabled redirect is kept but not served, and it takes no part in loop checks.

There is exactly one redirect per path and channel. A redirect for the visitor's channel wins over one for all channels.

Paths are not case-sensitive: /Sofa and /sofa are the same path. Nothing else is folded together, /über and /uber or /straße and /strasse stay different paths.

The list also shows the hit counter, the time of the last hit and the origin (Manual, Automatic on slug change, Import).

When a redirect runs

graph TD
    A["Storefront request"] --> B{"Does the shop have a page?"}
    B -->|"Yes"| C["Page is served normally"]
    B -->|"No, 404"| D{"GET or HEAD, no admin, no API, no path starting with underscore?"}
    D -->|"No"| E["Plain 404 page, nothing is counted"]
    D -->|"Yes"| F{"Active redirect found for path and channel?"}
    F -->|"Yes"| G["Increase hit counter"]
    G --> H["301 or 302 to the target, query string is appended"]
    F -->|"No"| I{"404 log enabled?"}
    I -->|"Yes"| J["Count the path in the 404 log, then plain 404 page"]
    I -->|"No"| K["Plain 404 page"]

Only storefront GET and HEAD requests are handled. These are left alone:

Cost per request:

Case Database access
A normal, existing page none
A 404 with a redirect one lookup and one UPDATE (hit counter, last hit)
A 404 without a redirect one lookup, plus counting in the 404 log

If the redirect table is missing, the lookup fails or a stored target is invalid, the visitor gets the normal 404 page. The error is logged, there is never a 500.

Query string

The request's query string is appended exactly as the visitor sent it when the target is a path without a query string of its own. Campaign parameters survive the redirect:

Request Target Redirects to
/en_US/old?utm_source=mail /en_US/new /en_US/new?utm_source=mail
/en_US/old?utm_source=mail /en_US/new?ref=1 /en_US/new?ref=1 (own query is kept)
/en_US/old?utm_source=mail https://example.com/new https://example.com/new (an absolute target receives nothing)

Non-ASCII characters in the target (umlauts, a stray space) are percent-encoded in the Location header.

Browsers cache a 301 and search engines pass it on. Use 302 for anything that is only temporary.

Chains and loops

On save in the admin and during import, the plugin checks and flattens:

graph LR
    A["Before: /a"] -->|"301"| B["/b"]
    C["After: /a"] -->|"301"| E["/c"]
    D["After: /b"] -->|"301"| E

The example shows a new redirect from /b to /c while /a already points at /b: /b now points at /c, and the existing redirect from /a is bent to /c too. The visitor only ever needs one hop.

Flattening is a snapshot taken at save time. If you change a redirect's target later, redirects that point at it keep the old target. A redirect for all channels is only flattened through other redirects for all channels. If it points at a path that a single channel redirects elsewhere, visitors in that channel make two hops.

A redirect for all channels is checked for loops against the redirects of every channel. If one channel has /b to /a, then /a to /b for all channels is a form error.

Automatic redirects on a slug change

When the slug of a product or category translation changes, the plugin automatically creates a 301 from the old to the new URL (origin Automatic). This applies to every changed locale and every channel in which the page is shown and that offers the locale. A product counts in its own channels, a category in all channels. The shop router builds the paths from the routes sylius_shop_product_show and sylius_shop_product_index, so the locale prefix of your routes is respected.

Example: you change a product's slug from old to new. It is sold in the channels web and outlet, both offer en_US. Two automatic redirects now exist:

Source Target Status Channel Origin
/en_US/products/old /en_US/products/new 301 web Automatic
/en_US/products/old /en_US/products/new 301 outlet Automatic

The exact spelling of the paths depends on your shop's routes. If a redirect from /en_US/products/older to /en_US/products/old already existed, it is bent to /en_US/products/new so the visitor stays at one hop.

To switch it off:

# config/packages/mmd_sylius_seo_suite_pro.yaml
mmd_sylius_seo_suite_pro:
    redirects:
        auto_on_slug_change: false

The details:

Slugs changed by a DQL or SQL bulk update or by direct SQL do not trigger Doctrine listeners and create no redirect. Renaming thousands of products in one flush writes that many rows. That is a one-off cost of the change itself, not a cost of page views.

404 monitoring

A 404 without a matching redirect is counted per path and channel: path, number of requests, first and last time and the last referrer (shortened to 255 characters). The 404 log screen lists the paths, most requested first.

Growth is bounded:

Remove old rows with a cron job:

bin/console mmd:seo-suite-pro:redirects:prune-log            # not seen for 90 days
bin/console mmd:seo-suite-pro:redirects:prune-log --days=30
# Crontab: daily at 03:30
30 3 * * * cd /var/www/shop && bin/console mmd:seo-suite-pro:redirects:prune-log --days=60

--days must be a whole number, zero or more. Switch counting off with redirects.log_not_found: false. If the table has not been migrated yet, counting is skipped silently.

From the 404 log, the row action Create redirect opens the redirect form with path and channel prefilled. As soon as you save a redirect for the path, the row disappears from the log. The audit dashboard additionally shows the ten most requested 404 paths without a redirect, see SEO audit.

CSV import and export

The format has four columns, the first row names them:

source,target,status,channel
/en_US/products/old-name,/en_US/products/new-name,301,
/en_US/old-page,https://example.com/new-page,302,shop
/en_US/summer-sale,/en_US/sale,,outlet
Column Required Description
source yes Old path, normalized as in the form
target yes Target path or absolute http(s) URL
status no 301 or 302. Empty means 301. Anything else is a row error.
channel no Channel code. Empty means all channels. An unknown code is a row error.

The import is tolerant about what spreadsheets produce:

Every row goes through the same validation as the admin form, including the chain and loop rules, and is saved on its own. A later row therefore sees the earlier ones, and one bad row never costs the others. Bad rows are reported with their line number and skipped. Imported redirects have the origin Import.

Existing redirects are not overwritten. A row for a path and channel that already exists is an error. Delete or edit the existing redirect first.

Limits: 1 MB file size and 2000 data rows. A larger file is refused as a whole. From row 2001 the import stops with a message, the rows read up to then stay saved. If the header lacks source or target, nothing is imported.

Using import and export

Admin

  1. Open SEO Suite Pro > Redirects.
  2. Click Import and choose the CSV file.
  3. After submitting, the screen shows the number of imported redirects and a list of rejected rows with line numbers.
  4. The Export button downloads the file redirects.csv.

Console

bin/console mmd:seo-suite-pro:redirects:import redirects.csv
bin/console mmd:seo-suite-pro:redirects:export redirects.csv
bin/console mmd:seo-suite-pro:redirects:export > redirects.csv

The import prints every rejected row as line N: ... and reports at the end how many redirects were imported and how many lines were refused. If at least one line failed, the command exits with an error code, the valid lines are saved anyway. Without a file argument, the export writes to standard output.

The export contains only source, target, status and channel, and it contains all redirects, disabled ones included. The active switch, the origin and the hit counter are not exported. A re-import makes every redirect active.

Admin screens

The plugin has a single sidebar entry (group MMKreativ Plugins > SEO Suite Pro). You reach all other screens through buttons:

Screen Reached via Actions
Redirects SEO Suite Pro > Redirects button Create, edit, delete, bulk delete
404 log Redirects > 404 log button Create redirect (prefilled), delete, bulk delete. No create or edit.
Import Redirects > Import button Upload CSV
Export Redirects > Export button Download CSV

The redirect list can be searched by source and target and filtered by channel (rows for all channels included), origin and active. You can sort by hits and last hit, among others. The 404 log is searched by path and referrer and filtered by channel. By default it is sorted by the number of requests, descending.

The lists are Sylius grids (mmd_sylius_seo_suite_pro_plugin_admin_seo_redirect and mmd_sylius_seo_suite_pro_plugin_admin_seo_not_found_log).

Console commands

Command Purpose Options
mmd:seo-suite-pro:redirects:export [file] All redirects as CSV file optional, otherwise standard output
mmd:seo-suite-pro:redirects:import file Import CSV, same validation as in the admin file required
mmd:seo-suite-pro:redirects:prune-log Delete old rows from the 404 log --days / -d, default 90

Put prune-log into a cron job, export and import are manual tools.

Settings

All keys live under mmd_sylius_seo_suite_pro.redirects. More on configuration is on the Configuration page.

Setting Default Description
redirects.auto_on_slug_change true Automatically creates a 301 when a product or category slug changes
redirects.log_not_found true Counts paths that end in 404 and have no redirect
redirects.not_found_log_limit 5000 Upper limit of rows in the 404 log (at least 1)
# config/packages/mmd_sylius_seo_suite_pro.yaml
mmd_sylius_seo_suite_pro:
    redirects:
        auto_on_slug_change: true
        log_not_found: true
        not_found_log_limit: 5000

Security

Shop API / Headless

Redirects are an HTTP-level concern, like robots.txt: they answer the request before any page is rendered. The listener handles storefront requests only, the API (/api) is excluded, and there is no API resource for redirects. A headless frontend on another host has to handle its own 404 pages, because redirects are only looked up on the path of the shop's own host. You can read them out through the CSV export and carry them into the frontend.

Troubleshooting

Symptom Cause and solution
The redirect does not trigger The path exists in the shop as a real page, redirects only apply to a 404. Also check Active, channel and the spelling including the locale prefix.
Import row rejected, already exists Path and channel already exist, existing redirects are not overwritten.
Form error about a loop The chain leads back to the source, see Chains and loops.
No redirect after a slug change Automatic redirects are switched off, the channel does not offer the locale, or the slug was changed by an SQL bulk update.
The log stays empty redirects.log_not_found is off, the path is asset-like, or the migration is missing (mmd:seo-suite-pro:setup).

More help: Troubleshooting and Technical details.