Stop Google Image Search from Hotlinking Your Photos (Without a Plugin)

By Jonathan Timar
30 Comments

A while back Google rolled out a new version of Image Search which made a lot people, myself included, very, very unhappy. Essentially the new version takes the entire full resolution image from your website, using up your bandwidth, and serves it up for anyone to take without them having to so much as visit your website. Google calls this “improvement”. As a photographer, I call it theft.

Several solutions sprang up over the weeks that followed, and I settled on using a plugin for WordPress called Imaguard and while it did stop Google from having its way with my copyrighted property, it also created a huge load on my server. I also believe that it caused Google to simply de-index all the photos on my site, which is not what I wanted. There is another plugin available now, but I tried it and found it bloated and needlessly complex.

So I spent the better part of the day looking for another solution (Hey Google, I charge $60 bucks and hour, just sayin’) and gave up. Months later, I finally found one. This requires a little technical knowledge, but it’s not too bad. The beauty of this solution is that is can be adapted to work on any website, not just WordPress websites. It works just as the plugins do, by swapping out your full resolution for a watermarked one.

First you need to disable client side caching for your website. This is important or the solution will not work. If you are going to disable client side caching with a WordPress site though, make sure you have a server-side caching plugin installed. It’s very unfortunate that this is necessary, but unfortunately it is. You can thank Google for that.

To do it, you need to edit you .htaccess file and add the following:

# Disable client side caching of images to stop Google and Bing image theft
Header unset Pragma
FileETag None
Header unset ETag
<filesmatch ".(jpg|jpeg)$"="">
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"

This will prevent browser-based caching of your images, which is critical for this technique to work. While you’re editing your .htacess file, also add the following lines:

# BEGIN Anti-Leech 
RewriteEngine on
# Uncomment next line to allow blank referrers (not recommended) 
# RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourwebsite\.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourwebsite\.com$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?mailchimp\.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?mailchimp\.com$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?paypal\.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?paypal\.com$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?craigslist\.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?craigslist\.com$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?gravatar\.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?gravatar\.com$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?wp\.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?wp\.com$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?twitter\.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?twitter\.com$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?pinterest\.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?pinterest\.com$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?linkedin\.com/.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?linkedin\.com$ [NC]

RewriteCond %{HTTP_USER_AGENT} !googlebot-image [NC]
RewriteCond %{HTTP_USER_AGENT} !googlebot [NC]
RewriteCond %{HTTP_USER_AGENT} !googlebot-news [NC]
RewriteCond %{HTTP_USER_AGENT} !googlebot-video [NC]
RewriteCond %{HTTP_USER_AGENT} !googlebot-mobile [NC]
RewriteCond %{HTTP_USER_AGENT} !mediapartners-google [NC]
RewriteCond %{HTTP_USER_AGENT} !mediapartners [NC]
RewriteCond %{HTTP_USER_AGENT} !adsbot-google [NC]
RewriteCond %{HTTP_USER_AGENT} !bingbot [NC]
RewriteCond %{HTTP_USER_AGENT} !facebookexternalhit [NC]
RewriteCond %{HTTP_USER_AGENT} !linkedinbot [NC]
RewriteCond %{HTTP_USER_AGENT} !baiduspider [NC]
RewriteCond %{HTTP_USER_AGENT} !duckduckbot [NC]
RewriteCond %{HTTP_USER_AGENT} !yandex [NC]
RewriteCond %{HTTP_USER_AGENT} !sogou [NC]
RewriteCond %{HTTP_USER_AGENT} !twitterbot [NC]
RewriteCond %{HTTP_USER_AGENT} !pinterest [NC]
RewriteCond %{HTTP_USER_AGENT} !photon [NC]
RewriteCond %{HTTP_USER_AGENT} !tineye [NC]
RewriteCond %{HTTP_USER_AGENT} !ggpht [NC]

RewriteRule (.*)wp-content/uploads/(.*\.(jpg|jpeg|gif|png))$ $1watermark.php?p=c&q=90&src=wp-content/uploads/$2
# END Anti-leech

The first block above is a list of websites that we DON’T want to block from seeing our real images. It is very important for you to list your own website here, as well as any other third-party sites that you use that need to access images on your server. You may also want to list certain social media sites here as well.

