Preventing hotlinking of images

Using an .htaccess file to prevent people stealing bandwidth by linking directly to your images from another website is a popular and well-known technique. I needed to do this, but decided to take it a step further with a little added PHP.

Most solutions simply display another image (as polite or as rude as you like) in place of the hotlinked image. But I was curious to know who was linking to which images. Some of them might be legitimate users, in which case I could unblock them. Others might need to get a stiff warning about copyright.

For the .htaccess file, I used the technique here. But the author was actually redirecting to another page with the requested image embedded in it. Hey, I’m trying to save bandwidth here! So I wrote a PHP script that would log the access and then display a small generic image advertising the website from which the image had been, er, “borrowed”. Here’s the .htaccess file:

RewriteEngine On
# hotlinked images
RewriteCond %{REQUEST_FILENAME} \.(gif|jpe?g|png)$ [NC]
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !mydomain\.com [NC]
RewriteCond %{HTTP_REFERER} !myotherdomain\.com [NC]
RewriteCond %{HTTP_REFERER} !alegitimatedomain\.com [NC]
RewriteRule (.*) /blockimage.php?imagename=$1 [NC,L]
# end hotlinked images

This file, placed in the images directory, blocks all but the authorised domains and any blank referrers from accessing images. Notice that the image requested is passed to my blockimage.php script in the query string (that’s what the $1 does), because I’m going to use it later.

I could have logged accesses to a text file, but I have a MySQL database so I might as well use it; it will let me do further statistical analysis of the accesses too. I created the following table:

hotlinkid int(10) unsigned NOT NULL auto_increment,
referrer varchar(255) NOT NULL default '',
imagename varchar(255) NOT NULL default '',
linktime timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (hotlinkid)

I used INNODB for the table type because I reckoned it would be quicker for frequent writes and infrequent reads, but you can use MyISAM if INNODB isn’t available on your server.

Now the PHP code:

// called when an image is hotlinked
// ... snipped code to connect to database ...
// get the referrer and image name
// first initialise an empty array to hold them
$values = array();
$values['referrer'] = mysql_real_escape_string($_SERVER['HTTP_REFERER']);
// get the image name from the query string thanks to .htaccess
$values['imagename'] = mysql_real_escape_string($_GET['imagename']);
// write it to Hotlinks table using my own insertrow function
$result = insertrow($DB, 'Hotlinks', $values);
// show a nice image advertising my site
$mimetype = 'image/jpeg';
$filename= $_SERVER['DOCUMENT_ROOT'].'/images/sitelogo.jpg';
header('content-type: '.$mimetype);
header('content-length: '.filesize($filename));

So after silently logging the access, the PHP code sets the mime type to what the calling page is expecting (an image) and then returns the specified image to the browser. Remember you can’t have any other output (including white space) before the calls to the header function.

Now I can check the database table to see who is accessing what, and if I see any legitimate domains there, I can add them to the “allowed” list in .htaccess.

Ironically I ended up not using this code on the site it was intended for, for various reasons. But I think it’s an interesting and potentially useful technique, so I’m posting it here anyway.

This entry was posted in PHP, Web development and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>