If you need to properly determine whether a request was secure when behind a load balancer you need to let the framework know that you're behind a proxy. This will ensure that the route()
and url()
helpers generate correct URLs and remove the need to create relative redirects which are both not 100% supported by browsers and also won't work properly when serving a webpage from a sub-path.
This is what we use to solve this problem and it's working so far for us:
.env
LOAD_BALANCER_IP_MASK=aaa.bbb.ccc.ddd/xx #Subnet mask
LoadBalanced Middleware
class LoadBalanced {
public function handle($request, $next) {
if (env("LOAD_BALANCER_IP_MASK")) {
$request->setTrustedProxies([ env("LOAD_BALANCER_IP_MASK") ]);
}
$next($request);
}
}
Then put the middleware in your Kernel.php
:
protected $middleware = [
LoadBalanced::class,
//.... It shouldn't matter if it's first or last as long as other global middleware don't need it
];
This is a feature available to Laravel because it is using the Symfony request as a base. How this work is that the load balancer forwards some important headers. Symfony currently understands:
protected static $trustedHeaders = array(
self::HEADER_FORWARDED => 'FORWARDED',
self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
);
which have information regarding the user making the request to the load balancer and the protocol used.
Also according to framework comments:
The FORWARDED header is the standard as of rfc7239.
The other headers are non-standard, but widely used
by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
Update:
Since version 5.5, the Laravel boilerplate package includes the TrustedProxy
middleware which uses the fideloper/TrustedProxy package.
To have it working you need to (a) make sure it's in your $middleware
array in your App\Http\Kernel
class and that you place the IPs of the trusted proxies in this middleware e.g.
protected $proxies = [
'1.2.3.4'
];
I would highly recommend to explicitly specify which forwarded headers your proxy sends e.g.:
protected $headers = Request::HEADER_X_FORWARDED_AWS_ELB;
if you're using an AWS load balancer.
The reason for this is quite important in that if you are using an AWS load balancer then someone could craft a request with the 'Forwarded` header and that will be forwarded by AWS and then processed by the middleware essentially allowing users to spoof their IP host/port etc.
$request->setTrustedProxies([ /* IPs of your load balancers */ ])
. Properly configured load balancers send aX-Forwarded-Proto
which laravel can use to determine the correct protocol as long as the header is coming from a trusted source. Check stackoverflow.com/questions/28402726/… for more details – apokryfosexample.com/subfolder/
– apokryfos