The second block is a list of user agents to allow. I highly suggest you copy this section exactly as it will make sure that search engines still see your normal images and therefore continue to index them. These rules also contain exceptions for Twitter, Facebook, Google+ and Pinterest, so that your image previews will still work when someone (or yourself) shares the page on one of those services.

    Keep In Touch

    Get the latest posts by email, plus exclusive content, offers, and free stuff just for subscribers.

    (required)
    (required)

    The final line tells the server that when none of the above conditions are met, it should replace the image requested according to the script in “watermark.php”. This line assumes you are using WordPress, and that your images are in the default location. If not, alter this accordingly.

    Now, copy the text below into a new text document and save it as “watermark.php”. Upload it to the public_html folder on your server.

    <?php
    //we tell the server to treat this file as if it were an image
    header('Content-type: image/jpeg');
    //image file path
    $img = $_GET['src'];
    //watermark position
    $p = $_GET['p']; if(!$p) $p = 'br';
    /*
    p can be anything from the following list:
    tl = top left
    tc = top center
    tr = top right
    cl = center left
    c = center of the image
    cr = center right
    bl = bottom left
    bc = bottom center
    br = bottom right
    */
    //watermarked image quality
    $q = $_GET['q'];
    //if the quality field is missing or is not on the 0 to 100 scale then we set the quality to 93
    if(!$q || $q100) $q = '95';
    $filetype = substr($img,strlen($img)-4,4);
    $filetype = strtolower($filetype);
    if($filetype == ".gif") $image = @imagecreatefromgif($img);
    if($filetype == ".jpg") $image = @imagecreatefromjpeg($img);
    if($filetype == ".png") $image = @imagecreatefrompng($img);
    if (!$image) die();
    //getting the image size for the original image
    $img_w = imagesx($image);
    $img_h = imagesy($image);
    /*
    //if the filename has 150x150 in it's name then we don't apply the watermark
    if (eregi("150x150", $img)) {
        imagejpeg($image, null, $q); die();
    } else {
        $watermark = @imagecreatefrompng('watermark.png');
    }
    */
    //if you want to use the watermark only on bigger images then use this instead of the condition above
    if ($img_w < "350") {//if image width is less then 150 pixels
        imagejpeg($image, null, $q); die();
    } else {
        $watermark = @imagecreatefrompng('watermark.png');
    }
    //getting the image size for the watermark
    $w_w = imagesx($watermark);
    $w_h = imagesy($watermark);
    if($p == "tl") {
        $dest_x = 0;
        $dest_y = 0;
    } elseif ($p == "tc") {
        $dest_x = ($img_w - $w_w)/2;
        $dest_y = 0;
    } elseif ($p == "tr") {
        $dest_x = $img_w - $w_w;
        $dest_y = 0;
    } elseif ($p == "cl") {
        $dest_x = 0;
        $dest_y = ($img_h - $w_h)/2;
    } elseif ($p == "c") {
        $dest_x = ($img_w - $w_w)/2;
        $dest_y = ($img_h - $w_h)/2;
    } elseif ($p == "cr") {
        $dest_x = $img_w - $w_w;
        $dest_y = ($img_h - $w_h)/2;
    } elseif ($p == "bl") {
        $dest_x = 0;
        $dest_y = $img_h - $w_h;
    } elseif ($p == "bc") {
        $dest_x = ($img_w - $w_w)/2;
        $dest_y = $img_h - $w_h;
    } elseif ($p == "br") {
        $dest_x = $img_w - $w_w;
        $dest_y = $img_h - $w_h;
    }
    imagecopy($image, $watermark, $dest_x, $dest_y, 0, 0, $w_w, $w_h);
    imagejpeg($image, null, $q);
    imagedestroy($image);
    imagedestroy($watermark);
    ?>
    Watermark

    Finally to make all of this work, your need a transparent PNG file to create the watermark from. See the image on the right for an example.

    Name it “watermark.png” and upload it to your public_html directory.

    And that’s it. To test if it’s all working, visit Google image search and type: site:yourwebsite.com

    I hope this guide is of some use to you. If it was, please leave a comment below and consider linking to this page and sharing it using the buttons below so that other can find it too.

    Please note that I cannot provide technical support through the comments here. I have been getting a lot of comments asking questions that are already answered in the article. If you are still having trouble then please re-read the article and look for any mistakes you may have made. If you are certain you have not made any mistakes then you likely have a server configuration issue that I cannot resolve for you.