Free for Craft CMS 5
Every dead URL has a friend
A 404 is usually a visitor holding a URL that used to work. The slug moved, or the page went and its parent survived. Friend works out which, finds the entry they were after, and sends them there — but only when the match clears a threshold you set, and with a log of every guess it made.
See exactly why
The tester — in the control panel or on the command line — resolves a URL as though it had just 404'd and shows the whole decision: every rule, why it matched or declined, and each candidate's score broken into slug, title and path. Nothing is cached, logged or redirected.
$ php craft friend/match/test blog/2019/our-new-offices
blog/2019/our-new-offices
site: Main slug: our-new-offices tokens: blog, 2019, our, new, office
skipped Ignore probes — The URI does not match `re:^wp-`.
matched Similar entries (best 92.3) — Redirecting to news/our-new-office.
Candidates
92.3 /news/our-new-office slug 0.87 title 1.00 path 0.00 [slug,tokens,search]
41.0 /news/office-move slug 0.44 title 0.40 path 0.00 [search]
→ 302 https://example.com/news/our-new-office
Features
Retrieval for recall, scoring for precision, and a threshold between them — not whatever the database happened to return first.
Rules, in order
Each rule says when it applies, which elements it may point to, and how sure it must be. The first rule that decides wins — and a rule that finds nothing above its threshold has not decided, so the next one gets a turn.
- Redirect, offer suggestions, or match and deliberately do nothing
- Limit by site, URI pattern, segment count, section, entry type or category group
Four ways to find a candidate
Exact slug, word overlap on slug and title, Craft's own search index, and the nearest surviving ancestor page — in any combination, per rule.
- /blog/our-new-office finds /news/our-new-office
- /services/seo-audits falls back to /services
A score you can read
Every candidate is re-ranked as a weighted blend of slug, title and path similarity. The weights are yours, and the threshold — 55 by default — is the whole safety mechanism.
A 404 log worth opening
One row per site and URI with a hit counter, so the top of the list is what is actually broken. Each row records the target chosen, its score, the rule that chose it, and whether the visitor was moved.
- Pruned by age and by row count during garbage collection
Pins for the one it gets wrong
Press Pin on a log row and that answer is stored, checked before any rule, for the cost of one indexed lookup — fixing one URL without making the rules worse for every other.
Guards that never move
Site GET and HEAD requests only — never the control panel, an action request, a preview or the homepage. Scanner paths and missing images, scripts and stylesheets stay 404, and no URL is ever sent back to itself.
- Ignored patterns and extensions are editable
- Stands down when config/redirects.php already covers the URL
Or just suggest
Set a rule to Suggestions — or let one decline — and the 404 stands, with a ranked "did you mean" list for your error template. Every tag takes an optional URI, so it works on any page.
{% set suggestions = craft.friend.suggestions(5) %}
{% if suggestions %}
<h2>Did you mean…</h2>
<ul>
{% for candidate in suggestions %}
<li><a href="{{ candidate.url }}">{{ candidate.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
Frequently Asked Questions
The questions worth answering before you install it.
Yes. There are no editions, no Pro tier and no licence key. It is licensed under the Craft License.
Yes — the Similar entries rule is on, and redirects with a 302 when the best match scores 55 or more. If you would rather look first, switch Look for friend off in Friend → Settings, or disable the rule, and use the tester until you trust it.
A 301 is cached by browsers and everything in between, often for far longer than anyone intends — so a guessed permanent redirect that guesses wrong keeps sending visitors to the wrong place after you fix the rule. Friend defaults to 302, and a rule can opt into 301 once the log has convinced you it gets the answer right.
Friend runs before Craft reads that file, so it checks it first and stands down when one of your rules there already matches. Explicit beats inferred. There is a setting for the opposite if you want it.
No. Missing files with an asset extension — images, stylesheets, scripts, fonts, archives — are guarded, because redirecting them turns a visible 404 into an invisible wrong-content-type bug. The list is editable.
Resolved outcomes are cached per URL for an hour by default, so a dead link getting hammered pays for the search once. Candidates are scored from a handful of columns and no element is loaded on the way, and saving a rule or a pin clears the cache.
Craft CMS 5.3+ and PHP 8.2+. No build step, and no dependencies beyond Craft's own.
Free, and ready for the next 404
One composer command, no licence key, and a sensible rule already switched on. Open the tester, try a few dead URLs, and see what it would do.