backstage/laravel-trailing-slash
A Laravel package for controlling trailing slash behavior on your application's routes, letting you normalize, redirect, or enforce trailing slashes consistently. Keeps URLs canonical, avoids duplicate-content issues, and helps prevent mismatched redirects with simple, configurable middleware.
Architecture fit. The package addresses a narrow but legitimate cross-cutting concern: canonical URL shape. In a Laravel application this touches two seams — outbound URL generation (UrlGenerator, route(), url(), action()) and inbound request normalization (HTTP kernel middleware issuing redirects). Both are extension points Laravel exposes cleanly, so a TPM can treat this as an additive layer rather than a refactor. The concern is orthogonal to domain logic, which means it can be adopted on a single route group first and expanded. Fit is high for content/SEO-driven Laravel apps (marketing sites, storefronts, public APIs with cached URLs) and low for internal services where URL shape is irrelevant.
Integration feasibility. Mechanically trivial: a Composer require, a publishable config, and middleware registration in the HTTP kernel. The real cost is not installation but verification — confirming that generated URLs in views, sitemaps, transactional emails, queued jobs, and API responses all agree on one canonical form. Budget 1–2 days of engineering plus 1 day of SEO/crawl validation, not the "10-minute install" the package implies.
Technical risk. The dominant risk is maturity, not complexity. Zero stars, zero dependents, and a single-maintainer provenance mean no field-hardening: edge cases around signed URLs, route:cache, and Octane worker reuse are unlikely to have been exercised. Specific failure classes to probe:
ValidateSignature): Laravel signs the full path; a slash rewrite invalidates the signature and produces 403s on password-reset and signed-asset links.php artisan route:cache may bypass it, producing behavior that differs between local and production.$request->getHost() without trusted-proxy configuration, it becomes an open-redirect vector.Key questions for the maintainer (or our own spike). Which Laravel versions are supported and against which are tests run? Is the redirect 301 or 308, and is it method-aware? Are query strings preserved byte-for-byte? Does it interact safely with route:cache, config:cache, and Octane? Does it respect AppServiceProvider::forceScheme / forceRootUrl behind a load balancer? Is there test coverage at all, and is CI green? Does it ship a canonical-tag or sitemap helper, or only redirects?
TPM read on the score. A 0.1 composite with an opportunity signal near 43 reads as "real problem, unproven solution." The TPM's leverage here is decision framing, not code review: the package converts a tacit, per-route URL convention into an explicit, enforceable policy. That is worth doing. But the artifact we ship should be a pattern we own, with the package as a reference implementation to vendor if its test posture is thin — not a dependency we inherit.
Stack fit. Clean fit for a standard Laravel HTTP app on PHP 8.1+, behind Nginx or a CDN. The package is stateless, has no database or queue surface, and no auth coupling, so it drops in beside existing middleware without ordering conflicts beyond one rule: normalization must run before signature validation, auth, and route-model binding, and after trusted-proxy and host resolution. On the outbound side, verify it composes with URL::forceScheme()/forceRootUrl() for proxied environments rather than replacing them.
Migration path. Treat it as a greenfield configuration, not a migration — there is no data to move. The genuine migration is behavioral: existing inbound links, backlinks, ad campaigns, and customer bookmarks may use the non-canonical form. Those must 301 (permanent) to the canonical form so link equity transfers and clients update. Publish a redirect map first, then flip the canonical direction only once crawl data confirms no orphaned URLs.
Compatibility. Verify explicitly against the Laravel minor we run, plus Octane if applicable, plus our route:cache/config:cache deploy step. Also check the interaction with any existing URL-shaping logic — localization prefixes (/en/), pagination links, API versioning segments, and any custom RouteServiceProvider bindings. Two packages both patching UrlGenerator is a silent-override hazard; confirm only one wins and that it is ours.
Sequencing.
Maintenance. This is the crux. A zero-star, single-maintainer package with no dependents carries an unbounded bus factor, and URL generation is a core Laravel seam that shifts between majors. Expect to own upgrades ourselves. Mitigation: pin the version, isolate it behind our own thin wrapper (a CanonicalUrl service plus one middleware), and add an integration test that fails loudly on the next Laravel major. If the source review in step 1 shows thin tests, vendor the code into app/ with attribution — the MIT license permits it and the maintenance calculus improves materially.
Support. No vendor SLA, no security contact process, no CVE history to reason about. The compensating factor is a small attack surface: no persistence, no deserialization, no external calls. The one security-relevant behavior is redirect target construction, which we must validate ourselves. Assign a named internal owner before adoption.
Scaling. Effectively free. One middleware pass and a string operation per request; no measurable latency, no shared state, no cache or storage dependency. It scales horizontally by construction and behaves identically on any node. The scaling concern is at the CDN edge, not the app: after enabling, purge cached 200s so the origin's new 301s are actually seen, and ensure Vary/cache-key behavior does not let the non-canonical form persist in edge caches.
Failure modes to monitor.
route:cache in production. Test the cached path in CI, not just php artisan serve.Host behind an unconfigured proxy. Enforce trusted-proxy config and assert hosts against an allowlist.Ramp-up. Engineering cost is low-to-moderate: a senior Laravel engineer reads the source in an afternoon and writes the wrapper and tests in a day or two. The non-obvious ramp is organizational, not technical — SEO/marketing must ratify the canonical direction and sign off on the redirect map; SRE must confirm middleware ordering against trusted-proxy and CDN behavior; QA needs a test matrix covering status codes, query strings, non-GET methods, signed URLs, pagination, and localized prefixes. Plan roughly one to two weeks from kickoff to enforced rollout, with the spike decision gate at day three. The TPM's deliverable is less code than it is: one declared URL policy, one owner, one regression test, and a documented rollback path.
How can I help you explore Laravel packages today?