Reader Stacks

How to Redirect HTTP to HTTPS in Laravel

A web-server-level redirect is generally the better place for this — the application-level middleware approach exists for situations where server config access isn't available.

How to Redirect HTTP to HTTPS in Laravel

Forcing HTTPS can be done at the web server level (Nginx or Apache config) or inside the Laravel application itself via middleware — the server-level approach is generally preferred where you have that access, since it redirects before the request even reaches PHP.

Nginx-level redirect (the generally recommended approach)

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

This redirects every HTTP request before it ever reaches PHP or Laravel at all — more efficient than an application-level redirect, since no framework bootstrapping happens for a request that's just going to be redirected anyway.

Apache-level redirect (.htaccess)

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Application-level middleware, when server config access isn't available

php artisan make:middleware ForceHttps
class ForceHttps
{
    public function handle(Request $request, Closure $next)
    {
        if (! $request->secure() && app()->environment('production')) {
            return redirect()->secure($request->getRequestUri());
        }

        return $next($request);
    }
}

Checking app()->environment('production') avoids forcing HTTPS during local development, where a local dev server typically doesn't have a valid SSL certificate configured at all.

Registering the middleware globally

// bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
    $middleware->prepend(ForceHttps::class);
})

Using prepend() rather than append() runs this check as early as possible in the middleware stack — reasonable given that redirecting away from an insecure connection is something you generally want to happen before most other middleware logic runs.

The simpler URL::forceScheme() alternative for generated URLs specifically

// AppServiceProvider::boot()
if (app()->environment('production')) {
    URL::forceScheme('https');
}

This doesn't redirect incoming requests at all — it only ensures that URLs Laravel itself generates (via route(), url(), and similar helpers) always use https://, which matters specifically when the app runs behind a load balancer or proxy that terminates SSL before the request reaches Laravel, making $request->secure() potentially report incorrectly without additional trusted-proxy configuration.

Trusted proxies: a common gotcha behind a load balancer

// bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
    $middleware->trustProxies(at: '*');
})

When Laravel runs behind a load balancer that terminates SSL (common on many cloud hosting platforms), the actual request Laravel receives internally may appear as plain HTTP even though the original client connection was HTTPS — configuring trusted proxies correctly is what lets Laravel's $request->secure() check read the real original scheme from the X-Forwarded-Proto header instead of being fooled by the internal connection.