Laravel On Shared Hosting
Tags: php, laravel
Published: 2020-12-15 15:42:46

Before we start its worth caveating this post with the message: **It’s always more desirable and recommended to serve Laravel apps from bespoke hosting environment**.

There are plenty to choose, from first party tools like [Forge](https://forge.laravel.com/) and [Vapour](https://vapor.laravel.com/) to more general providers like [Digital Ocean](https://www.digitalocean.com/), [AWS](https://aws.amazon.com/) and [GCP](https://cloud.google.com/).

However as i find myself in a similar situation there may be times you have to deploy on a CPanel environment.

I am going to cover the usual quite simple redirect you see across the web, but more importantly a second step thats usually left out. This second step can have a massive impact on your SEO.

## The Redirect

As you may of found elsewhere theres a simple redirect you can put in the root folder of your project:

```html
# Internally rewrite all top-level requests to the public directory
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
```

It’s very simple and just redirects everything from the root of your site `public_html` down into the `public` folder of your Laravel project.

From there the `.htaccess` provided with Laravel will take over and route incoming requests.

Job done? Maybe not…

## The SEO Problem

The problem with the above redirect is that it doesn’t prevent access to ALL of your urls with a `/public/` prefix. So both `/` and `/public/` will server your applications homepage!

This is obviously bad for any SEO efforts your planning to make to the site as you now have duplicate pages and on top of that if someone stumbles upon those urls its not great for users.

I spent sometime trying out redirects to prevent this from the `.htaccess` file itself but came up blank. In my particular case this is a CPanel server with Litespeed installed. And i know from experience its not quite 100% like for like with Apache.

## The Solution

With the `.htaccess` approach coming up blank i went with a Middleware solution. Again this isn’t ideal as its not really the applications responsibility but we are not in an ideal situation anyway.

It’s a pretty simple middleware, it looks for the public prefix and returns a 301 redirect if found. This does mean you cannot have urls that start with public but in our case this wasn’t a problem:

```php
<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class RedirectPublicFolderRequests
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        if (strpos($request->getRequestUri(), '/public') === 0) {
            return redirect()
                ->to($request->getSchemeAndHttpHost() . '/' . substr($request->getRequestUri(), 8))
                ->setStatusCode(301);
        }
        return $next($request);
    }
}
```

Once again this isn’t ideal but works for our purposes.

Add this to your global middleware stack before anything else.

Note how we use the `$request->getRequestUri()` method and not simply `$request->path()`. As the path will be effected by the servers rewrites.

Now any requests for `public` will redirect with a 301 and you wont be serving duplicate pages.