Super Plugin Sale! Your Choice: BOGO or 30% Off »
Web Dev + WordPress + Security

Creating the Ultimate .htaccess Anti-Hotlinking Strategy

[ Image: Illustration of two hands holding a glowing object ] When I wrote my article, Stupid htaccess Tricks, a couple of years ago, hotlink-protection via htaccess was becoming very popular. Many webmasters and bloggers were getting tired of wasting bandwidth on hotlinked resources, and therefore turned to the power of htaccess to protect their content. At that time, there were only a couple of different hotlink-protection methods available on the internet, and the functional difference between them was virtually insignificant. All that was necessary for up-and-coming bloggers-slash-site-administrators to eliminate leaking bandwidth and stolen resources was a relatively straightforward copy-&-paste procedure. Implementing the de facto htaccess hotlink protection code required a simple binary decision: “do you want hotlink-protection or not?”

These days, however, things are not so simple. Today, if you do a search on the internet for “htaccess hotlink protection”, you’ll get the phone book. There are countless mutations of the once tried-and-true htaccess code. There have been significant improvements, but there is also plenty of noise and error lurking among the countless techniques. These days, protecting your site’s assets and conserving bandwidth requires either trusting the first htaccess trick you find, or performing a mind-numbing and time-consuming amount of research to find the htaccess anti-hotlinking strategy that’s perfect for you and your domain. With this article, I do the grunt work for you — analyzing, deconstructing, and cannibalizing a contemporary collection of hotlink-protection methods to create the ultimate htaccess Anti-Hotlinking Strategy.


Before we dig into the critical analysis of the myriad methods, let us continue our practice of catering to all of you copy-&-paste hounds out there by providing the finished product right up front:

# ultimate hotlink protection
<IfModule mod_rewrite.c>
 RewriteEngine on
 RewriteCond %{HTTP_REFERER}     !^$
 RewriteCond %{REQUEST_FILENAME} -f
 RewriteCond %{REQUEST_FILENAME} \.(gif|jpe?g?|png)$           [NC]
 RewriteCond %{HTTP_REFERER}     !^https?://([^.]+\.)?domain\. [NC]
 RewriteRule \.(gif|jpe?g?|png)$                             - [F,NC,L]

Of course, there is much more to the story, as well as a small army of configurational options and possibilities. Nonetheless, if you could care less about the carefully executed logic and reasoning behind the development of this “ultimate” hotlink protection strategy, feel free to copy and paste the entire chunk into your site’s root htaccess file and remember to change the term “domain” to match that of your own. No other adjustments or edits are necessary. Strictly plug-n-play dude.

Now, for the intellectually inquisitive, or for those seeking a deeper understanding of the htaccess rules involved in hotlink protection, may I enthusiastically invite you to “read on”..

Digging in..

Okay, after much deliberation, I have decided to break this down as simply and concisely as possible. Thus, we will consider our collection of anti-hotlinking techniques individually and sequentially. For each method, we will examine the complete code, and then proceed with a brief analysis and summary of the technique’s key aspects. As we deconstruct each strategy, we will collect these gems and cannibalize the best of the best to create the finished product. Additionally, we will check out a few choice code variations and alternate configurational options that serve to expand overall functionality while facilitating a more flexible implementation. Here is a peak at the menu:

  • Hotlink Protection via Stupid htaccess Tricks
  • Hotlink Protection via REQUEST_FILENAME
  • Hotlink protection allowing all variations of the owner’s URL
  • Hotlink protection allowing for multiple domains
  • Streamlined, simplified hotlink protection
  • Code variations and configurational options
  • The ultimate htaccess hotlink-protection strategy
  • Conclusion (for reals this time)

Sound good? Okay, let’s begin..

Hotlink Protection via Stupid htaccess Tricks

In my article, Stupid htaccess Tricks, I present the following method of htaccess hotlink protection:

