Save 25% on Wizard’s SQL for WP w/ code: WIZARDSQL
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! ;)


Jeff Starr
About the Author
Jeff Starr = Designer. Developer. Producer. Writer. Editor. Etc.
Blackhole Pro: Trap bad bots in a virtual black hole.

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

  1. Avatar photo

    Hi Mike,

    1) That’s the beauty of this method – it works for any TLD

    2) Yes, as far as I can tell (it all depends on context)

    3) Yes, that is true, but make sure to test everything and then keep an eye on access logs

  2. Avatar photo

    I thought I had it all working using the long form, but then I got clever enough to test it. Hotlinkers are getting the 403, it has confused a half dozen of them enough to go to the URL, directly in the address bar I guess, in which case they get a 403. However I wanted to show this image.

    I originally did not edit this line:

    # RewriteCond %{REQUEST_URI} !^/hotlink\.jpg$ [NC]

    Other than commenting it. I’ve since done some testing and none of the images are getting shown that are outside the /OffSite/ directory which I created to allow me to post images in certain forums years ago, but I thought I had everything working. This is what I put for the link in my .htaccess file now

    RewriteCond %{REQUEST_URI} !^/CommonImages/doNotHotLink\. [NC]

    I guess I’ll try the full URI next.

    Oh here is a test post in an obscure forum:

  3. Avatar photo

    The rewrite rules in the blog subdomain does not get along with the .htacces file in this article. I can modify the one in the blog subdomain, even just including the rules below, but then all the images in my blog disappear…

    Here is what was in my blog subdomain for my WordPress installation. I know there are wordpress plugins to prevent hotlinking maybe I need one of those… Your code works for my other subdomain(s) and my main domain.

    # BEGIN WordPress

    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    # END WordPress

  4. Avatar photo


    Thank you very much for this elaborate post. I would like to give back in the form of a nice addition, maybe even something you would like to add to your article.

    You have discussed how to catch leechers and point them to either an image, a 403 error or perhaps even your custom 404 page.

    I would like to share my solution. What I wanted to achieve was redirecting traffic to the relevant pages rather than my home page or custom 404 page. The reason I want to do this is because I offer 30 – 120 minutes long audio mixes, and I want people who click a hotlink to land on the article post, rather than download the mix and completely bypass my website =(

    I used your RewriteCond’s and used the following RewriteRule:

    RewriteRule ^([^/]+).(mp3|zip|rar)$$1 [R=301,L]

    I place this .htaccess inside /downloads/dubstep/ where my dubstep mixes are located.

    For example, Excision’s Shambhala 2010 Dubstep Mix is a mix with filename excision-shambhala-2010-dubstep-mix.mp3 and the permalink to the post is /dubstep/excision-shambhala-2010-dubstep-mix/

    Note how the filename and permalink closely match.

    The .htaccess uses the requested filename to find the related post and redirect the user there.

    It’s also possible to do a 2-step redirect if your post’s permalinks don’t match the filenames.

    For example, you could have underscores (” _ “) in your filename and dashes (” – “) in your permalink structure.

    In your .htaccess you could redirect to a redirect.php file, which transforms the text and then redirects to the related acticle, for example:

    RewriteRule ^([^/]+).(mp3|zip|rar)$$1 [R=301,L]

    The redirect.php file would have:

    I hope I made some sense…

  5. Avatar photo

    Sorry, the PHP of course didn’t get through the comments:


    $file = $_GET['file'];
    $category = $_GET['category'];
         $article = preg_replace('/_-_/', '-', $file);
         $article = preg_replace('/_/', '-', $article);
         $article = strtolower($article);


    Hope this came through, otherwise I give up =P

  6. Avatar photo
    Jeff Starr 2011/01/17 3:16 pm

    Awesome, Marc, Thanks for sharing :)

    And apologies for the mangled code :P

  7. Ultimate?

    Authors of malicious crawlers will appreciate seeing your suggested “exempt from blocking cases where referer header is blank”… and will try their requests twice, first spoofing google as the referer and again, using an empty referer.

    If you piss around with conditionally serving content, GOOGLE crawler may catch ya… and ding you.

    Your “hair hole” suits the pervs of the world, but what about my kids? While browsing a forum (or wherever) containing user-linked content, they wind up being exposed to hairy hole pics thanks, in part, to your senselessness. Pervs may even INTENTIONALLY link to your images if they discover linking via bbcode posts will result in perv pics being displayed? Who wins? Not the kids viewing the site. Not the forum site owner — in fact, once the appearance of perv images is reported, what recourse can the admin take? Probably will add your to a blacklist, forbidding any posting any links to your entire domain, period (not just image URLs). I’m saying: your advice to serve “hairy hole” pics is ill-advised.

    So, I would grade this article as a “B” or perhaps a “B+”. I’ll chalk up your claim of “Ultimate” as SEO bait .

  8. Avatar photo
    Craig Anderson 2011/08/04 4:16 am

    Great read, very helpful. One thing that I am wondering is if there is a way to target a specific type of bandwidth stealing: people who post direct links to images, rather than people who embed those images in other websites. I would like to do one thing for embedded images (serve them a different image) and another thing for those direct image links (redirect them to a specific full page).


  9. Avatar photo

    Hey Craig, great question. This article provides some great ways to block/redirect traffic using Apache’s mod_rewrite:

    To redirect direct requests for your images, something like this should do the trick (combining techniques from that article):

    <IfModule mod_rewrite.c> RewriteCond %{THE_REQUEST} \.(jpg|png|gif) [NC] RewriteCond %{HTTP_REFERER} !^http://(www\.)?.*(-|.)?example(-|.).*$ [NC] RewriteRule .* [F,NC,L] </IfModule>

    Just change the “example” part in the 2nd line to match your domain, and then customize the “special-page” in the last line to whatever you prefer.

    This is just off the top of my head, so I haven’t tested it, and there are probably other ways of doing the job as well.

    Good luck!

  10. hi,

    how do i implement this if my webserver is using NGINX? it does not have mod_rewrite or htaccess.


  11. Thanks for posting this.

    I’ve been having trouble with hotlink protection on my site. I am using Cpanel’s hotlink protection and have been unable to allow acces to Google images etc.

    Reading thru the comments I see that my situation is the same as that described by Ububtulinux help.

    Hopefully now I’ll finaly be able to get this working properly.

    Many thanks

  12. I used your code hoping it would solve my problem, but alas it did not. While it does stop image hot linking, the redirect does not work no matter what I do. As such I am unable to serve up my “angry” image. I believe this is because it conflicts with the rewrite rules of WordPress Multisite as if I temporarily remove those rules, the “replacement” image does get served.

    Obviously though, removing those rules is not an option.

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 »
WP Themes In Depth: Build and sell awesome WordPress themes.
Daylight savings is a complete waste of time and needs to be eliminated.
Got a couple of snow days here in mid-March. Fortunately it's not sticking.
I handle all email in real time as it comes in, perpetually clear inbox for years now.
Added some nice features to Wutsearch search engine launchpad. Now 21 engines!
.wp TLD plz :)
Nice collection of free SEO APIs and user-agent lookups for Googlebot, Bingbot, Applebot, YandexBot, and more.
90% of online customer support is just explaining how to do basic troubleshooting.
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.