Sending HTTP Requests Through a Proxy in Laravel

By Paulus, 15 May, 2023

I recently changed the authentication method for our Laravel applications at work from Shibboleth to Microsoft Azure AD. I decided to use Laravel Socialite and implementing it was incredibly easy and "just worked." At least in our development environment.  When the application was deployed to our beta environment, which was setup to mirror the production environment, I ran into a snag where I wasn’t able to login via Azure AD. After entering my credentials, the application would time out. The problem was that the firewall blocks all outgoing traffic, including traffic to login.microsoft.com that is needed to authenticate.

Since version 7, Laravel has included the Guzzle HTTP client. Any time a request is made it goes through there, creating a single point where you can override its properties such as adding headers, or in my case, specifying a proxy. Guzzle uses cURL to make requests and defining CURL options is easy. However, Laravel has added wrappers for somethings to make generating a request even easier. For example, there is the withHeaders method. There isn't a helper specifically for adding a proxy or setting cURL options. In order to do that you have to call the withOptions method and pass in Guzzle request options. By leveraging this method, I can define a proxy to go through when making requests to the outside world.


Illuminate\Support\Facades\Http::withOption([
    'proxy' => 'proxy.example.tld:3128',
    // To set cURL options. 
    'curl' => [
        CURLOPT_FOLLOWLOCATION => true,
        // See: https://www.php.net/curl_setopt
    ],
])->get('https://www.paulslinuxbox.net')

This works and may be fine for the single instance where you need to grab a file from the outside world. This won't work when a dependency needs to make remote connections and becomes messy if you have a bunch of these calls. The elegant solution is to extend the HTTP client factory in the AppServiceProvider.


namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Client\Factory as Http;
class AppServiceProvider extends ServiceProvider
{
    /**
    * Register any application services.
    *
    * @return void
    */
    public function register()
    {
        $this->app->extend(Http::class, function ($service, $app) {
            return $service->withOptions([
                'proxy' => 'proxy.example.tld:3128',
            ]);
        });
    }
    /**
    * Bootstrap any application services.
    *
    * @return void
    */
    public function boot()
    {
        //
    }
}

If you only need the application to authenticate against Azure AD, you can add the proxy in the config/services.php file or add a variable called PROXY in .env file.

return [
/*
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
|
| This file is for storing the credentials for third party services such
| as Mailgun, Postmark, AWS and more. This file provides the de facto
| location for this type of information, allowing packages to have
| a conventional file to locate the various service credentials.
|
*/
    'azure' => [
        'client_id' => env('AZURE_CLIENT_ID'),
        'client_secret' => env('AZURE_CLIENT_SECRET'),
        'redirect' => env('AZURE_REDIRECT_URI'),
        'tenant' => env('AZURE_TENANT_ID'),
        'proxy' => env('PROXY', 'proxy.example.tld:3128')  // optionally
    ],
    'mailgun' => [
        'domain' => env('MAILGUN_DOMAIN'),
        'secret' => env('MAILGUN_SECRET'),
        'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
        'scheme' => 'https',
    ],
    
    'postmark' => [
        'token' => env('POSTMARK_TOKEN'),
    ],
    
    'ses' => [
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
    ],
];
AZURE_CLIENT_ID=
AZURE_CLIENT_SECRET=
AZURE_TENANT_ID=
AZURE_REDIRECT_URI=
PROXY=

Resources