Prevent Hotlinking to your Images R R




How would you feel if you discovered that someone has been stealing from you? It's the same way I felt when I discovered that some people have linked to my images and reused my tutorials on their sites without my permission, and without giving credit for my work.

My first reaction as a programmer was to do something to prevent these thieves from being able to link to my images and consume my bandwidth. I'll show you how to do this. This is a two part tutorial, so if you haven't read the first part, do so first.

What do you need for this?

1. You should be running your own web server. I run Apache, and this tutorial shows you how to prevent hotlinking on apache server. I'm sure however, you can apply the same ideas to other servers.

2. If you're not running your own server, you should have access to your server configuration files, httpd.conf in the case of apache.

Decisions you have to make

1. You can either create a separate directory for all your images, OR
2. You can protect the whole website, which lets you put images into any folder on your site.

How does this work?

It's simple. Each time a file is requested from your webserver, the request has the url of where the request came from. This info is called the HTTP_REFERER, and it comes amongst other pieces of information in what is called a header.

So we tell apache to examine the header and see the HTTP_REFERER. If the url is from our website, the file is served, otherwise, access is denied to the file.

Our challenge now is how to tell apache to examine the http_referer.

We do this using SetEnvIf. What this does is set an environmental variable if some condition is true. If the http_referer has our site's url, the environment variable will be one value, if not, it's another value. I'm sure you know what an environment variable is. In a nutshell, it's just a variable that is available to your server and your scripts at any time, without being declared first as a global variable.

SetEnvIf Referer "http://ourdomain.com/" Is_Local=1

So the enviromental variable will be called Is_Local, and it will have a value of 1 if the http_referer is http://ourdomain.com.

Next, we must tell apache what to do with the value of the enviromental variable. In short we're saying, unless the value of Is_Local is 1, deny access to the requested file.

We can do this either in our apache config file access.conf, or we can put it in our .htaccess file. I put mine in the .htaccess file. It can also go into httpd.conf.

If we used SetEnvIf Referer "http://ourdomain.com/" Is_Local=1 as-is, apache will deny access to all our subdirectories, and allow only files in the base directory. We don't want that. Quite simply what we want is tell apache to match the url to anything that has http://ourdomain.com/ at the beginning. This is done with Regular Expressions (Regex). In Regex, the carat sign (^) matches characters at the beginning of a string. So let's modify our expression.

SetEnvIf Referer "^http://ourdomain\.com/" Is_Local=1

The stuff inside the quotes is a regex saying "match any string that has http://ourdomain.com/ at the beginning of it".

But what if you have sub domains with url like http://hostName.ourdomain.com/? We want to be able to match those also.

Let's extend our regex to cover that situation.

SetEnvIf Referer "^http://([a-z0-9_]+\.)?ourdomain.com/" Is_Local=1

The regex ([a-z0-9_]+\.)? is telling apache to match any alphanumeric character (i.e. letters and numbers), including underscores. The plus sign means match One Or More characters, and the \. puts a dot at that spot. Finally the question mark says match zero or more of what is in the parenthesis. So http://one.domain.com/ and http://one.two.three.four.five.domain.com/ will all be matched. Wow! what a beauty regex is.

This is not a regular expressions tutorial, so you can find a good tutorial about regex later, if you wish to further customize this tutorial.

Files types to Restrict access for

OK, what is left now is a way to define files to restrict. What files do you want to prevent people from accessing remotely? In other words, the only time someone would be able to access such files is when you create a webpage yourself to show them. Someone cannot just link to such files from their own webpages, thereby stealing your bandwidth.

Since we're dealing with files, we use apache's <FilesMatch> </FilesMatch>. The beauty of <FilesMatch> is that it lets you define string patterns using Regular Expressions. Oh no! Here we go again with the regex stuff.

The general format is

<FilesMatch "files_to_restrict">

Order Allow,Deny
Allow from env=Is_Local

</FilesMatch>

Order Allow,Deny will prevent access to files_to_restrict. Then we tell apache to allow access to the same files from Is_Local, which is our url only. As you may have guessed, env= stands for environmental variable name.

Defining files_to_restrict

What we want now is to identify certain files by their file extensions. Since we want to restrict access to our images, we define regex for files with gif, jpg, jpeg, and png as the extensions. This should take care of most image formats on the web. The regex for that is \.(gif|jpe?g|png)$

<FilesMatch "\.(gif|jpe?g|png)$">

Order Allow,Deny

Allow from env=Is_Local

</FilesMatch>

