Reader Stacks

Redirecting HTTP to HTTPS in Laravel

Forcing HTTPS at the web server level is generally the more reliable, more efficient approach — the Laravel-level solutions here matter mainly for asset URL generation and shared hosting without server config access.

Redirecting HTTP to HTTPS in Laravel

While forcing HTTPS at the web server level (Apache/Nginx) is generally the more reliable and efficient approach, Laravel offers its own application-level solutions — useful on shared hosting without direct server config access, and for making sure Laravel's own generated URLs are correctly HTTPS.

Forcing HTTPS via middleware

// app/Http/Middleware/ForceHttps.php
class ForceHttps
{
    public function handle(Request $request, Closure $next)
    {
        if (! $request->secure() && app()->environment('production')) {
            return redirect()->secure($request->getRequestUri());
        }

        return $next($request);
    }
}
// bootstrap/app.php (Laravel 11+) or Kernel.php (earlier versions)
$middleware->append(ForceHttps::class);

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

Forcing Laravel's generated URLs to use HTTPS, without redirecting

// AppServiceProvider boot() method
public function boot(): void
{
    if ($this->app->environment('production')) {
        URL::forceScheme('https');
    }
}

This is a distinct concern from actually redirecting requests — forceScheme('https') only affects how Laravel generates URLs internally (via route(), url(), asset helpers), which matters specifically when the app sits behind a load balancer or proxy that terminates SSL before the request reaches Laravel, making $request->secure() unreliable on its own.

Trusting proxies for accurate scheme detection

// bootstrap/app.php
$middleware->trustProxies(at: '*');

Behind a load balancer, AWS ELB, or similar reverse proxy, Laravel needs to trust the X-Forwarded-Proto header to correctly determine whether the original request was HTTPS — without this, $request->secure() can incorrectly report false even though the original client connection was genuinely secure.

Redirecting via .htaccess instead (Apache)

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

Redirecting via Nginx server block instead

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

Handling the redirect at the web server level (Apache or Nginx, as shown) is generally preferable to Laravel middleware — it's more efficient (rejecting or redirecting the request before it even reaches PHP) and works correctly even for non-Laravel-routed requests like static assets.

Setting the HSTS header, to prevent even the first insecure request

header('Strict-Transport-Security: max-age=31536000; includeSubDomains');

HSTS (HTTP Strict Transport Security) tells a browser that has visited the site before to always use HTTPS automatically, without even attempting an insecure HTTP request first — this closes a small window that a plain server-side redirect alone doesn't cover, where a single initial HTTP request could theoretically be intercepted before the redirect happens.