# hotlink protection via stupid htaccess tricks
<IfModule mod_rewrite.c>
 RewriteEngine on
 RewriteCond %{HTTP_REFERER} !^$
 RewriteCond %{HTTP_REFERER} !^http://(www\.)?domain\.tld/.*$ [NC]
 RewriteRule .*\.(gif|jpg)$ http://www.domain.tld/eatme.jpe   [R,NC,L]

Back when I wrote the article, this basic hotlinking technique was widely employed and taken as the de facto standard method of preventing hotlinking scumbags. Although simplistic, there are several key aspects to this technique:

Error Prevention — The hotlink-protection rules are enclosed within an IfModule container that checks for the availability of the required Apache rewrite module (aka mod_rewrite) before attempting to process any Rewrite directives. This helps to avoid sudden, unwanted errors from crashing your site, and is just good practice in general.

Consolidated Canonicalization — As we will see, many other hotlink-protection methods use two RewriteCond rules for each targeted domain (either blocked or allowed) in order to accommodate for both the www and non-www versions of the site’s URL. Here, we have eliminated redundancy by considering either URL with a single line of code. We do this by preceding the domain with “(www\.)?”, which makes the www optional.

So, from this hotlink-protection ruleset, we will keep the error-preventing IfModule container and the consolidated canonicalization trick. The remainder of this technique is quite common, and will be repeated several times before this article has finished. Let’s move on..

Hotlink Protection via REQUEST_FILENAME

This method of hotlink protection takes a different approach by using the REQUEST_FILENAME parameter in the RewriteCond, thereby targeting the names of the hotlinked files rather than the referring domain:

# hotlink protection via request filename
RewriteEngine On
RewriteCond %{REQUEST_FILENAME}   -f
RewriteCond %{REQUEST_FILENAME}   -d
RewriteCond %{REQUEST_FILENAME}   \.(jpeg|jpg|gif|png)$         [NC]
RewriteCond %{HTTP_REFERER}       !^http://domain\.tld/.*$      [NC]
RewriteRule protected/(.*)        http://domain.tld/hotlink.jpg [R,NC,L]

This is another widely used technique that has been modified in countless ways. This version represents a generalized technique for protecting a specific directory (i.e., /protected/) from hotlink requests. Key aspects of this technique include the following:

