Laravel On Shared Hosting

Lee Mason

Lee Mason

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 and Vapour to more general providers like Digital Ocean, AWS and GCP.

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:

# 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
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.

Want to know when I have new content? Enter your email address to be notified when it’s available.

Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

Comments