Alibaba Cloud SLB Causing Laravel URLs to Use HTTP Protocol

Background⌗
Our company’s customer-facing project is developed using the Laravel framework, with pages that mix frontend and backend code. During development, we frequently see code logic using the $load_by_https
global variable to determine whether to load resources via HTTPS, as well as when generating URLs. This is quite cumbersome and inelegant. Although I’ve always felt uncomfortable with this approach, I abandoned the idea of refactoring due to the large amount of code involved.
Catalyst⌗
Recently, this project needed SEO optimization work. During the optimization process, I discovered numerous issues caused by Laravel generating non-HTTPS URLs. So I decided to investigate why the original developers chose to use the global $load_by_https
variable to manually set whether to enable HTTPS.
After investigation and reasoning, I found that it was because our project uses Alibaba Cloud’s Server Load Balancer (SLB) to distribute requests to multiple backend servers. However, since we bind the HTTPS certificate to the SLB, all backend services listen on HTTP ports. The specific request flow is roughly as follows:
Solution⌗
Configuring SLB⌗
From the flow diagram above, we can see that when HTTPS requests reach the SLB, they are forwarded to the backend services as HTTP requests. Therefore, we need to enable the X-Forwarded-Proto
configuration in the SLB settings.
In the SLB management page, modify the listener configuration and check the option Get the SLB listening protocol through the X-Forwarded-Proto header field.
This allows backend services to obtain the client request’s scheme from the request header.
Laravel Settings⌗
Due to Laravel’s security settings, even after setting X-Forwarded-*
, it still cannot correctly obtain valid information. We also need to configure the SLB as a trusted proxy in the App\Http\Middleware\TrustProxies
middleware.
Since Alibaba Cloud SLB IPs are not fixed, we cannot set specific IP addresses and can only use * or **.
<?php
namespace NeoX\Http\Middleware;
use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array|string
*/
protected $proxies = '*';
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers = Request::HEADER_X_FORWARDED_ALL;
}
The main settings here are $proxies
and $headers
.
With this configuration, when Laravel generates URLs, it will call the Request’s isSecure()
method to determine whether to enable HTTPS.
/**
* Checks whether the request is secure or not.
*
* This method can read the client protocol from the "X-Forwarded-Proto" header
* when trusted proxies were set via "setTrustedProxies()".
*
* The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
*
* @return bool
*/
public function isSecure()
{
if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_X_FORWARDED_PROTO)) {
return \in_array(strtolower($proto[0]), ['https', 'on', 'ssl', '1'], true);
}
$https = $this->server->get('HTTPS');
return !empty($https) && 'off' !== strtolower($https);
}
Now the question is: replacing the $load_by_https
variable in numerous page templates is another time-consuming task.
I hope this is helpful, Happy hacking…