Selective hotlinking prevention through .htaccess
Purpose of this dorky little tutorial
This tutorial describes how to selectively limit hotlinking on your website. You can block hotlinking of most of the images on your website, but allow certain directories to be hotlinked, since this can be useful for you (the website owner).
UPDATE: I’ve published an updated tutorial that improves upon the approach outlined here. I recommend it over this approach. See Preventing image hotlinking: An improved tutorial.
Overview of hotlinking
I’ve read a lot of tutorials on the Web about hotlinking. It’s a common problem—users steal other sites’ bandwidth by directly linking to images on the external website. Hotlinking is particularly a problem with the many forums and message boards on the Web that allow users to insert images. These can’t be kept on the host site, so the user hotlinks them when creating the <img> tag.
AltLab’s useful site defines hotlinking as:
Bandwidth theft or “hotlinking” is direct linking to a website’s files (images, video, etc.). An example would be using an <IMG> tag to display a JPEG image you found on someone else’s web page so it will appear on your own site, journal, weblog, forum posting, etc.
This site also provides a “test” to tell whether your images can be hotlinked. But there’s a simple way to prevent hotlinking, and that’s through the use of regular expressions in your .htaccess file. Using the mod_rewrite module of Apache, you give your Web server this instruction: “If the image being loaded has a referrer, or source URL, that is different from my website, don’t allow it.”
One piece of information that’s lacking from many of the tutorials on the Web is how best to go about structuring your .htaccess files. That is, often you’ll want to limit hotlinking from most of your images, but allow it on others, such as when you (the all-powerful website owner) want to hotlink images from your own site. If you prevent hotlinking of all images from your site, you’ve screwed yourself! So you have to know how to do it selectively, and you have to know a small bit about how mod_rewrite works.
Before you start, make a game plan
Let’s say you’ve got the website http://www.dorkytutorial.com and you want to prevent hotlinking of its images. However, you want to be able to use images from http://www.dorkytutorial.com/vacation/ on another site. Here is what you need to do:
- Put your main anti-hotlinking rules in http://www.dorkytutorial.com/.htaccess, i.e. at the root-level
- “Turn off” the anti-hotlinking in another .htaccess at http://www.dorkytutorial.com/vacation/.htaccess
Ready, set, go? All right.
Blocking hotlinking site-wide
Many variations exist on the specific regular expressions you can write, but they all accomplish the same thing. In the actual rewrite component, you can either throw the client a FORBIDDEN (meaning the image ain’t here), or you can redirect it to another image (either something bitching about their hotlinking, or something suitably offensive).
Here’s my anti-hotlinking .htaccess instructions:
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://([-a-z0-9]+\.)?dorkytutorial\.com [NC]
RewriteRule \.(gif|jpe?g|png)$ - [F,NC,L]
Lines 2 and 3 establish the conditions for blocking an image from being rewritten. Line 2 says, to block client loading of the image, “the referrer must be not null.” Line 3 says, to block client loading of the image, “the referrer must not come from any domain of dorkytutorial.com”—and the “NC” part makes that check not case sensitive.
Line 4 checks that the filetype is a GIF, JPEG, JPG, or PNG, and if so, sends an HTTP 403 response of FORBIDDEN to the client (that’s the “F” part on the end). Also, the “L” flag means that this is the last rule to process, so that if an HTTP request matches this rule, don’t bother stepping through any further rewrite rules in the .htaccess. This one is mostly for effiency’s sake.
Selectively allowing hotlinking
Okay, now we’ve blocked hotlinked site-wide at dorkytutorial.com. But what about allowing it in the vacation/ directory? To do that, we need to invoke the RewriteEngine again, because as Apache serves files from the vacation/ directory, it will “override” the rewrites of the root-level .htaccess with vacation/.htaccess. If we invoke a dummy rule that does nothing to stop hotlinking, we’ll be set. And here it is—place this in http://www.dorkytutorial.com/vacation/.htaccess to override the main anti-hotlinking instructions:
RewriteEngine on
RewriteRule ^.*$ -
Big props to Ten from my work and WebPimps’ .htaccess Code Tricks & Tips for alerting me to this.
June 23rd, 2004 at 9:44 pm
thanks for the breakdown on L flag. Couldn’t figure out why subsequent rules weren’t running…
December 22nd, 2004 at 1:21 am
Thanks a lot! It’s easy to find out how to block direct-linking with .htaccess, but your site is the ony one I’ve seen so far that shows how to unblock a subdirectory. That’s just what I needed to know.
April 2nd, 2005 at 9:46 am
I still don’t understand how you can allow certain sites to hotlink.
April 21st, 2005 at 1:38 am
I tried many different .htaccess files like yours and they just don’t seem to work for me. I looked at the apache modules.conf and mod_rewrite is in there. Does it need turned on elsewhere in the apache config?
April 21st, 2005 at 7:28 am
Jason,
Do you have Apache set up so as to AllowOverrides?
Also, this new page lays out a better anti-hotlinking setup.
May 15th, 2005 at 12:51 am
Thanks for the script…works perfectly! Those hotlinkers were pissing me off, but now I don’t have to worry about it.
June 12th, 2005 at 12:48 am
Thanks much. I’ve tried a few hotlinking strings but yours works like a dream.
September 29th, 2005 at 10:26 pm
Thanks - very helpful!
October 11th, 2005 at 4:36 am
Thanks!
But only a *$ was needed for my site, cause it would not open a new page in a new frame…
RewriteCond %{HTTP_REFERER} !^https?://www.example.com/.*$ [NC]
December 5th, 2005 at 10:45 am
please overide it for me
February 26th, 2006 at 4:56 am
Thanks for this tutorial! I had already written my rules, but I had a bug on IE that prevented right click saves from my site (I actually block only large files .avi .zip and so on).
And thanks also for the L flag … I didn’t know it! Good Job
November 28th, 2006 at 3:19 pm
After reading this helpful tutorial, I still have an issue, and I am curious to know whether there is a solution. I understand the direction the tutorial explains, but I would like to ask outside the box, if I might…
I am trying to prevent direct linking to an image on my site. I can block hot linking directly to the folder containing the image, and I can block any direct access to the specific image. I am trying to do this to prevent any ambitious outside application, or even a user entering the direct link in a browser, from mining an image.
To block direct access to the folder and issue a redirect, I use this:
RedirectMatch temp ^/someFolder/$ http://someWebSite.com/
To block direct access to the specific file within the folder and issue a redirect, I use this:
RedirectMatch temp ^/someFolder/someImage.jpg$ http://someWebSite.com/
When a potential thief searches for a specific folder containing the image, and is sophisticated to locate the folder, simply blocking the folder does not prevent the thief from retrieving the image directly, if they know the identity of the image. The specific image identity must be in the “RedirectMatch” to prevent direct access.
My problem is I do not know how to place a condition within the .htaccess file, which would allow my own site to access the image file, while disallowing outside direct access. Since I am pointing to a specific image file with the blocking code listed above; my own website can not display the blocked image.
I apologize for the length of this question. I just need some incremental guidance, if you have a moment, to correct this problem of mine.
Thank you in advance,
jim k
November 28th, 2006 at 4:47 pm
Jim K:
I would do 2 things in your htaccess file.
1. Disable directory indexes. Use this directive in the .htaccess file:
2. Require the HTTP_REFERER value (that is, the website the user is coming from) for requests of image files to come from your website. That means that you can link to images from your site and embed images in your site, but it’s more difficult to others to link/use them.
Note1: If you do this, you won’t be able to type in the URL to the image yourself. You have to link to it from your site.
Note2: that this is not a foolproof method. If I know the image URL, I can easily spoof the HTTP_REFERER value and get it. This technique keeps most people away but is by no means secure.
Here’s the full .htaccess file:
Make sure that the
RewriteCondline above is all on one line.November 29th, 2006 at 12:34 am
Dear Tom,
Thank you for your very quick reply…
I will work on this suggestion tonight and tell you how it went, possibly sometime tomorrow. I have a new website with a new host, and it seems that I also have a new version of cPanel, where many .htaccess commands are initiated from this panel. I notice that this new cPanel places several .htaccess files into folders (directories) I wish to protect, and the automatically links these “sub” .htaccess files back to the main .htaccess, compared to simply writing my own.
That said, I do realize most of this exercise might be very futile, and I appreciate your feedback, and more so, your help.
I will contact you shortly…
Thank you again,
jim k
November 29th, 2006 at 1:32 am
Dear Tom,
Thank you kind sir…
It works as advertised. That said, I see your code will generate a 403 error, so maybe I need to address this with a different error code message, which in turn generates a 404 error, because I do not have these error pages set up to respond properly.
I really do not wish to let the data miner know he is only a few inches from pay dirt. So if I have a different error message appear, instead of “you do not have permission to access this file,” maybe it should read “Bad request,” or “Document not found.”
Good idea, or bad idea?
As a side note, my own Cocoa data miner application gets sent home. :)
Thank you again
jim k
November 29th, 2006 at 1:05 pm
Here’s a tricky little thing I just thought of!
Instead of giving a 403 FORBIDDEN, you can fake a 404 document. Not only does it look like a 404, it returns the 404 HTTP response code, too. It’s essentially indistinguishable from a truly “not found” document.
Save the following as “fake404.php” in the root level of your website:
Here’s the htaccess:
Watch the linebreaks on the htaccess…
December 2nd, 2006 at 9:25 pm
Dear Tom,
Thank you again…
I decided to test a few lines of my own code, after I wrote the last reply, and I found that I could use this line of code in your .htaccess file:
ErrorDocument 403 http://www.someWebSite.com/HTMLPages/ErrorPages/youHaveAProblemPage_error404.html
where an error 403 page is redirected to a 404 error page of my own creation. I also decided to include a simple Java countdown script from ten to zero within the 404 error page, to give the user a chance to read what the error page meant. When the countdown reaches zero, the user is automatically redirected to my home page. I also decided to place a simple “go there now” button at the bottom of the page, for the impatient users, to get back to the home page too. :)
I will give your code a good review.
Again thank you for your time, since I did not expect any information at all, from anyone. Your solution is outstanding to say the least…
jim k
January 8th, 2007 at 7:52 am
Great overall article! Kinda short tho, im going to check out your updated article mentioned above right now.. You may find these links interesting.
code snippets and SSL usage in .htaccess
January 11th, 2007 at 11:01 pm
Hi Tom,
Thanks for an excellant resource addressing a problem Ive been working on for quite a while.
Not to be redundantredundant…..
but will the coding suggestions offered above on this blog answer my question or is there additional tweeking needed.
thanks in advance and here is my question. MusicMan
I have a wmv and flv files that are large
150mb in size. I wish to prevent hotlinking and anyone accessing thru direct URL or framing etc etc.
My goal is to allow the wmv & flv files to load only from within my own site. My bandwidth is costly enough without others having access. I do not wish to have viewers enter passwords etc. to access.
I wish visitors to load video from within my site, thats ok, but any other way to access will be thwarted is my goal. my htaccess coding works preventing hotlinking but not direct url addy access, they can still access. Kudos given if you can help me reach my goal.
Recap:
….no hotlinking
….no framing access
….no direct url entry
Yes from only within my site with no passwords.
Keep up the great work!
MusicMan
ok to reply with email on file as sometimes Im unable to surf often.MM
January 12th, 2007 at 2:10 am
MusicMan: Put this in a .htaccess file where you movies are:
Changing “yourwebsite” above, of course. Also, watch the linebreak… there are 3 lines of code, not 4.
January 12th, 2007 at 10:34 am
Hi Tom,
Thanks for the suggestion and prompt reply. I will give it a college try and see if
this can work for me and I will report back.
All the best for 2007,
MusicMan
February 13th, 2007 at 5:48 am
This doesnt work for me..
Someone else?
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^http://([-a-z0-9]+\.)?yourwebsite\.com [NC]
RewriteRule \.(wmv|flv)$ - [F,NC,L]
February 13th, 2007 at 11:30 am
What doesn’t work? People can still hotlink? Try wannabrowser.com to test.
March 1st, 2007 at 6:46 am
Hi,
I want a .htaccess file which can only allow .css and .js fie for rest it should redirect to home page
September 24th, 2007 at 8:31 pm
I want to redirect the address
Ej I have my page http://www.primary.com
and i have second domain in same place.
http://www.second.com in sudirectory lrbmx i have a index.html
how redirect my second domain in http://www.primary.com
this is my htaccess
RewriteEngine on
RewriteCond %{HTTP_HOST} second.com$ [NC]
RewriteRule ^(.*)$ http:\\www.primary.com\lrbmx\$1 [R]
and the respond
500 Internal Server Error
October 9th, 2007 at 5:55 am
@MusicMan, Tom Sherman, Eppo, etc.
If Windows Media Player (or its embedded plugin) is requesting the file and NOT the browser itself, then you can’t use this method of protection because the HTTP Referer field will always be blank.
If you merely want to offer these files for download-only (so the browser doesn’t pass the request on to WMP, or any other an external application), you can tell the browser to treat this file as an attachment by adjusting the ‘content disposition’ for WMV files on your web server. This will make referer protection work as effectively as it does for images, but it means your viewers will have to download the video files before viewing them.
If that doesn’t meet your requirements, you can take one of several other approaches.
There are third party Apache modules available that attack this problem. I know of two: AHL and ConvertVP. I’ve had limited experience with AHL and it works however it doesn’t scale beyond 1 server. I’ve had no experience with ConvertVP because it only works for Apache 1.3.
The theory behind AHL is quite simple:
All requests for protected video files are denied unless the client’s IP is whitelisted.
The webmaster designates a dummy CSS file to be used as a “permission slip” and when hit, the client’s IP is whitelisted.
The whitelist period is defined by the webmaster (e.g. 30 minutes).
The webmaster includes the CSS file on his page so when the client visits, their browser hits it and their permission slip is granted.
That’s pretty much it.
To stop others from simply hotlinking your CSS file, you can use the same HTTP Referer method of protection you’re using for images.
Also, if one AOL proxy IP is whitelisted, they’re all whitelisted because AOL users rarely go through the same proxy from hit to hit.
There are other approaches to solving this problem but this one is quite simple and effective.
December 28th, 2007 at 6:12 pm
chipawhoo boots
June 17th, 2008 at 2:51 pm
Thanks! It works perfect.