Handling Proxies in Laravel 4

Laravel uses Symfony for handling Requests and Responses. These classes have means to handle proxies, however Laravel doesn't have a configuration option for this out of the box.

That's not necessarily bad, but the need for it will arise if and when your Laravel app web server sits behind a load balancer or uses a reverse-proxy such as Varnish.

I'll refer to load balancers, reverse-proxies or similar as "intermediaries", as they sit between your clients and your web servers.

If you do have an intermediary between your clients and your web server(s), your web server will see each request as coming from the intermediary, rather than the client.

This means every request will come from the same location (same IP address), and potentially wreak havoc on session handling, logging, or anything that relies on having the client's IP address handy.

To combat this, common convention is for the intermediary to include a HTTP_X_FORWARDED_FOR header, with the client's IP address. If this header exists, the client IP address should be taken from that header, rather than the usual suspects (REMOTE_ADDR for instance).

Proxies in Laravel

In order for Laravel to check for the forwarded IP address, we need tell Laravel what IP addresses to "trust" as a proxy. If it finds the IP address received is a trusted IP, it will look for the forwarded IP address and set it as the client's true IP address.

If we do not tell Laravel what the IP address of our proxy (or proxies) is, it will ignore it for security reasons.

Note: If you use Rackspace or other PaaS "cloud" providers which provide load balancers, the IP adddress of the load balancer may not be known. For instance, rackspace uses many load balancers, and so you never know what IP address the request will be coming from. This means every IP address would need to be trusted.

Simple setup

The simplest to set this up in Laravel is to add this code somewhere in your code:

Request::setTrustedProxies(array(
    '192.168.1.52' // IP address of your intermediary
));

Better setup

A better setup would be to put this into a Service Provider.

An example of what that would look like:

<?php namespace Fideloper\Proxy;

use Illuminate\Support\ServiceProvider;

class ProxyServiceProvider extends ServiceProvider {

    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = false;

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $request = $this->app['request'];
        $proxies = $this->app['config']->get('proxy.proxies');

        if( $proxies === '*' )
        {
            // Trust all proxies - proxy is whatever
            // the current client IP address is
            $proxies = array( $request->getClientIp() );
        }

        $request->setTrustedProxies( $proxies );

    }

}

After that, you can add this Service Provider to app/config/app.php:

'providers' => array(
    ..other providers..
    'Fideloper\Proxy\ProxyServiceProvider'
)

Lastly, set what IP addresses to trust. Create and edit app/config/proxy.php:

<?php
return array(

    /*
    |--------------------------------------------------------------------------
    | Trusted Proxies
    |--------------------------------------------------------------------------
    |
    | Set an array of trusted proxies, so Laravel knows to grab the client's
    | IP address via the HTTP_X_FORWARDED_FOR header.
    |
    | To trust all proxies, use the value '*':
    |
    | 'proxies' => '*'
    |
    */

    'proxies' => array(
        '172.16.27.141',
        '172.16.27.142',
        '172.16.27.143',
    ),

);

That will trust the three IP addresses as seen above. If you need to trust all IP addresses as proxies, you can set 'proxies' to '*'.

'proxies' => '*'

Or…install my package from Packagist.

I've created a package, available via Composer, for this. You can check out my Proxy Package on Packagist and directly on GitHub.

comments powered by Disqus