Targets the file, not the referrer — Although I am not sure if it is more effective to target the requested file rather than the actual referrer, using REQUEST_FILENAME is known to be quite effective. In this particular example, all requests for .jpg, .jpeg, .gif, and .png images that are located in the “protected” directory will be blocked, unless from the owner’s domain (i.e., http://domain.tld/).

Checks for existence of requested file and directory — This is an added layer of protection that many hotlink protection methods use to further secure their server environment. In the second and third lines in our example, we are checking for the existence of the requested file (-f) and directory (-d). If the requested file and directory actually exist on the server, the remaining rules will be processed. If everything lines up, requests for protected file types will return the hotlink.jpg image; otherwise, the requested image does not exist and hotlink.jpg will not be served. This prevents the serving of your anti-hotlinking image in cases where the requested image does not exist, thus saving you bandwidth and avoiding confusion in general.

Beyond these two features — targeting the file and checking the file/directory— the remainder of this technique is rather common. In addition to these two gems, exclusively protecting a specific directory is also a handy trick. Let’s save these three items in our collective memory and continue with another example..

Hotlink protection allowing all variations of the owner’s URL

Another common implementation of hotlink protection allows image access only for all variations of the owner’s URL, including both www and non-www versions, as well as the IP address and port 80 access for the domain:

# hotlink protection allowing all source urls
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://domain.tld.*$            [NC]
RewriteCond %{HTTP_REFERER} !^http://www.domain.tld.*$        [NC]
RewriteCond %{HTTP_REFERER} !^http://domain.tld:80.*$         [NC]
RewriteCond %{HTTP_REFERER} !^http://www.domain.tld:80.*$     [NC]
RewriteCond %{HTTP_REFERER} !^*$       [NC]
RewriteCond %{HTTP_REFERER} !^*$    [NC]
RewriteRule .*[Jj][Pp][Gg]$|.*[Gg][Ii][Ff]$|.*[Pp][Nn][Gg]$ - [F,NC,L]

This common method incorporates two key aspects that we will cannibalize for our “ultimate” hotlink-protection strategy:

Comprehensive access for the source domain — This technique goes to great lengths to ensure that every possible version of the source domain is allowed open access to all images. Aside from blank referrer requests, all other domains and access attempts are stopped cold. It is unnecessary, however, to employ six lines of code to account for all instances of the host domain. Later in the article, when we integrate this aspect into our improved strategy, we will accomplish the same thing (and more) with only two lines of code.

Accounts for all variations of the target file extensions — The last line of this ruleset specifies which types of images to protect. In this case, we are protecting .jpg, .gif, and .png file types. Even better, we are preventing access via any variation of the file extension itself. File extensions written in uppercase, lowercase, or any combination thereof, are effectively blocked. This is a key aspect of any hotlink protection technique. Fortunately, however, the [NC] specified at the end of the last line makes it unnecessary to specify both uppercase and lowercase letters in each of the file names.

As mentioned, allowing comprehensive access is important, not only for the URL variations specified here, but for any required subdomains as well. Further, this code would benefit from the addition of logical [OR] operators combined with each of the first six [NC] operators. Without explicitly specifying “or” after each line, the conditions are processed with an inherent “and”, meaning that all conditions must apply before the rewrite occurs.

Hotlink protection allowing for multiple domains

In this example, we demonstrate a widely used technique for allowing image access to multiple domains, including Yahoo!, Google, and three additional domains:

# hotlink protection allowing for multiple domains
RewriteEngine on
RewriteCond %{HTTP_REFERER}  .
RewriteCond %{HTTP_REFERER}  !^http://([^.]+\.)?domain-01\. [NC]
RewriteCond %{HTTP_REFERER}  !^http://([^.]+\.)?domain-02\. [NC]
RewriteCond %{HTTP_REFERER}  !^http://([^.]+\.)?domain-03\. [NC]
RewriteCond %{HTTP_REFERER}  !search\?q=cache               [NC]
RewriteCond %{HTTP_REFERER}  !google\.                      [NC]
RewriteCond %{HTTP_REFERER}  !yahoo\.                       [NC]
RewriteCond %{REQUEST_URI}   !^/hotlink\.jpg$               [NC]
RewriteRule \.(gif|jpg|png)$ http://domain.tld/hotlink.jpg  [R,NC,L]

This very useful method enables us to specify additional domains for which to allow image/resource access. There are many situations in which webmasters need to extend access to search engines, feed readers, and associate sites. Here are the key points of this method:

Better “blank-referrer” access — Every serious hotlink protection strategy provides resource access for “blank-referrer” (or “no-referrer”) requests. Blank referrers are commonly associated with third-party ISPs, firewalls, direct requests, and other such situations. Unless you have specific reason to do otherwise, it is highly recommended that you enable access to blank-referrer requests. As seen in previous examples, this is accomplished via “!^$”, which means “not blank.” This example employs the more semantically correct “ . ” (dot), which specifies any character, including …

Comprehensive access for multiple domains, subdomains — Following the pattern presented in the first three lines, we may allow access to as many domains as necessary. Even better, all subdomains associated with the allowed domains are also included via the regex, ([^.]+\.)?, which literally matches virtually any string preceding the domain. Thus, all subdomains are also allowed access.

Access for Google, Yahoo!, et al — Allowing access to the major search engines is a great way to attract new visitors to your site. Of course, there are many possibilities here, depending on your personal site-optimization strategy.

Allow universal access to the hotlink.jpg image — If you are serving a nasty image to the worms that would otherwise steal your bandwidth, it is important that they are able to access it. Many of the hotlink-protection strategies I have seen around the Web somehow fail to accommodate or mention this important aspect. Fortunately, this example reminds us of the fact by allowing complete access to our offensive anti-hotlinking image.

Wow, that’s a lot of useful material from this technique. Let’s add it to our stash and move on..

Streamlined, simplified hotlink protection

In our final example of htaccess hotlink-protection techniques, we examine an effective, streamlined approach:

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://([a-z0-9]+\.)?domain\.tld [NC]
RewriteRule \.(gif|jpe?g|png)$ - [F,NC,L]

Although simple, this method provides two key aspects:

Alternate wildcard regex wildcard string for subdomains — In our previous example, we enable access to all subdomains associated with our allowed domains via regular expression. This method accomplishes the same functionality, only by using an alternate regex statement. Both are effective, but it is good to have an alternative ;)

