Removing trailing slashes in Laravel, the right way
While doing a quick search for a Laravel middleware to remove trailing slashes from URL's, I found some articles at the top of the search results with some naive and incorrect solutions. Let me explain.
The proposed solutions looked something like this:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class RedirectTrailingSlash
{
public function handle(Request $request, Closure $next)
{
if (preg_match('/.+\/$/', $request->getRequestUri())) {
$url = rtrim($request->getRequestUri(), '/');
return redirect($url, 301);
}
return $next($request);
}
}
At first glance this seems to work, but it contains an obvious error.
$request->getRequestUri()
returns the requested URI, which consists of the path and the query string. The regular expression that checks for the existence of a trailing slash does not work correctly when the requested URI contains a query string.
A more correct implementation should check the requested path in isolation. Something like this:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class TrimTrailingSlash
{
public function handle(Request $request, Closure $next)
{
$path = $request->getPathInfo();
// Skip root path
if ($path === '/') {
return $next($request);
}
$trimmedPath= rtrim($path, '/');
// If $trimmedPath is not equal to actual path, a trailing
// slash has been trimmed. We should redirect to the new path
if ($request->getPathInfo() !== $trimmedPath) {
$uri = $trimmedPath;
// If the requested URI has a query string we should
// include it in the new URI
$queryString = $request->getQueryString();
if ($queryString) {
$uri = "$uri?$queryString";
}
return redirect($uri, 301);
}
return $next($request);
}
}
Or you could use illuminatech/url-trailing-slash if you don't want to roll your own.