Let's see what the expression \.(gif|jpe?g|png)$ means. First the $ matches characters at the end of a string. The \. matches the last dot in the filename. jpe?g matches both jpg, and jpeg. As you know e? means match zero or more e. Therefore, jpg and jpeg both satisfy our condition. The | means OR in regular expression. So all together, the expression means match any filename that has gif, jpg, jpeg, or png as the extension.

Got it?

Case sensitivity

One last thing we need to add is case sensitivity in the url. We want apache to see http://ourdomain.com/, http://OURDOMAIN.COM/, http://OurDomain.com/, all as the same thing.

SetEnvIf Referer NoCase "^http://([a-z0-9_]+\.)?ourdomain.com/" Is_Local=1

NoCase removes case sensitivity.

Putting everything together

SetEnvIfNoCase Referer "^http://([a-z0-9_]+\.)?ourdomain.com/" Is_Local=1

<FilesMatch "\.(gif|jpe?g|png)$">

Order Allow,Deny

Allow from env=Is_Local

</FilesMatch>

Put the above inside your .htaccess file, and you should be all set.

We already talked about .htaccess files and how to set them up. If you haven't read it, take a look at it now.

Telling apache to restrict access

Even though we've defined the files we want to restrict access to inside .htaccess, apache by default will not restrict access to any files. You have to tell it to do so. But how? By changing a line in httpd.conf.

In general, you tell apache what to do with a directory, and all the subdirectories under it, by using apache directives
<directory "directoryPath"> </directory>

So let's say we want to stop hotlinking to images on http://ourdomain.com/, and our web documents folder is c:/apache/htdocs/, we will add the following line to apache config file, httpd.conf:

<directory "c:/apache/htdocs/">

AllowOverride All

</directory>

It is very likely that you already have the above directive in your httpd.conf, but it will say AllowOverride None. If that is the case, just change None to All. Save httpd.conf and restart apache.

Now each time your users try to access images within http://ourdomain.com/, access will be granted only if the request came from a page located within http://ourdomain.com/. All other requests get broken images. This stops all hotlinking to your images.

To test this, try to link to one of my images using the img src tag. You should get a broken image. But the same image shows up on my website. Can we say mission accomplished!

Now I can see your hotlinkers scrambling to do something about those broken images they've relied on for so long - ha, ha, ha!! A site with broken images just doesn't look as good.

Restricting access to more file formats

The same idea can be extended to movies, and any other file format you might have on your server. For example, you can use it to prevent access to your scripts such as .cgi, .php, .js files. Just add more extensions to gif|jpe?g|png, like this
gif|jpe?g|png|cgi|php3?|js

Granting access from other domains

Our current configuration will only allow access from ourdomain.com. But what if you want to allow access to your images from some other domains also? Say we want anotherdomain.com to link to our images, we just add it to our configuration, like this:

SetEnvIfNoCase Referer "^http://([a-z0-9_]+\.)?ourdomain.com/|^http://([a-z0-9_]+\.)?AnotherDomain.com/" Is_Local=1

<FilesMatch "\.(gif|jpe?g|png)$">

Order Allow,Deny

Allow from env=Is_Local

</FilesMatch>

You can make it more compact:

SetEnvIfNoCase Referer "^http://([a-z0-9_]+\.)?(ourdomain.com|AnotherDomain.com|YetAnotherDomain.com)/" Is_Local=1

<FilesMatch "\.(gif|jpe?g|png)$">

Order Allow,Deny

Allow from env=Is_Local

</FilesMatch>


For completeness, we have to take into account the fact that some browsers do not send referer info. Furthermore, there are visitors who would get to your website by typing the url directly into the address field. In both these cases, the referer field would be blank, and your webserver would deny access. You don't want that. To grant access to these categories of visitors, use:

SetEnvIf Referer "^$" Is_Local=1

If we merge this last bit with our code above, we get:

SetEnvIfNoCase Referer "(^http://([a-z0-9_]+\.)?(ourdomain.com|AnotherDomain.com|YetAnotherDomain.com)/)|^$" Is_Local=1

<FilesMatch "\.(gif|jpe?g|png)$">

Order Allow,Deny

Allow from env=Is_Local

</FilesMatch>


Share thoughts about this tutorial so I can improve on it.

Home|Comments, suggestions?


Login
Photo Personals · URL Submitter · User Registration · Image Editor · Search Engine · Voting Booth · Free-For-All Links · File Uploader · META Tag Generator · Lookup · Web Spider · WebPage Generator · Password Script · User-comment Script · Login Script · Edit Profile · Home
Copyright © 1998-2022 Richie's Pages All rights reserved.
Last modified: May 28 2011 07:56:37.