Apache 2: Prevent image hotlinking serverwide
I'm trying to block access to images on my server if they are linked from some common "baddies", i.e. sites with users that tend to use other peoples bandwidth:
- livejounal.com
- blogger.com
- myspace.com
- ...
There are two things that differentiate my question from others like this question and that question:
- I want to do it "serverwide", i.e. configured in apache.conf for all hosts/domains, not in .htaccess or individual virtual hosts
- I want to redirect the request to an image of my choice, not just send a 403 FORBIDDEN
I tried to include RewriteRules in apache.conf, but it does not do anything at all. And there doesn't seem to be an option that allows me to redirect depending on SetEnv.
Any suggestions?
I have a solution, even if it is not very beautiful, because it leaves the default error 403 document changed even if the image is not hotlinked:
This goes into apache2.conf:
SetEnvIfNoCase Referer "livejournal.com" hotlink
<FilesMatch ".(gif|jpg|jpeg|png)$">
ErrorDocument 403 /server404/no_hotlink.php
</FilesMatch>
<FilesMatch ".(gif|jpg|jpeg|png|mpg|avi|flv)$">
deny from env=hotlink
</FilesMatch>
Alias /server404 "/var/www/_404"
<Directory "/var/www/_404">
AllowOverride AuthConfig
Order allow,deny
Allow from all
</Directory>
We used the /server404 path for a serverwide 404 error document for some time, so we already had that. You could also call it 403 or GlobalNoHotlink or whatever you want.
For images (first FilesMatch) we set the "forbidden" ErrorDocument to a PHP file in our /server404 directory. Then we check if it is hotlinked (there we also do that for video files, thus two FilesMatch tags) and forbid access if hotlinked.
This goes into /var/www/_404/no_hotlink.php - the PHP file called on forbidden image:
<?PHP
$file = dirname(__FILE__) . '/_default/no_hotlink.png';
header('HTTP/1.1 200 OK', true, 200);
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header('Content-length: ' . filesize($file));
header('Content-type: image/png');
readfile($file);
?>
- The first line is used to identify the image file we want to send out.
- Then we set the HTTP header to 200 (this is optional. Decide for yourself),
- add some headers to avoid caching of the image (we don't want to let that 8000x8000 pink pixel file appear "from cache" on our site if the person decides to go to our site, do we?).
- Content-length according to the dummy file's size and
- MIME type (You might have to change the latter of not using PNG).
- Then we read the file.
It works - but it is not as beautiful as I'd like to have it, so will still be happy about other solutions.