logo

The downside of just forcing HTTPS when behind a reverse proxy

You mess up other important behavior

Joel Clermont
Joel Clermont
2026-08-17

Our production and development apps run behind a reverse proxy. Often, our simple apps are just nginx sitting in front of php-fpm.

Regardless of the specific tools in use, the proxy terminates TLS, and without any additional configuration, the Laravel app receives plain HTTP traffic.

One obvious side effect of this is that common helpers, like url() and route(), now generate http links instead of https links.

In the past, my fix was to hard-code the flag my web server passes to PHP, so Laravel would treat every request as secure.

# nginx.conf
fastcgi_param HTTPS on;

I've seen other approaches where at the app level you force the behavior in a service provider with URL::forceScheme().

Both those solutions "work", at least in the sense that your generated links and assets are now https instead of http. But you have other problems just lurking in the background waiting to bite you.

Instead, we should leverage the information our proxy already provides in HTTP headers. By default, for security reasons, Laravel doesn't trust proxy headers.

In our case, we can whitelist our one expected proxy using the mechanism built into the framework:

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(at: '10.0.0.0/8');
})

This works great if you fully control the proxy inside your infrastructure, but if you're using something like Cloudflare, you'll need to configure a larger list of IPs. And you'll also have to monitor these for changes over time, though thankfully Cloudflare changes them extremely infrequently (only 5 times total in their whole history!).

Why go through this extra work to set up the TrustProxies middleware?

First, consider the failure mode when something changes in your infrastructure, like a load balancer added in front of your web server.

With trustProxies, because that new load balancer is not explicitly whitelisted, your app breaks loudly. Links come out http, browsers complain, and you fix it the same day.

But if you used that earlier blunt hammer to just force the scheme in your app or web server, the links still look perfect, and the app appears to be working, but now ALL traffic appears to be coming from the load balancer's IP. Given the choice, I want the configuration that fails loudly.

In addition, there's an even more subtle problem. Just forcing the https scheme to fix link rendering ignores the fact that your application isn't seeing the real request IP of the end user. Without a proper proxy setup, your login rate limiter becomes one shared bucket for the whole internet, and your logs attribute everything to one IP.

That's a much bigger problem to me, and it can all be avoided by properly configuring the proxy middleware in your Laravel app.

That single middleware config resolves both the URL scheme and the real client IP. Instead of just silencing one behavior, we're properly leveraging the proxy server's forwarded headers and using the information the proxy is already sending us.

Here to help,

Joel

P.S. Did my brief mention of security make you want to learn more? Our security book covers several key security principles to keep in mind for your Laravel app.

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼
email
No spam. Only real-world advice.