Condensed file extension list — This example demonstrates a way to consolidate and simplify the list of protected file types by using employing a “?” (i.e., question mark) to signify an optional letter “e” for the .jp(e)g extension. Here, the question mark indicates that the preceding character (the letter “e” in this case) is optional, thereby producing a match for either scenario. This is a useful trick that will serve us well in our final strategy.

Now that we have pillaged a fine collection of contemporary htaccess anti-hotlinking rules, let’s shift gears momentarily and consider several individual htaccess directives involved with hotlink prevention. Many of the following rules are alternate versions of previously considered code.

Code variations and configurational options

Let’s face it, there are often many ways to formulate htaccess directives, especially when it comes to using Apache’s mod_rewrite to prevent hotlinking. Here are a few examples demonstrating variations of previous code examples..

Optimizing the file type list

In our third anti-hotlink example, the following code is used to specify the protected file types:

RewriteRule .*[Jj][Pp][Gg]$|.*[Gg][Ii][Ff]$|.*[Pp][Nn][Gg]$ - [F,NC,L]

That works, but we can simplify it a bit:

RewriteRule \.([Jj][Pp][Gg]|[Gg][Ii][Ff]|[Pp][Nn][Gg])$ - [F,NC,L]

Okay, that’s a little better, but there is still room for improvement. Notice the NC in the brackets. That tells Apache to ignore the casing of characters in the regex string. Thus, explicitly specifying both uppercase and lowercase characters in the list of file types is unnecessary. This fact enables us to simplify the RewriteRule quite significantly:

RewriteRule \.(jpg|gif|png)$ - [F,NC,L]

There, that’s much better — but we can improve it a little bit more by supporting all three types of .jpg files (i.e., .jpg, .jpeg, and .jpe), in addition to .gif and .png files:

RewriteRule \.(jpe?g?|gif|png)$ - [F,NC,L]

Of course, to protect additional file types, simply add another pipe symbol (“|”) followed by the associated file extension. For example, to add several additional file types, we could write something like this:

RewriteRule \.(jpe?g?|gif|png|bmp|tiff?|pic)$ - [F,NC,L]

And yes, we can easily support other, non-image resources files as well. Let’s protect some multimedia files and Microsoft documents:

RewriteRule \.(jpe?g?|gif|png|bmp|tiff?|pic|mp3|doc|xls)$ - [F,NC,L]

Different responses to hotlink requests

In most of the anti-hotlinking rules presented above, the server responds to all blocked resource requests with a “403 – Forbidden” error. This works well enough, but there are many situations where a simple 403 just doesn’t cut the mustard. For example, if you utterly despise hotlinking bandwidth thieves, you may prefer to serve ‘em a piping-hot close-up of your favorite hairy hole (or whatever). To do this, modify the last line in your set of rules as follows:

# serve em a macro shot of your hairy hole
RewriteRule \.(jpe?g?|gif|png)$ http://www.domain.tld/hairy-hole.jpg

Now, when you do this, you want to ensure that you are not blocking access to your hairy-hole.jpg by including a line similar to this directly before the RewriteRule:

# ensure access to your hairy hole jpg
RewriteCond %{REQUEST_URI} !^/hairy-hole\.jpg$ [NC]

