If you have a Laravel project, you might be surprised to find your routes are probably available on a number of different URLs.
Imagine the following example from your routes file:
<?php
Route::view('/feature/uptime-monitoring', 'front/feature/uptime-monitoring');
This creates a route at yourdomain.tld/feature/uptime-monitoring, which you’d expect.
But in most webserver configs, this means the following URLs are all valid and will all return an HTTP/1.1 200 OK with the same page:
https://ohdear.app/feature/uptime-monitoring
https://ohdear.app/index.php/feature/uptime-monitoring
What’s that index.php doing in there? It’s needed for some hosting environments that don’t support pretty-printed URLs.
But you might not need/want it, so let’s get rid of it!
Removing index.php from within your Laravel code#
One way to get rid of it, is to check the route in Laravel and issue a redirect there if needed.
Here’s one way to do this in app/Providers/RouteServiceProvider.php.
<?php
use Illuminate\Support\Str;
class RouteServiceProvider extends ServiceProvider
{
public function map(Router $router)
{
$this->removeIndexPhpFromUrl();
}
protected function removeIndexPhpFromUrl()
{
if (Str::contains(request()->getRequestUri(), '/index.php/')) {
$url = str_replace('index.php/', '', request()->getRequestUri());
if (strlen($url) > 0) {
header("Location: $url", true, 301);
exit;
}
}
}
}
A heads-up if you’re on a recent Laravel: this hooks RouteServiceProvider::map(), which is the Laravel 6 way. The map() method was dropped in Laravel 8, and RouteServiceProvider itself is gone entirely in Laravel 11+ (routing now lives in bootstrap/app.php). The snippet below is the original Laravel 6 form; on a modern app you’d drop the same removeIndexPhpFromUrl() check into a middleware or a boot() closure instead. The logic doesn’t change, only where you hang it.
This will redirect any URL with index.php in it.
$ curl -I "http://ohdear.app.test/index.php/pricing"
HTTP/1.1 301 Moved Permanently
Location: /pricing
$ curl -i "http://ohdear.app.test/index.php/feature/uptime-monitoring"
HTTP/1.1 301 Moved Permanently
Location: /feature/uptime-monitoring
There are some alternative ways to get the same result, by redirecting on the webserver itself.
Redirecting index.php in Nginx#
You can redirect if index.php is in the URL like this.
if ($request_uri ~* "^/index\.php(/?)(.*)") {
return 301 $2;
}
You might have to repeat this if you have different location {} blocks in your Nginx config.
Redirecting index.php in Apache#
You can add an additional line in your .htaccess.
$ cat .htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
# Redirect if index.php is in the URL
RewriteRule ^index\.php/(.+) /$1 [R=301,L]
</IfModule>
At least that’s an easy regex/fix. :-)
Redirecting index.php in Caddy#
On Caddy v2 (the syntax above was for the long-gone Caddy v1) it’s a one-liner with a path_regexp matcher and a redir.
$ cat Caddyfile
ohdear.app {
@indexphp path_regexp idx ^/index\.php/(.*)
redir @indexphp /{re.idx.1} 301
}
The named matcher captures whatever follows /index.php/, and redir issues the 301 to the cleaned path using the {re.idx.1} capture group.