Remove index.php from the URL in Laravel

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;
            }
        }
    }
}

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#

It’s a bit cumbersome, but this seems to work in Caddy.

$ cat Caddyfile
https:// {

	tls {
		ask https://ohdear.app/caddy/allowed-domain
	}

	rewrite {
	 	regexp ^/index.php/(.*)
	    	to /{1}
	}

	redir {
	    	if {uri} not {rewrite_uri}
	    	/ {rewrite_uri}
	}
}

It requires an internal rewrite to match the index.php and then a redir to redirect the actual request.