Warm tip: This article is reproduced from serverfault.com, please click

Redirect to rename remote images so as to get around adblock

发布于 2020-12-10 19:09:10

I'm working with a partner who provides me with images of their products from an API. The images are sent as URLs and they expect me to just use these URLs in img tags, not download the images and serve them locally.

The problem is that 1/4 of users use ad blockers and their images have URLs like https://example.com/ad-image/ads/1234/500.jpg which read to ad blockers as ads (which they are of course, technically). Would it be possible to set up a redirect so an img tag like <img src="/image-redirects/500.jpg"> would show them the image from https://example.com/ad-image/ads/1234/500.jpg? Is that feasible? Would they not show up anyways because they're remotely hosted images?

Questioner
pg.
Viewed
0
MrWhite 2020-12-15 20:29:55

Would it be possible to set up a redirect so an img tag like <img src="/image-redirects/500.jpg"> would show them the image from https://example.com/ad-image/ads/1234/500.jpg?

Yes, you can, but not with an external "redirect" as that will simply present the target URL to the browser on the redirect response (and be blocked - if the browser is blocking simply based on keywords in the URl).

Reverse Proxy

You will either need to configure your server as a reverse proxy (using mod_proxy and related modules with ideally ProxyPass / ProxyPassReverse in the server config), then you can do something like the following in .htaccess (before the WordPress front-controller) using mod_rewrite to route the request through mod_proxy:

RewriteRule ^image-redirects/(\d+\.jpg)$ https://example.com/ad-image/ads/1234/$1 [P]

(Assuming 500 is a dynamic filename consisting of digits only - as in your example).

PHP Script to fetch image

Or, you internally rewrite such requests to a PHP script that fetches the image from the remote server and sends this back in the response.

For example, in .htaccess, using mod_rewrite (again, before the WP front-controller):

RewriteRule ^image-redirects/(\d+\.jpg)$ image-fetch-script.php?file=$1 [L]

In image-fetch-script.php you can examine the file URL parameter and fetch the intended image from the remote server and send this back to the client in the response. You could also cache the image on your server for a period of time. However, the fact that they want you to fetch the images directly from their server may be because they are logging impressions, so local caching would affect this.

Example image-fetch-script.php could be something like:

<?php
/**
 * Fetch remote image and send to client
 */

// Remote URL from which to fetch images
$remoteBaseUrl = 'https://example.com/ad-image/ads/1234/';

// Permitted file extensions / mime-types
// NB: If using CURL, can get the mime-type from the HTTP response instead
$mimeTypes = [
    'gif' => 'image/gif',
    'jpg' => 'image/jpeg',
    'png' => 'image/png',
    'webp' => 'image/webp',
];

// Get the reqd file the "file" URL parameter in the rewritten URL
$file = $_GET['file'] ?? null;
$remoteUrl = null;

// Validate filename format
// Or even check against a known list of files?
$filenameRegex = '/^[\w-]+\.('.implode('|',array_keys($mimeTypes)).')$/';
if (preg_match($filenameRegex, $file, $matches)) {
    header('Content-Type: '.$mimeTypes[$matches[1]]);
    $remoteUrl = $remoteBaseUrl.$file;

    // OPTIONAL: Check cache here...
    // readfile() and exit if current.

    // Using readfile()
    // - simple, but does not provide any error handling on the HTTP response
    //   eg. if the remote file does not exist etc.
    //$bytes = readfile($remoteUrl);
    //if ($bytes === false) {
    //    General 500 Internal Server Error
    //    http_response_code(500);  
    //} else {
    //    // No error - assume success!
    //    // But there could have been an HTTP response "error" that is lost
    //}
    //exit;

    // Using CURL
    $ch = curl_init($remoteUrl);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $content = curl_exec($ch);
    $curlError = curl_errno($ch);
    if ($curlError) $curlError .= ': '.curl_error($ch);
    $httpStatus = curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($curlError) {
        // Only set header when debugging, otherwise could expose the target URL
        //header('X-CURL-ERROR: '.$curlError);

        // General 500 Internal Server Error
        http_response_code(500);    
    }
    // CURL request is OK, but could still be an HTTP failure...
    else {
        // OK - serve image
        if ($httpStatus === 200) {
            // OPTIONAL: Save to cache...
            echo $content;
        }
        // Pass through HTTP status from CURL request... 404, 403, etc...
        else {
             http_response_code($httpStatus);
        }
    }
    exit;
}

// Default failure... "Bad Request"
http_response_code(400);