Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Url Trailing Slash Laravel Package

illuminatech/url-trailing-slash

Laravel middleware that enforces consistent trailing slashes by redirecting requests to the canonical URL form. Avoid duplicate-content SEO issues and keep links predictable, with flexible per-route configuration for when a trailing slash should or shouldn't appear.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install the package:
composer require illuminatech/url-trailing-slash
  • Read the package README and its middleware source first. This is a small package: the important pieces are the middleware class, its config/env switch, and the service provider. Confirm the exact FQCN/config key for your installed version before copying examples.
  • Decide the canonical style for the app. For most Laravel apps, “no trailing slash” is the safest default. Use “with trailing slash” when existing URLs, SEO, or legacy clients require it.
  • Register the middleware as global/pre-routing middleware. Do not rely on route middleware: if Laravel cannot match /foo/ to the /foo route, route middleware never runs.

Laravel 11+:

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    // Add after TrustProxies so scheme/host are correct behind proxies,
    // but still before routing.
    $middleware->append(\Illuminatech\UrlTrailingSlash\Middleware\TrailingSlash::class);
})

Laravel 10 and below:

// app/Http/Kernel.php
protected $middleware = [
    \App\Http\Middleware\TrustProxies::class,
    \Illuminatech\UrlTrailingSlash\Middleware\TrailingSlash::class,
    // ...
];
  • Configure the package’s enforcement mode to match your decision. The package exposes a single “with slash / without slash” policy; set it via the published config or package env option, then clear caches:
php artisan config:clear
php artisan route:clear
  • First use case: canonicalize public content URLs such as /blog/my-post/ → /blog/my-post with a permanent redirect, while leaving APIs, webhooks, and admin tooling untouched. Verify with:
curl -I http://my-app.test/blog/my-post/
curl -I http://my-app.test/blog/my-post

Implementation Patterns

  • Global canonical redirect for public web routes. This is the primary pattern: run the package globally, then exclude paths that must not receive redirects:
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminatech\UrlTrailingSlash\Middleware\TrailingSlash;

class CanonicalTrailingSlash
{
    public function handle(Request $request, Closure $next)
    {
        if ($request->is('api/*', 'webhooks/*', 'horizon/*', 'telescope/*', '_ignition/*')) {
            return $next($request);
        }

        // Do not turn POST/PUT/PATCH/DELETE into GET via a 301.
        if (! $request->isMethodCacheable()) {
            return $next($request);
        }

        return app(TrailingSlash::class)->handle($request, $next);
    }
}

Register CanonicalTrailingSlash globally instead of the package middleware directly when you need exclusions.

  • Choose one canonicalization layer. If nginx/Apache already redirects trailing slashes, disable one side. Running both server-level and Laravel-level rules is a common source of redirect loops, especially when the proxy and app disagree about scheme/host.

  • Keep route definitions simple. Define routes in Laravel’s normal style and let the middleware redirect inbound URLs to the canonical form. Avoid mixing /foo and /foo/ route definitions for the same resource.

  • Treat API and webhook routes as non-redirecting. API clients often do not follow 301/302 redirects, and webhook providers may sign the exact URL. Return consistent 404s or document the required style instead of redirecting.

  • Be careful with generated URLs. The package primarily normalizes incoming requests. Check whether your version also affects route()/url() output. If it does not, centralize link generation so generated URLs already match the canonical style, especially for pagination, sitemaps, emails, and signed URLs.

  • Exclude signed URLs unless you control generation. Redirecting a signed URL can invalidate the signature or surprise clients. Generate signed URLs in the canonical form and test them through the middleware.

  • Use feature tests as policy guards. Add tests for both directions and for exceptions:

$this->get('/about/')->assertRedirect('/about');
$this->get('/about')->assertOk();
$this->get('/api/things/')->assertNotFound(); // or expected API behavior

Gotchas and Tips

  • Middleware order matters. Place the package after TrustProxies but before route resolution. If redirects use http:// behind an HTTPS proxy, fix TrustProxies/APP_URL first; otherwise you can create loops.
  • 301s are cached by browsers. Test with 302s or use curl -I --max-redirs 0 during rollout. Changing the policy later will not fix clients that cached the old permanent redirect.
  • Do not redirect non-idempotent methods. A 301 on POST can become a GET and drop the request body. Exclude APIs, form submissions, and webhooks, or ensure the package uses an appropriate status code such as 308.
  • Handle the root URL explicitly. / should stay /; avoid generating // or redirecting the homepage in a loop.
  • Preserve query strings and fragments. Check that /search/?q=foo redirects to /search?q=foo (or the reverse) without losing the query string.
  • Watch double slashes and encoded paths. /foo//bar, %2F, and mixed encodings may not be covered by a simple trailing-slash check. Normalize at the web server or with a dedicated URL-normalization middleware if these matter.
  • Exclude admin/tooling routes. Nova, Filament, Horizon, Telescope, Ignition, health checks, and debug endpoints often have their own routing assumptions. Add them to exclusions before enabling globally.
  • Clear caches after config changes. Run php artisan config:clear, route:clear, and restart Octane workers if applicable.
  • Confirm licensing before production use. The repository metadata reports NOASSERTION; verify the actual license and maintenance status. Because the package is small, pin the version and be prepared to fork or vendor the middleware if needed.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource