How to remove the public or index.php folder in Laravel (even when .htaccess isn't working)
Content Index
- Why /public or /index.php Appears in Laravel and Why it Affects SEO
- URL Duplication and Production Risk
- Shared Hosting Limitations and Misconfigured Servers
- Correct Virtual Host / Root Configuration
- Common Errors: Loops, NotFound, and Disabled mod_rewrite
- My Alternative Method: Remove public or index.php from AppServiceProvider (Last Resort)
- How to Detect Folders in the URL with Str::contains
- Automatic and Secure 301 Redirection
- Complete Implementation Example
- Security Considerations (Avoiding Exposure of .env or Internal Routes)
- Final Tips for Production and SEO
- Frequently Asked Questions
Many times when we are developing a Laravel project and moving it to production, we see errors that appear in the URL, the public folder, or the index.php file:
// Valid URL
https://example.com/blog
// Invalid URL
https://example.com/index.php/blog
https://example.com/public/blogAnd although they work, they are not pretty at all, they generate duplicate content and can cause SEO penalties. I've seen it several times when uploading projects to limited servers where you don't have full control over Apache or Nginx. The typical situation where you try everything you find on the Internet… and nothing works. If you are exactly there, breathe: there are other ways.
In this guide, I explain how to completely clean up your URLs, when to use .htaccess, when not to, and how to apply a final solution using PHP from AppServiceProvider when you've tried everything and the server won't cooperate.
Why /public or /index.php Appears in Laravel and Why it Affects SEO
Laravel was designed for all traffic to enter through public/, and that makes sense: that folder contains the index.php that acts as the front controller and protects the rest of the framework. The problem is that, on some servers, instead of pointing to the public directory, the configuration points to the project's root directory, causing the browser to try to "guess" routes.
Which, if we have a blog, this can bring us penalties with SEO; in this post we are going to see how to fix this problem, not from .htaccess which often doesn't work and there are many examples of how to do it on the Internet, but through PHP code. Therefore, if you are desperate, this is the last measure you can implement.
URL Duplication and Production Risk
In my case, the first time I encountered this, Google started indexing strange routes with /public/ and with /index.php/. And of course, that not only looks amateur, it also produces duplicate content. Google detects two different URLs for the same content, and the ranking suffers.
At an SEO level, this affects:
- Incorrect canonicals
- Internal competition
- Inconsistent redirects
- Decrease in crawl budget
Shared Hosting Limitations and Misconfigured Servers
The problem? If mod_rewrite is disabled or the hosting does not allow overrides, this is absolutely useless.
Correct Virtual Host / Root Configuration
The official Laravel solution is always:
DocumentRoot /project/publicBut this requires access to Apache/Nginx. If you are in an environment where you don't have permissions, forget it.
Common Errors: Loops, NotFound, and Disabled mod_rewrite
Several competitors had users with errors like:
- AH00124: Request exceeded the limit of 100 internal redirects
- “Not found” when removing /public/index.php
- “Forbidden” when accessing the project without /public/
- The .htaccess is simply ignored
It happened to me too: you try to redirect and end up entering an infinite loop. That's why when .htaccess fails, I don't waste any more time and apply the solution I'm showing you now.
My Alternative Method: Remove public or index.php from AppServiceProvider (Last Resort)
When you are desperate because the server does not respect .htaccess, this method saves your life. It has worked for me on shared hosting, old VPS, and servers that do not allow configuration modification.
It consists of intercepting the URL from Laravel and automatically redirecting it with a 301.
How to Detect Folders in the URL with Str::contains
Laravel allows us to analyze the incoming URL from PHP using request()->getRequestUri().
From there I can check if the URL contains /public/ or /index.php/.
Automatic and Secure 301 Redirection
I use:
if (Str::contains(request()->getRequestUri(), '/public/')) {
$url = str_replace('public/', '', request()->getRequestUri());
header("Location: $url", true, 301);
exit;
}And the same for index.php.
This forces any client or crawler to always see the clean version. The SEO advantage is immediate: Google stops crawling the incorrect routes.
Complete Implementation Example
If you are also on limited hosting or an unpermissioned VPS to modify virtual hosts, modify sites-available, or without control over Apache modules, it is common for .htaccess not to work.
This happened to me several times: you rack your brains editing .htaccess, and in the end the server ignores it. That's where I started using a PHP method as a last resort.
To remove the public folder and/or index.php from the URL, what we must do is go to our application provider:
app/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->removePublicPHPFromURL();
}
protected function removePublicPHPFromURL()
{
if (Str::contains(request()->getRequestUri(), '/public/')) {
$url = str_replace('public/', '', request()->getRequestUri());
if (strlen($url) > 0) {
header("Location: $url", true, 301);
exit;
}
}
}
}Or if you want to remove index.php, it looks like this:
app/Providers/AppServiceProvider.php
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;
}
}
}In both cases, as you can see, we simply check through the request if the index.php folder or the public folder exists and we remove it, then we do a 301 type redirection which means it indicates a permanent redirection to the same URL removing the public and index.php folder; complete code:
app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->removePublicPHPFromURL();
$this->removeIndexPHPFromURL();
}
protected function removePublicPHPFromURL()
{
if (Str::contains(request()->getRequestUri(), '/public/')) {
$url = str_replace('public/', '', request()->getRequestUri());
header("Location: $url", true, 301);
exit;
}
}
protected function removeIndexPHPFromURL()
{
if (Str::contains(request()->getRequestUri(), '/index.php/')) {
$url = str_replace('index.php/', '', request()->getRequestUri());
header("Location: $url", true, 301);
exit;
}
}
}Security Considerations (Avoiding Exposure of .env or Internal Routes)
One of the most important warnings in the forums —and one I have also suffered— is that moving the index.php to the root or serving Laravel from / can leave routes exposed like:
https://mydomain.com/.envYour application is left open like a book.
The advantage of my method is that you don't need to move anything. Everything remains protected as Laravel intends: from the public folder.
Final Tips for Production and SEO
- Do 301 redirects, never 302
- Maintain an updated sitemap after cleaning the URLs
- Check Search Console to detect old duplications
- Keep public/ as the real entry point, even if the clean URLs hide it
- Test several routes: /blog, /blog/, /css/app.css
- If later you have server control, correctly configure the DocumentRoot to do without this “patch”
Frequently Asked Questions
- Is it safe to remove /public or /index.php from PHP?
- Yes, because you don't move sensitive files. You only do redirects.
- Why doesn't my .htaccess work?
- Generally because AllowOverride is disabled or mod_rewrite is inactive.
- Can this method be used with any version of Laravel?
- It works from Laravel 5 up to Laravel 13.
- Does this affect performance?
- The condition is evaluated in microseconds and only when the URL contains /public/ or /index.php/.
- Does this automatically fix SEO?
- It helps a lot, but you should still clean up old URLs in Search Console.
I agree to receive announcements of interest about this Blog.
Many times when we are developing a project in Laravel and move it to production, we see errors that appear in the URL, the public folder or the index.php file; Let's see how to remove them.