Skip to content

Redirects & Error Tracking#545

Merged
duncanmcclean merged 70 commits into
7.xfrom
redirects
Apr 29, 2026
Merged

Redirects & Error Tracking#545
duncanmcclean merged 70 commits into
7.xfrom
redirects

Conversation

@duncanmcclean
Copy link
Copy Markdown
Member

@duncanmcclean duncanmcclean commented Apr 23, 2026

This PR adds redirects to SEO Pro, allowing you to create manual redirects, auto-redirects when slugs change and 404 error tracking.

Redirects

Redirects can be created and managed in the Control Panel under Tools > SEO Pro > Redirects. Each redirect has a source URL, destination, response code and can be enabled or disabled.

You can even use wildcards in your source URLs. Each * captures a segment, which can be referenced in the destination with $1, $2, etc:

  • Source: /blog/* → Destination: /articles/$1
  • Source: /blog/*/posts/* → Destination: /articles/$1/entries/$2
CleanShot 2026-04-23 at 18 24 14

Automatic Redirects

When enabled, SEO Pro will automatically create redirects when an entry or term's slug changes. This can be scoped to specific collections or taxonomies in the config.

If a redirect exists for the old URLs, its destination will be updated rather than creating a duplicate.

Error Tracking

When enabled, 404 errors are recorded with hit counts and timestamps. Errors can be viewed from the Control Panel, and each error has a quick link to create a redirect for that URL:

CleanShot 2026-04-23 at 18 32 12

When a redirect is created, the associated error will be automatically deleted.

SEO Pro automatically purges errors older than 30 days via a scheduled command. This threshold can be changed in the seo-pro.php config file.

A dashboard widget is also available to surface recently hit errors.

Multi-site

Redirects and errors are scoped to individual sites.

Source URLs are stored relative to the site root (eg. /about, not /de/about). The source field uses a custom fieldtype which prepends the site's domain/path as a visual prefix, and strips pasted domains.

Listings are automatically filtered to the selected site.

Storage

Redirects and errors are stored as YAML files by default. Redirects live in content/seo-pro/redirects and errors in storage/statamic/seopro/errors.

Both can optionally be moved to the database by setting 'driver' => 'database' in the config then running the corresponding publish command:

  • Redirects: php please seo-pro:database-redirects
  • Errors: php please seo-pro:database-errors

The commands publish migrations and import existing file-based data. Redirects and errors can use different drivers independently.

duncanmcclean and others added 19 commits April 24, 2026 11:42
A 410 status means the resource is permanently gone — it should
return a plain 410 response with no Location header. Previously,
all response codes (including 410) went through redirect(), which
set a Location header. The destination field is also now optional
when 410 is selected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These columns are queried together on every 404 hit (exact match
lookup, error recording, uniqueness checks) so they benefit from
a composite index as redirect/error counts grow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a WHERE source LIKE '%*%' clause so only wildcard redirects
are loaded into memory, rather than fetching all enabled redirects
and filtering in PHP. This keeps the result set small as the total
redirect count grows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the config check to registration time so the four event
listeners aren't attached at all when automatic redirects are
disabled, removing the per-handler checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
410 is semantically different from a redirect — it means the
resource is permanently gone, not that it moved somewhere. Keeping
it in the redirect manager creates awkward UX (destination field
with no destination) and special-case handling. Users who need 410
responses can handle that in their own middleware.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The registration-time guard in the service provider is an
optimization, but the subscriber also needs runtime checks so
that disabling via config mid-process (e.g. in tests) is
respected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The service provider already guards subscriber registration behind
the config check, so the runtime checks and their corresponding
tests are unnecessary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@duncanmcclean duncanmcclean marked this pull request as ready for review April 27, 2026 17:06
Copy link
Copy Markdown
Member

@jackmcdade jackmcdade left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a few lang tweaks and removing the 307/308s, all of this is good to go in my book! The "Test Redirect" thing we chatted about in Slack was the only add, but I don't feel like it warrants a "Request changes" status as we could ship without it.

Nice work! 👏

@duncanmcclean duncanmcclean merged commit 99b792b into 7.x Apr 29, 2026
14 checks passed
@duncanmcclean duncanmcclean deleted the redirects branch April 29, 2026 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants