Protecting your website's content from deep linking, when using the Drupal CMS

Let's assume you operate a blog on your Drupal website. Let's further assume, you regularly write blog posts, containing things like zip archives or graphics. In that case, you have two problems. The first being, that Drupal throws all file attachments into the same directory, which occasionally leads to undesired automatic renames. The second and more annoying one is, that sooner or later someone will directly link to your files from a social media website. Such deep links will cost you bandwidth, without giving you the chance to convert those visitors into readers.

Naturally, you'd want to protect your assets from deep linking, by smartly redirecting visitors to the pages, the requested files belong to. The good news is, that the apache web server provides a powerful redirection engine, which can evaluate the HTTP referrer header and send people to any desired page, if they try to access a file, which is linked to from outside your domain. The bad news however is, that since Drupal keeps all file uploads in the same directory, there is no way of telling from the URL, which file request should be redirected to which page. You'd basically have to create an individual rewrite rule for every single file, which is neither scalable nor manageable.

The solution to this problem is to teach Drupal to store files in a per article directory, which contains the node id of the article in it's name. This requires the clean URL feature to be enabled, as well as a few extra modules to be installed:

  • http://drupal.org/project/cck
  • http://drupal.org/project/token
  • http://drupal.org/project/filefield
  • http://drupal.org/project/filefield_paths

Once you have installed and enabled these modules, edit the Blog content type. Click the "Manage fields" tab and add a new field of the type "File". The important thing in setting up the new field is to put "cck_upload/node/[nid]" as the value for the file path. You probably also want to enable an unlimited amount of uploads, remove the file type restrictions and hide the new field from public viewing via the "Display settings" tab, so it only shows up, when editing the page.

For the apache part of the job, add the following rewrite rule (obviously with onyxbits.de replaced by your domain) to your web server config or .htaccess file:

RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www.)?onyxbits.de(/)?.$ [NC]
RewriteRule ^(.
)sites/default/files/cck_upload/(.)/(.)$ $2 [R,NC]

Whenever a file is now uploaded via the new CCK field, Drupal will automatically create a unique subdirectory for the blog post under the "cck_upload" directory and store that file within it. Since the URL to the file contains the node id of the blog post, it belongs to, Apache can easily redirect a request for a deep linked file to it's proper page.

There is a minor drawback with this method, when it comes to aliasing pages (e.g. by using the path auto module). Visitors cannot easily be redirected to aliased pages. You can use the [path] token instead of [nid], but unfortunately, it's value only becomes available after the node has been saved for the first time.

Please note, that this method does not provide an airtight protection, as it relies on the browser submitting a correct http referrer header, which can be suppressed or modified by privacy extensions or proxies. Also note, that the rewrite rule given above is suitable for content like zip archives, which is referenced through the <a> tag. Images, which are hotlinked through the <img> tag will simply break on the hotlinking site, as this tag does not force the browser to through a redirect.