By Jonathan 30 Comments

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

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.

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.

//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);

WatermarkFinally 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:

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.


If you liked it, please take a moment to share it!

Comments are closed on this post. Most likely it's because I just don't want to talk about it anymore, so please don't contact me about this topic. At all. Be respectful. Thank you.


  • Michael says:

    Jonathan, your method is not secure enough and is very easy to bypass – for everyone!

    Let us take an image of yours. This one for example:
    Click on it and see what happens. Right, that copyright information shows up. But wait, right click on the image and click on ‘open in new tab’ (use Chrome or Firefox for this). Boom! See what happens? You know about any method to fix this? Because I’m looking for something similar to that for my website.

    • Jonathan says:

      I never made any claims that the method was totally secure. It’s not, and no method is. That’s why I still use watermarks in the images themselves. That being said, the goal of this is just to stop people from being able to view full images in Google image search without clicking through to the website. I think it largely accomplishes this goal. Your workaround only works in Chrome as far as I can tell, Firefox and Internet Explorer do not have an “open in a new tab” option for images accessed directly. And even then it is unlikely most people would think to try it (the one’s that would aren’t going to be stopped by any image protection scheme anyway).

      • Michael says:

        Well Jonathan, your absolutely right and I agree with you. Securest method is not to upload the pictures at all ;) However thank you for this great method which already stops most of the internet users!
        By the way I just accidentally clicked on ‘open in new tab’.


  • Nobody Special says:

    A note to the unwashed masses that think anti-hotlinking using this method is a good idea: You’re wrong.

    If you want some sites to get the real images and not others, then DO NOT USE THE REFERRER. If I want your original images all I have to do is set mine to ‘googlebot’ and your protection scheme falls flat on its face.

    Ignore all of the above and filter based on netblock, even better, don’t even bother with any of this at all.

    • Jonathan says:

      A note to Nobody Special who thinks he’s being clever: You’re wrong.

      The “unwashed masses”, as you so politely call them, don’t know the first thing about how to change their referrer. They are the people who just browse Google images and take what they want because Google makes it convenient and easy for them to do so. This method absolutely DOES protect your images from those people, and those people are the vast majority of image thieves. At the very least it will force them to visit your website before stealing your image.

      No it will not stop a pro, which is why I advocate using semi-transparent watermarks on the image file itself as well.

      Thanks for your (obnoxious) two cents, Nobody. Feel free to fuck right off now. :-)

  • Juanma says:

    With this, no fail (I get 500 internal error). missing file close ()

    FileETag None

    Header unset ETag
    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”

    Thank you very much Jonathan Timar by mail.
    But still I am not able to output the png, you could spend your file watermark. Please.

    • Jonathan says:


      I am very sorry but I am simply not able to troubleshoot errors in server configurations. Everything you need to get this working is in the article. Double check that you copied everything over exactly.

      Sorry I cannot help further.


  • Manners says:

    Thanks for the reply. Perhaps your item a) was supposed to answer both my questions, but just in case my question b) went unanswered:

    Is there a (simple) way to redirect the “view image” button to the actual page where the image is located?

    In the last week, I’ve been looking all over the Internet for an answer. I’ve even tried different languages, but NO luck. Yet, obviously it can be done — as has been doing it for months…

    Thanks again. (And apologies if you’d already answered that.)

    • Jonathan says:

      Sorry, I must have glazed over that part because I don’t have an answer for you. I know that it is possible, there are a couple of WordPress plugins that do it. I am not up to date on how well those plugins are functioning. I know when I tried them I found one of them to be unreliable and caused my images to disappear from Google over time (not what I wanted) and the other plugin was a bloated mess that was difficult to understand and set up properly.


  • Manners says:


    I’ve got a couple of questions, hopefully you can help:

    a) is there a(n easy) way to do what is doing? No watermark; just a smaller version of their images on Google Images and the like.

    b) Is there a(n easy) way to redirect the visitor who clicks on the “view image” button to the actual PAGE where the image is located? Once again, see results on Google Images.

    There’s now an “image mismatch” penalty that Google has imposed. Not sure how that’s going to work, but certain forms of hotlinking protection — Google doesn’t specify which ones (watermarks??) — could be penalized.

    Google wants access to your images (under the guise of “providing a good user experience” [by stealing your content and making it easier for them to do same]). They’re huge; they have their lackeys everywhere; they can do it…

    • Jonathan says:

      a.) Fansshare used to do things the way I am doing it, I don’t know why they changed their system, but whatever they are doing now should be easy enough to implement. Unfortunately I am unable to help with this.

      b.) The type of protection I describe above should be undetectable to Google because Googlebot itself is served the real image. That’s why indexing occurs normally etc. Only people searching are shown the watermarked images, Google sees the real ones.

  • Goran says:

    Hi Jonathan,

    thank you for your effort to help us.

    when I add:
    # Disable client side caching of images to stop Google and Bing image theft
    Header unset Pragma
    FileETag None
    Header unset ETag

    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”

    I get 500 internal error. Everything else works. It’s above wordpress rewrite rules. Chris was mentioning that there is ; missing somewhere?
    Do you know why do I get this error? Thank you very much

    • Jonathan says:

      Unfortunately .htaccess can be complicated and it’s very picky about syntax and as a result I cannot help you with troubleshooting. I can only tell you that this is the exact code that I use on my own sites and it’s working perfectly.

  • Corrine says:

    eeeeeeeeeeeeek! I don’t even know where to start with this, but so need it. I am going to study it and get it ready for when I publish my wordpress blog.

    Hotlinking and scraper sites have been having a field day with my blog it has been a nightmare.

  • Chris says:

    Sorry to keep adding here, but will this effect my website’s ranking? I’m concerned about the “caching” and how this modification disables it.

  • Chris says:

    Hi there, thanks for the code. Want to make a few comments:

    1) You’re missing

    2) I have over 10 000 images uploaded. After implementing the code on a non-WP website, 95% of the images, when searching on Google and then clicked, appear small. Before the change, they appeared large (like their actual size while searching them). I am wondering why this is? Why doesn’t it continue to show them big, but have this watermark on top of it?

    3) 5% of the images are coming up correctly, with the transparent background on top of the original picture.

    What should I do?

    • Jonathan says:

      1.) I’m missing? Not sure what that means, sorry.

      2.) I don’t know why this is happening, sorry. If you included your URL I could check it out myself and possible be able to tell you, but you didn’t.

      3.) My educated guess is that this is because Google caches images on it’s own servers, and 5% of your images have not previously been cached. I can only guess at this, sorry.

  • Yahya says:

    Greetings ,

    My website is hosted on blogger.Can you please guide me if there’s any way that it works on blogger ? or any other way .. Thanks


  • Veer says:

    when i try to add the server side disabling code into my .htaccess i get 500 internal server error.

    • Jonathan says:

      Hi Veer,

      If you are using WordPress then the .htaccess rules that WordPress adds have to be the last items in the .htaccess file. My guess is that you added the code above after those rules.

      If not, then I am afraid I don’t know, sorry. :-(

  • Can you give us an example of a server-side caching plugin? Do you mean like WP-Cache or something like that?

    Is Google still spidering your photos properly?

    • Jonathan says:

      Hi Bryan,

      Yes, like Wp-Cache, although that one in particular is outdated. I personally use Lite Cache, and find it works perfectly.

      Google is still spidering my photos normally. This method is undetectable to Google (unless they checked manually, which is highly unlikely). I have added many photos to my site since I started using this solution, and they have all been picked up on Google image search as normal.



  • Thanks for the info.
    This seems pretty comprehensive and should work (I guess) for non-wordpress sites as well.
    My traffic has been steadily falling after implementing IMA Guard, though I am not sure IMA Guard is the reason.
    If I was making more than £10 a month from my photo web site I would spend some more time looking into this! ::-)

    • Jonathan says:

      It’s more about general principle for me than anything else.

      I can’t say for sure about the Imaguard plugin, but I do not that when I had it active, all of my images were de-indexed from Google completely. I don’t think it’s the watermarking aspect of the plugin, but the way it handles the redirection. I think there is a flaw.

      The other plugin, ByREV WP-PICShield, seemed to work more reliably, but it was unnecessarily complex in my view. I like the KISS approach whenever possible.

      None of this solves the problem of Google being entirely too big and powerful, with absolutely no checks and balances on its activities.