Likewise, rather a macro-shot of your nose hole (or ear hole, for that matter), you may want to refer hotlinking scum to a specific file, say “hotlink-policy.html”. In this case, modify the last line in your set of rules as follows:

# send em a web page explaining your hotlink policy
RewriteRule \.(gif|jpe?g|png)$ http://www.domain.tld/hotlink-policy.html

Finally, if you are concerned that your server will not process the RewriteRule correctly unless the requested file type happens to match that of your hairy hole image, you will need to prepare a version of your hairy-hole image in each of the protected file formats.

For example, if you are protecting .jpg, gif, and png file types, and would like to serve hotlinkers a copy of your hairy hole, you will need to prepare a version of the image in each of the three file formats (e.g., hairy-hole.jpg, hairy-hole.gif, and hairy-hole.png). Then, to summon the matching file type when hotlinking is detected, replace the last line in your ruleset as follows:

# serve em a copy of your hairy hole with a matching file type
RewriteRule \.(jpg|gif|png)$ http://domain.tld/hairy-hole.$1 [F,NC,L]

As described, this directive matches the requested file type with a similarly formatted copy of your hairy hole. You know the one. Now, let’s take a look at a complete example using this technique:

# serve hotlinkers a macro shot of your hairy hole
RewriteEngine on
RewriteCond %{HTTP_REFERER}  !^$
RewriteCond %{HTTP_REFERER}  !^http://([a-z0-9]+\.)?domain\.tld [NC]
RewriteCond %{REQUEST_URI}   !^/hairy-hole\.                    [NC]
RewriteRule \.(jpg|gif|png)$ http://domain.tld/hairy-hole.$1    [F,NC,L]

Let’s move on..

Serve ‘em a nice 404 page

A great way to confuse content thieves is to respond to their hotlink requests with an artificial 404 page. To do this, add the following code to htaccess:

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://([a-z0-9]+\.)?domain\.tld      [NC]
RewriteRule ^(.*)\.(gif|jpg|png)$ http://domain.tld/404.php?$1.$2   [NC,L]

..and then place a file called 404.php in the site root (or directory of your choice, just change the htaccess RewriteRule to match the new location). Within the blank 404.php document, copy & paste the following:

<?php header("HTTP/1.0 404 Not Found"); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
<html xmlns="">
<head><title>404 Not Found</title></head>
<body><h1>404 Not Found</h1>
<p>The requested URL /<?php if ($_SERVER['QUERY_STRING']) : echo $_SERVER['QUERY_STRING']; endif; ?> was not found on this server.</p>
<p>Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.</p>

Protect resources on secure domains

As we wrap up the odds and ends, let’s take a look at a simple tweak that will expand hotlink protection to target resources on secure (https://) domain. To achieve this, simply add a “s?” (optional “s” character) after the http portion of the HTTP_REFERER string that represents the site in question. Here is an example:

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://([a-z0-9]+\.)?domain\.tld [NC]
RewriteRule \.(gif|jpe?g|png)$ - [F,NC,L]

At last, the ultimate htaccess hotlink-protection strategy

Now that we have deconstructed a plethora of htaccess hotlink-protection directives, it’s time to throw down the “ultimate” htaccess hotlink-protection strategy. Rather than present a convoluted, one-size-fits-all chunk of heavily commented htaccess code, I provide two different versions, one requiring minimal editing, and another packed with everything:

  • Complete site protection — complete protection, minimal editing.
  • Comprehensive protection — allow multiple sites, your IP, and more.

Version 1) Complete Hotlink Protection

If you are looking for complete hotlink protection for your site and all subdomains, copy & paste the following code into your site’s root htaccess file:

# ultimate hotlink protection
<IfModule mod_rewrite.c>
 RewriteEngine on
 RewriteCond %{HTTP_REFERER}     !^$
 RewriteCond %{REQUEST_FILENAME} -f
 RewriteCond %{REQUEST_FILENAME} \.(gif|jpe?g?|png)$           [NC]
 RewriteCond %{HTTP_REFERER}     !^https?://([^.]+\.)?domain\. [NC]
 RewriteRule \.(gif|jpe?g?|png)$                             - [F,NC,L]

