1

I have searched a lot this topic, and found this piece of code (or slight variations) in many places, included some answers in this website. This is the code:

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www\.)mydomain.com/.*$ [NC]
RewriteRule \.(gif|jpg)$ - [F]
</IfModule>

I'm not a webmaster but I can understand what the code does. In my code of course I have replaced mydomain.com with my real domain.

The problem is that when I put this code in my root folder, it takes down the whole website with a 500 error. If I put it in the subdirectory that I want to protect, all the files in the subdirectory become unavailable to anyone, and in the website I see a broken image placeholder. What's wrong with it?

The reason why I need this code is that only subscribers must be able to access the files. I don't want that they share the link and make them available to anyone. This is a testing code, the real files to protect have a different extension than gif or jpg but the principle is the same.

2

If your subscribers have to login to your website, then you could use a PHP script in your images folder to check that they are logged in before serving an image in the same way you would check this before serving them a subscriber-only webpage.

Your .htaccess file might have:

# Protect subscriber-only assets from hot-linking (*.gif)
RewriteCond %{REQUEST_URI} (.*)\.gif
RewriteRule ^(.*\.)(gif)$ protected-asset.php?f=$1$2 [L]

# Protect subscriber-only assets from hot-linking (*.jpg)
RewriteCond %{REQUEST_URI} (.*)\.jpg
RewriteRule ^(.*\.)(jpg)$ protected-asset.php?f=$1$2 [L]

And then your protected-asset.php file might have:

<?php
$sWebrootPath = '/var/www/www.mydomain.com/';
$sAsset = isset( $_GET['f'] ) ? $sWebrootPath . $_GET['f'] : '';
$aHeaders = array();

/* HTTP 404 if asset does not exist */
if( !file_exists( $sAsset )) {
  header( 'HTTP/1.0 404 Not Found' );
  exit;
}

/* Check if request comes from authenticated subscriber */
require_once( 'classes/session.php' );
if( Session::UserId() == 0 ) {
  header( 'HTTP/1.0 401 Unauthorized' );
  exit;
}

/* Declare MIME-Type */
$sFileExt = strtolower( substr( $sAsset, 1+stripos( $sAsset, '.' )));
switch( $sFileExt ) {
  case 'gif': $sMimeType = 'image/gif'; break;
  case 'jpg': $sMimeType = 'image/jpg'; break;
  default: $sMimeType = 'text/plain'; break;
}
$aHeaders[] = 'Content-Type: ' . $sMimeType . '; charset: UTF-8';

/* Cache for 1 hour (you could extend this as required) */
$iCacheSecs = 60 * 60;
$dtNow = time();
$dtExpires = strtotime( sprintf( '+%s seconds', $iCacheSecs ));
$aHeaders[] = 'Expires: ' . date( 'r', $dtExpires );
$aHeaders[] = 'Last-Modified: ' . date( 'r', $dtNow );
$aHeaders[] = 'Cache-Control: public, must-revalidate, ' .
  sprintf( 'max-age=%s', $iCacheSecs );

/* Generate unique ETag */
$sData = @file_get_contents( $sAsset );
$sETag = md5( $sData );
$aHeaders[] = sprintf( 'ETag: %s', $sETag );

/* HTTP 304 if asset has matching ETag to browser cached copy */
$sSuppliedETag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? 
  $_SERVER['HTTP_IF_NONE_MATCH'] : '';
if( $sSuppliedETag == $sETag ) {
  header( 'HTTP/1.1 304 Not Modified' );
  header( 'Content-Length: 0' );
  exit;
}

/* GZip Compression */
if( !ob_start( 'ob_gzhandler' )) ob_start();

/* Respond with Asset headers and data */
foreach( $aHeaders as $sHeader ) header( $sHeader );
echo( $sData );

The two snippets in the above code that won't just work straight away and will need replacing with your own code are the require_once( 'classes/session.php' ); and Session::UserId(). The premise here being that the require_once loads any pre-requisite files you might need to identify if someone is logged-in or not, and my UserId() function was returning 0 to indicate no authenticated user assigned to the current session. You'll probably need to use a cookie to manage your sessions etc but it sounds like you already have the subscriber login part your website already setup and working so just copy some code across to replace these two parts and it should work ok for you.

While in principle I'm all for best practice, I generally don't put <IfModule> statements in .htaccess files as they can typically cause more problems than they solve in my experience - it may be worth trying your existing code without these before trying my solution!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.