Watermarking hotlinked images

It's not the use of my bandwidth which annoys me so much as the lack of attribution.

I'm talking about hotlinked images, of course.

What's hotlinking?

"Hotlinking" is where you link to an image stored on someone else's site without making your own local copy. It's done a lot by people posting to forums, weblogs, or to social networking sites such as MySpace.

The usual solution to this problem is to redirect them to another (often tasteless) image indicating one's dissatisfaction with the hotlinked file. This is a pretty good disincentive from using other peoples' bandwidth; however, I care more about people maintaining attribution, and standard hotlink-prevention stuff simply just causes people to upload a copy of the image to another site and then keep on not attributing it; in this case it's even worse because if someone were to look at the image's URL, they don't see anything regarding where the image originally came from.

So my preferred solution is to make a simple proxy script which adds a watermark to an image, and also logs the hotlinked image.

Requirements

To use this, you'll need:
  • A webhost which allows you to run CGI scripts written in Bourne shell (most UNIX-based webhosting will allow this)
  • ImageMagick installed on the webhost
  • Access to mod_rewrite

The watermarking script

I have the following script sitting in my server root directory: (saved as a CGI file; let's call it 'add-watermark.cgi' though I prefer to keep the location private)

/add-watermark.cgi

#!/bin/sh

in="`echo $PATH_INFO | sed 's/\.\.//g;s,^/,,'`"
out=".wm/$in"

log=.wm/log-`date +%Y-%m`
printf "`date`: %s from %s\n" "$PATH_INFO" "$HTTP_REFERER" >> $log

COMPOSITE=/usr/bin/composite

if [ ! -f "$in" ] ; then
        cat << EOF
Content-type: text/html

$in does not exist, mofo
EOF
        exit 1
fi

if [ ! -f "$out" ] || [ "$in" -nt "$out" ] ; then
        mkdir -p `dirname "$out"`
        $COMPOSITE -gravity -compose atop northwest watermark.png "$in" "$out"
fi

cat << EOF
Content-type: text/html
Location: /$out

Wups, this image was hotlinked.  Taking you to a slightly uglier version.
EOF
This script basically sanitizes the URL a bit, composes the watermark image on top of the requested image, and caches the result for later, and redirects the browser to it (via a 302 redirect, so the original URL doesn't get the watermarked image in its cache entry). It also logs the watermarking.

Note that this script requires composite from ImageMagick. The location of the actual binary may have to be modified if it's kept in a nonstandard location (e.g. it may be in /usr/local/bin or /sw/bin instead).

Also, it doesn't currently handle URL-encoded characters, so spaces in the filename and so on will probably break this.

When you install it, remember to set the CGI executable. On UNIX/Linux/etc. hosts, you'd do this with chmod 755 add-watermark.cgi, and many FTP clients will also let you set the permissions when you upload it (make sure to set it read/write/execute for yourself but only read/execute for group and others).

The watermark image

The script above needs an image to work on. I keep this image in the same directory as the watermarking CGI: (the purple represents transparency)
It's simple and to the point.

Proxying the images

Next we use the magic of mod_rewrite. It helps to understand regular expressions; at the very least change the example\.com to your own domain, though:

.htaccess

RewriteEngine On

# No referrer is okay
RewriteCond %{HTTP_REFERER} !^$ [NC]
# Avoid an infinite loop
RewriteCond %{REQUEST_URI} !\.wm/.* [NC]
RewriteCond %{REQUEST_URI} !/add-watermark.cgi/.* [NC]
# Don't watermark it if it's being shown on this site
RewriteCond %{HTTP_REFERER} !^http://([^/]*\.)?example\.com($|/.*) [NC]
# Things in the /stuff directory are okay to be hotlinked
RewriteCond %{REQUEST_URI} !^/stuff/ [NC]

### Sites to not watermark
# Let's be friendly to search engine image caches
RewriteCond %{HTTP_REFERER} !^http://([^/]*\.)/search\?q=cache\:.*$ [NC]

# Weblog syndications
RewriteCond %{HTTP_REFERER} !^http://([^/]*\.)?bloglines.com($|/) [NC]
# (other whitelisted regular expressions go here - start them with ! to negate them)

# If something gets this far, it's hotlinked and not whitelisted; add the watermark
RewriteRule ^(.*)/([^/]*\.(gif|png|jpg)) /add-watermark.cgi/$1/$2 [R,L]
And there you have it; an image which looks like this on my site:
looks like this when it's hotlinked:
So people know where it's from while still seeing the original image. And, I get a log entry so if anyone is being excessively abusive it's easy enough to track them down and send them a polite email.