Build a reverse image proxy in 10 lines of PHP/Laravel

First up, there’s better ways to do this. Maybe you should use nginx, and keep this out of PHP altogether. Maybe you should pull down people’s images, host them locally, and then serve them up yourself.

This isn’t about the best way. For me, tonight, this 10 liner that doesn’t mess with my nginx config is the best way.

Why build a reverse image proxy in PHP/Laravel?

We want to use image data from connected stores’ websites. We want the images to be fairly current. We want them to load super fast. And we want to load a lot of them at a time.

Connected stores are WooCommerce, Shopify or Magento. WooCommerce is known for being hosted on shitty $9/mo VPS plans, and they’re easy to kill with 50 fast requests for full sized images.

We use CloudFlare to cache assets aggressively, and it’s a total no-brainer. Can you convince your freemium customer to do that, who doesn’t even know where their DNS records are hosted? No.

So, instead of requesting images from the connected store site directly, we’re proxying them through our server. The server goes off and loads the image, and returns it to the app. Cloudflare captures the response and caches it for next time, so we only serve it once.

The code


Route::get('/external_resource', function(\Illuminate\Http\Request $request){
$resource = $request->get("resource");

$options = array('http' => array('user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'));
$context = stream_context_create($options);
$f = file_get_contents($resource, false, $context);

$finfo = new finfo(FILEINFO_MIME);

$contenttype = $finfo->buffer($f);
header("Content-Type: " . $contenttype); //HTTP 1.1

echo $f;
});

What’s it do, in detail?

Pull a URL out of the ‘resource’ get param, e.g. https://app.elvenda.com/external_resource?resource=https%3A%2F%2Fpioneergear.com.au%2Fwp-content%2Fuploads%2F2017%2F05%2Fhat1.jpg

Fetch the underlying image, with a chrome user agent (Shopify was 403’ing the requests).

Setting the content type to the content type of the image

Returning the image to Cloudflare, who return the image to the browser.