To use the previous code, only one edit is required: change the term “domain” to match your domain. For example, if your domain name is, you would replace “domain” with “website”. Note that this code is set to protect the following file types: .jpg, .jpeg, .jpe, gif, and png. To protect additional files, such as those with the .ico format, simply add “|ico” after the “|png” in both the 6th and 8th lines.

Version 2) Comprehensive Hotlink Protection

For those of you desiring a more robust, flexible solution for hotlink protection, the following code encompasses the entire spectrum of functionality. In order to accommodate multiple features, certain lines of code have been temporarily disabled via comments (i.e., pound signs #). Further, comments are included throughout the code to explain the various options. Having said that, grab a copy, read through the code, and edit to taste:

# ultimate hotlink protection #

# disable directory browsing
# uncomment this option to protect access to directories
# Options -Indexes

# enable the following of symlinks
# uncomment this option if hotlink protection fails to work
# Options +FollowSymLinks

# verify presence of mod rewrite
<IfModule mod_rewrite.c>

 # enable the rewrite engine
 RewriteEngine on

 # check that file exists
 RewriteCond %{REQUEST_FILENAME} -f

 # check for requested file types
 # include additional file types here
 RewriteCond %{REQUEST_FILENAME} \.(gif|jpe?g?|png)$                [NC]

 # allow blank referrer requests
 RewriteCond %{HTTP_REFERER}     !^$

 # allow all requests from your domain
 # edit the domain to match your own
 RewriteCond %{HTTP_REFERER}     !^https?://([^.]+\.)?domain\.      [NC]

 # allow all requests from your ip address
 # edit the ip address to match your own
 RewriteCond %{HTTP_REFERER}     !^https?://123\.123\.123\.123(.*)$ [NC]

 # additional site access
 # include additional sites here replace domain names and or 
 # remove unnecessary lines or add new lines for more sites
 RewriteCond %{HTTP_REFERER}     !^https?://([^.]+\.)?domain_01\.   [NC]
 RewriteCond %{HTTP_REFERER}     !^https?://([^.]+\.)?domain_02\.   [NC]
 RewriteCond %{HTTP_REFERER}     !^https?://([^.]+\.)?domain_03\.   [NC]

 # search engine access
 # include or remove search engines feed readers and other sites
 RewriteCond %{HTTP_REFERER}     !search\?q=cache                   [NC]
 RewriteCond %{HTTP_REFERER}     !google\.                          [NC]
 RewriteCond %{HTTP_REFERER}     !yahoo\.                           [NC]

 # allow access to all requests for your anti hotlink image
 # to serve an image instead of delivering a 403 error
 # uncomment the next line and edit the path and file name
 # RewriteCond %{REQUEST_URI}    !^/hotlink\.jpg$                   [NC]

 # deliver the hotlink image for all requests for the listed files
 # protect additional file types by editing the list below
 # if you are serving a file instead of a 403 
 # uncomment the next line and edit the path and file name
 # RewriteRule \.(gif|jpe?g?|png)$ http://domain.tld/hotlink.jpg    [R,NC,L]

 # return a 403 error when any of the following file types is requested
 # you can protect additional file types by editing the list below
 RewriteRule \.(gif|jpe?g?|png)$                                  - [F,NC,L]

# close the module container

Essentially, both of these hotlink-prevention methods are the same, employing the same underlying htaccess rules. The first version provides the fundamental functionality required to completely protect any domain, and is a generalized version intended for everyday, plug-n-play usage. The second version provides the same comprehensive coverage while also facilitating flexible customization and configuration via strategically implemented code comments.

Wrapping up then, a huge “thank you” goes out to everyone who contributed to the myriad anti-hotlink techniques discussed in this article. And, as is always the case with code presented here at Perishable Press, if you know of any way to improve any of the code examples, please drop a comment or contact me directly. Thanks ;)

Conclusion (for reals this time)

