Skip to content
Subset

For developers

Developer reference

Where a rule can be overridden in PHP, what the CSV and server-config exports look like, and why matching is not pluggable.

Overriding a match

One filter, at the moment a request has been matched and before the redirect is sent. It receives the match and may change the destination, change the status code, or cancel the redirect entirely by returning null.

<?php
// PLACEHOLDER — filter name and match array shape are not final.
add_filter(
	'subset_redirector_match',
	function ( ?array $match, string $request_path ): ?array {
		// Logged-in editors see the real 404, so broken links get noticed.
		if ( current_user_can( 'edit_posts' ) ) {
			return null;
		}

		return $match;
	},
	10,
	2
);

That example is the integration people actually want, and it is worth spelling out why: a site with a thousand redirects hides its own rot. Letting editors see the 404 is how the rot stays visible to the people who can fix it.

Planned for 1.0.0Changelog

A match filter is in the free version. Automatic redirects on slug change, and the chain and loop health check, are Pro.

CSV import and export

The import format is the export format, so a round trip is lossless and a spreadsheet is a legitimate editing tool.

source,destination,status,match_type,notes
/old-page,/new-page,301,exact,
/blog/2019/(.*),/archive/$1,301,regex,Old dated permalinks
/discontinued-widget,,410,exact,No replacement

A 410 row has an empty destination, which is the one thing about the format worth knowing in advance: 410 is not a redirect, so there is nowhere to go.

Exporting to the web server

Rules that fire constantly should not fire in PHP. Hot rules render as server configuration so they are handled before WordPress boots:

# PLACEHOLDER — the exact generated form is not final.
# nginx
rewrite ^/old-page$ /new-page permanent;

# Apache
RewriteRule ^old-page$ /new-page [R=301,L]

The generated block is meant to be copied into your own configuration, not written there by the plugin. A plugin that edits .htaccess in place is a plugin that can take a site down from a settings screen, and WordPress core’s own experience of doing exactly that is the argument.

What will deliberately not be extensible

Matching is not pluggable. The order in which exact, prefix and regex rules are tried is fixed, and there is no filter for it. Redirect matching is on the request path for every 404 on the site, and a hook there is a hook in the hot path — plus a site where the match order is custom is a site where nobody can reason about which rule won.

The 404 log is not a public table. It is aggregated, pruned and will change shape. Read it through an export endpoint (placeholder — not final) when one exists, and if you need an event when a miss is recorded, that is a reasonable request — open an issue.

Testing redirects

The test that catches real regressions asserts the status code as well as the destination, because a rule that silently becomes a 302 still passes a test that only looks at where it ended up:

curl -sS -o /dev/null -w '%{http_code} %{redirect_url}\n' https://example.com/old-page

Expect 301 https://example.com/new-page. Note -o /dev/null and no -L: following the redirect is how you miss the fact that there were three of them.