Search by

no404 / laravel

ropi

no404 for Laravel: real server-side 301/302 redirects for your application's 404s, matched against your live catalogue.

v1.0.0 2026-09-12 20:37 UTC

This package is auto-updated.

Last update: 2026-09-12 20:48:09 UTC


README

Turns your application's 404s — deleted products, renamed categories, old campaign links — into real server-side 301/302 redirects to the closest live page on your site.

What no404 is

no404 watches the 404 traffic of your site and matches every dead address against your live catalogue (read from your sitemap). You see which addresses break, where their visitors come from, and where they were sent.

What this package does

When your Laravel application answers 404, the package asks no404 for the closest live page and sends the visitor there with a real HTTP redirect — the thing a JavaScript snippet on the 404 page cannot do (search engines still see a 404 there).

  • Every kind of 404: no matching route, abort(404), a missing model (findOrFail, route-model binding), Route::fallback(), a custom 404 renderer.
  • 301 or 302 is decided per site in the no404 dashboard (how sure a match must be before it becomes permanent). Your own redirects defined in the dashboard are always 301.
  • Fail-open: if no404 is slow or unreachable, the visitor gets your normal 404 page after at most 1.5 seconds, and lookups pause for a minute.
  • Quota-friendly: answers are cached per path — misses too — and static files, /storage, /livewire, /api, admin panels and similar paths are never sent.
  • Normal pages cost nothing: the package only looks at responses that are already 404.

Requirements

Laravel 12 or 13
PHP 8.2+ (Laravel 13 needs 8.3+)
Cache any store that survives between requests (redis, database, file, memcached…)
no404 an account and a site with its API key

Laravel 11 reached end of life on 12 March 2026 and is not supported: Composer will refuse to install the package there. Upgrade to Laravel 12 or 13 first.

Installation

composer require no404/laravel

Add the API key from the no404 dashboard (Site → Integration) to .env:

NO404_API_KEY=your-api-key

Check the connection:

php artisan no404:test

That is all — the middleware registers itself. Then press Test in the dashboard's Integration tab: it opens a random missing address on your site and confirms that the 404 reached no404 through this package.

Make sure APP_URL is your site's real address: redirect targets are only accepted on that host (and its www. twin).

Configuration

Everything works with the defaults. To change them, publish the config file:

php artisan vendor:publish --tag=no404-config
.env Default
NO404_API_KEY Required.
NO404_ENABLED true Keep the package installed but inert.
NO404_CACHE_STORE default store Use a persistent store; with array every 404 uses quota.
NO404_CACHE_TTL 3600 Seconds an answer is remembered (60 – 604800).
NO404_FORCE_301 false Send every match as 301, ignoring the dashboard threshold.
NO404_LOG_CHANNEL default channel Where configuration problems are logged (at most once an hour).
NO404_DEBUG false Adds X-No404-Source, X-No404-Score, X-No404-Skip headers.

In config/no404.php you can also add allowed redirect hosts, extra path prefixes and extensions to skip, and turn visitor forwarding off.

Registering the middleware yourself

Set 'middleware' => ['register' => false] in config/no404.php and append it in bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\No404\Laravel\Http\Middleware\RedirectNotFound::class);
})

Keep it global (not in a route group): when no route matches, route middleware never runs.

Commands

Command
php artisan no404:test Asks no404 once and explains the answer (key, plan, network). --path=/old-address resolves a real address (uses one lookup of your quota).
php artisan no404:status The local configuration and anything that needs attention. No network request.
php artisan no404:flush Resumes lookups paused after an error (once you have fixed the key or the plan).
php artisan about Includes a no404 section.

Hooks and events

Register hooks in a service provider's boot() method:

use Illuminate\Http\Request;
use No404\Laravel\Facades\No404;
use No404\Laravel\RedirectDecision;

// Never ask about some requests.
No404::skipWhen(fn (Request $request, string $path) => str_starts_with($path, '/members'));

// Answer from your own data first — not cached, no quota used.
No404::resolveUsing(fn (string $path) => LegacyUrl::where('path', $path)->first()?->toRedirect());
// returns ['redirect' => '/new-path', 'status' => 301] or null

// Change or cancel a redirect (return null to keep the 404).
No404::beforeRedirect(fn (RedirectDecision $decision) => $decision->withStatus(302));

// Other domains of the same site that targets may point to.
No404::allowedHosts(['shop.example.org']);

// Replace the visitor data (return null to send none).
No404::visitorUsing(fn (Request $request, array $visitor) => $visitor);

Events for your own telemetry: No404\Laravel\Events\RedirectResolved, LookupFailed, LookupSkipped.

What is sent to no404

For each 404 that is not answered from the cache:

  • the path, without the query string;
  • the Referer header, if any;
  • the ad network category (google, microsoft, meta, other) when the URL carries a paid-traffic marker — never the click ID itself;
  • the API key, in the Authorization header (never in the URL);
  • User-Agent: no404-laravel/<version>; <APP_URL>;
  • about the visitor: the IP truncated to its network (203.0.113.0, /48 for IPv6), a hash of the IP keyed with a secret derived from your APP_KEY (no404 cannot turn it back into an address or match it across sites), the user agent, and the country from CF-IPCountry. Private addresses are never sent.

Never sent: the full IP, cookies, session data, form data, user IDs, APP_KEY.

The visitor IP comes from $request->ip(), so it honours your trusted proxies setup.

Known limits

  • A page cache in front of Laravel (a CDN caching 404s, full-page cache packages) can answer before the application runs — then the package never sees the 404.
  • A 404 turned into a 200 ("soft 404") is not seen either. Keep the 404 status.
  • A global middleware that redirects on its own (locale prefixes) runs first, so a missing address can take two hops: its redirect, then no404's.
  • Requests that expect JSON, Livewire updates and JSON 404 responses are left alone.

License

MIT — see LICENSE.