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.

Conclusion

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

..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"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<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>
</body></html>

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:

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:

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 http://www.website.com/, 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:

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

Yes, I removed the [or] operators, this is my final hotlink strategy with the following not working

a) my ip address is not allowed to access files directly :-(
b) some swf files call on elements of other swf files within the same site this also leads to the redirect.swf statement being called which shouldn’t happen

I commented out the ‘check if the filename exists’ as I have over several thousand swf files and it was slowing things down.

I took out the blank referrer as several thousand people were hotlinking with no referer.

So far my friends can hotlink my assets and I can see lots of hotlinking peeps being redirected to the redirect.swf other than the problems mentioned above it beats over 400 lines of rules I had before.

That’s certainly complex, but there is no reason that the directives can’t be resolved logically. If this were my project, I would begin with the most basic set of directives possible, like maybe just allowing yourself access to the files. Begin with what is most important, test it and play with it for awhile, and then add another rule. With HTAccess, this is the approach that I have learned to use. Building things up in incremental steps keeps things logical and under your control. When something slips up, you will know exactly what caused the issue. I hope this general advice helps — unfortunately I don’t have time for more specific analyses.
Regards,
Jeff

I ran into this same issue when helping someone else with their cPanel server. I talked to cPanel support, they were able to duplicate this and have said:

“…able to replicate the issue with that server as well, and have replicated it in our test environments as well. I’ve submitted an internal bug report for this issue, and our development team will be looking into it in the near future. In the meantime, you may want to consider editing .htaccess via “vi” or another local text editor, as a workaround until a fix is propagated to a future cPanel release…”

I found the final code worked great for WordPress MU or multisite with the subdomain setup.

However I am also using a domain mapping plugin that allows users to map any domain. So I tried adding the IP address of the main site, since this is what the mapped domains are pointed at. But the mapped domains still were treated as outside sites.

The control of that (hot linking denial) is not via the plugin. Instead, edit the directives on the physical (or virtual) server. You could, for example, use .htaccess to allow specific domains to hot link, but deny all others.

Check the section of Jeff Starr’s post where he has code that begins with the comment:

# additional site access

or even the section with the beginning comment of:

# allow all requests from your ip address

and include the IP address of the remote, each on a new line (using the correct syntax).

The problem is a system where users can signup on their own for domain mapping. Manually adding domains or IPs is impossible. We are talking hundreds, even thousands of domains.

My only best guess is to create a plugin that dynamically writes to htaccess as each domain is mapped, not a huge deal but I am hoping there is something in that process that could be used to know these are mapped domains. They are afterall pointing at the main IP address – that info must be in there somewhere, no?

If there was a way to detect that, it would be much simpler. I just don’t know enough about that side of things to know if this is possible.