I hope this article has shed some light on the various aspects of hotlink protection via htaccess. Hopefully, the code examples presented here will help beginners gain a clearer understanding of how hotlink prevention works, while reminding a few of the Rewrite veterans in our audience of some of the subtleties associated with mod_rewrite and the process of preventing access to target resources. At the very least, I hope that the “ultimate” htaccess hotlink-protection strategy developed in this article provides webmasters with more control over their resources, images and otherwise. … God bless! ;)


About the Author
Jeff Starr = Web Developer. Security Specialist. WordPress Buff.
Digging Into WordPress: Take your WordPress skills to the next level.

75 responses to “Creating the Ultimate .htaccess Anti-Hotlinking Strategy”

  1. Andreas Kohl 2012/06/17 4:15 am

    Can anybody make a hotlink code for .htaccess with the bellow exceptions:
    google webcache
    google images
    pin it

    Thank you.

  2. Melinda McCaw 2012/07/12 1:23 pm

    I have the Digging Into WordPress book and have used several htaccess tips from there to secure my WordPress sites. One snippet that I would like to use causes issues every time I try to use it. It is the one for protecting WP-Admin files. Every time I add it to my htaccess file I get an Internal Server Error. This is the snippet I am trying to use:


    Order Deny,Allow
    Deny from all
    Allow from

    Any ideas on why this is happening and how to fix it?

  3. Bob Bonomo 2012/08/22 9:49 am

    Well written article but today, 2012, there is a new “menace” that is not addressed for image hotlinking: Facebook
    Because Facebook is smart they know about: RewriteCond %{HTTP_REFERER} !^$
    and conveniently allow their users to bypass hotlinking techniques by blanking out the referrer but use a facebook user agent and now we need to test for that also.
    I found this works just before the checks for %{HTTP_REFERER}: RewriteCond %{HTTP_USER_AGENT} ^facebook.*$ [OR]
    Being “facebooked” is great for a webpage but devastating for just an image.

    • I think you’ll find Facebook makes a small copy of the image when it is officially shared. My anti-hotlinking solution based on this one, seems to stop Facebook hotlinking just fine. Pinterest makes a copy of the image and stores it locally. If people share the page, it works, if they try to hotlink to the image, it doesn’t work.

      I haven’t had problems with Facebook stealing my bandwidth, but I had major problems with people hotlinking to images on my website as their signature on forums, so every page view of every comment they ever made hit my host and ended up costing me money as I posted some stupid pics on my blog in the past.

      Most people want their content shared on Facebook, if you provide the buttons in your blog or whatever everything works out fine, I might do things slightly differently, but again I think the goal of most website owners is to have their content shared, Facebook’s and other official buttons seem to understand this.

      • Bob Bonomo 2012/08/22 1:54 pm

        Kinda disagree on this. This was not a blog or FB page. People want their content shared yes! By having visitors go to their site (adsense, etc) but not when your content is published on their site as if it were their content and your server provides the bandwidth for images.

        Still think FB has done this on purpose. Why block the referer? Because it bypasses hotlink code like in this article. Everyone follows this example… Well the ones that do hotlinking protection.

  4. I can’t seem to reply… I’ve worked with a lot of APIs, Facebook isn’t my favourite but it isn’t the worst, I think Amazon Product API makes you jump through the most hoops and they seem to have taken away features…

    Facebook has like 600 million users. Most people’s websites don’t. Most people are happy for the traffic Facebook, Google, etc. send their way. True hotlinkers don’t send any traffic, they just take bandwidth. I don’t know why Facebook does all the stuff they do, they’re trying to make money. They’re #winning but not as much as they used to be.

    This is a useful solution, I adapted it:

    It is totally possible to block a domain such as, you can complain to Pinterest too. Just leaving out the social sharing buttons makes it harder for people to share content. Facebook users share, bandwidth thieves steal, there is a difference. A lot of what Facebook and other sites do is to try and prove they’re popular and successful at generating sales and page views. If the biggest problem your website has is too many people are sharing your content on Facebook, you’re doing well. Scrapers, plagiarizers, bandwidth thieves, port sniffers, DOS attacks, spammers, etc. are way more problematic than Facebook users.

    If something gets popular on Facebook, I have to do more replying to comments, then a few days later everything is back to normal. It took a lot of time and effort to shut down all the bandwidth thieves.


    • Bob Bonomo 2012/08/23 6:14 pm

      I think somewhere I must have explained it incorrectly.

      This IS an instance of “True hotlinkers don’t send any traffic, they just take bandwidth”. In my logs I see an instance of a high number of accesses for an image only. The page containing the image is never accessed. These FB users ARE sharing information alright. My image. But it uses my bandwidth to do so. Steal my image, I don’t care. Put it on a FB area somewhere and share it from there.

      But to be honest I think most people aren’t even aware they are doing this. They do a G search for an image of someone, find one they like, and insert the link into one of their FB pages or blog or in their signature (your case)… and magically it works and it’s “free” because it’s on the Internet.

      But Facebook knows what’s going on in the background.

      I find myself in the same situation you are/were in with signature thing.

      I’m amazed some hot shot lawyer has not picked up on this and launched a class action for bandwidth theft.

  5. Bob Bonomo 2012/08/23 6:25 pm

    I read that link you posted. That is about what I do too, sorta. I did get the alternate image replacement stuff to work and that is what I do but I serve a very small image file. It ends like this after the domain checks:

    RewriteCond %{REQUEST_URI} !/hotlink.png
    RewriteRule \.(jpg|jpeg|png|gif)$ [NC,R]

  6. The latest edition of Google images somehow is able to bypass the hotlink option, with the referring URL being included within the link:,d.cGE&psig=AFQjCNFNiNi9GPrDTb08xzhCD9inLZKrYQ&ust=1359255892927380

    I assume that’s why they’re able to hotlink anyway. Is there a workaround if someone does a “url=” with your domain or an allowed domain (if someone did put google and bing on the exempt list) at the end of the source link?

  7. Paul Harvey 2013/02/03 6:42 am


    Why isn’t it possible to create a generic version of this script, that doesn’t specify the domain name?

    This looks close, but I have my doubts:


    • Jeff Starr 2013/02/08 4:21 pm

      Yeh, I’ve never seen anything like that before.. have you tried it? It might work, but I would investigate the syntax and test thoroughly.

  8. I’m trying to figure out if it’s a typo or not. In the Version 2) Comprehensive Hotlink Protection all the URL calls have “https” instead of the “http” listed in the single examples above. Is this correct / will it work or should I leave off the “s” in the Comprehensive Hotlink Protection code?

    Thanks for the great article.

    • It’s correct.. if you’ll notice the question mark after each “s”, which means that it’s optional. For example:


      This will match “example” or “examples”, because the “?” makes the “s” optional.

  9. Jack Dorsee 2013/03/24 2:29 pm

    I have 2 sites sites full of gif’s and Is there any optiomn to redirect hotlinks that request the source .gif file to the homepage? Will the Version 1) Complete Hotlink Protection work?

  10. I’ve known for a long time that Google Image search was doing something that got around hotlinking, but once again a forum has posted my image (a screen shot) inline when they should not be able to.

    I did create a directory to allow myself to post images to forums in my solution, but this images isn’t in that directory, it is in my blog images directory.

    Any idea what is doing that allows them to go around this anti-hotlinking solution.

Comments are closed for this post. Something to add? Let me know.
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
SAC Pro: Unlimited chats.
All free plugins updated and ready for WordPress 6.6 dropping next week. Pro plugin updates in the works :)
99% of video thumbnail/previews are pure cringe. Goofy faces = Clickbait.
Crazy that we’re almost halfway thru 2024.
I live right next door to the absolute loudest car in town. And the owner loves to drive it.
8G Firewall now out of beta testing, ready for use on production sites.
It's all about that ad revenue baby.
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.