How to restrict image access to logged in users with PHP and .htaccess

Since I run ran a Utah wedding photography site, and each user gets their own online wedding gallery, I needed a way to restrict access to the user’s photos to only the logged in user. I am running Apache2 and using php, so the solution involved using .htaccess to redirect all JPG requests to a PHP file which checks if the user is authorized or not. If they are not, it prints out a “please log in” JPG image, instead of the requested image.

This page is OLD and may  contains errors, out of date information, security flaws or other problems.

Where to go from here

You could use PHP’s getimagesize() to determine wether to send a full sized image or a thumbnail of the error message.

.htaccess to redirect requests

Download htaccess I’m pretty sure that both jpg and JPG aren’t needed. I put them in initially while trying to correct a different problem.

Options -Indexes   # Don't allow index view of these folders
RewriteEngine on   # Enable rewrite

# If the requested filename ends in jpg or jpeg...
RewriteCond %{REQUEST_FILENAME} .*jpeg$|.*jpg$|.*JPEG$|.*JPG$ [NC]  # The JPG and jpg versions probably aren't needed

# Then grab the requested URL from the current directory on and stuff in in the $1 variable (first () pair maps to $1)
# Append the $1 variable as part of the query string when processing auth.php
RewriteRule (.*)$1 [NC,L]

PHP to restrict access

Download auth.php

if(!strip_tags($_GET['img']) || !isset($_GET['img']) || $_GET['img'] == "" ){
	// If they tried to access this file directly, send them elsewhere

// We keep user authentication in a session, so we'll need access to that

$reqpath = strip_tags($_GET['img']); // Use strip_tags to be safer

// User albums are structured like so
// $reqpath will now have the <username>/<album>/... part
// <username> matches the username stored in the session variable
// so we get a substring from the start of $reqpath to the first /

$foundslash = strpos($reqpath,'/');  // Get the position of the first slash

if($foundslash === FALSE){  // $foundslash could return 0 or other "false" variables, use ===

// Save their username off...
$username = substr($reqpath,0,$foundslash);

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-store, no-cache,must-revalidate");
header("Cache-Control: post-check=0, pre-check=0",false);
header("Pragma: no-cache");
header("Content-type: image/jpeg");

// Assume they aren't authorized unless everything lines up
$authed = FALSE;
if($_SESSION['validated'] && $_SESSION['uid'] == $username){
    $authed = TRUE; // By requireding validated and uid to be correct
		    // We prevent loggedin users from seeing other people's
		    // pictures

    // If they're authorized, read userphotos/$PATH
    // The .htaccess file is in userphotos (since that's the top level we care to protect)
    // so the path is relative to that folder. auth.php is up one level to keep photos
    // and code as separate as possible
    @readfile("userphotos/".strip_tags($_GET['img'])); // Read and send image file

This entry was posted in Programming, Projects and tagged , , , , , . Bookmark the permalink.

6 Responses to How to restrict image access to logged in users with PHP and .htaccess

  1. JasonSaeho says:

    Thank you for this post, it helped me with an application I was making.

  2. Francisco says:

    Thank you so much! You saved me with this wonderful idea of using mod_rewrite for access control.

    I just wish this method was more often discussed in on forums (took me hours to get here). I will sure point some people to this page.

  3. Ruatas says:

    How about PDF files? or any other kind of documents?

    • stuporglue says:

      To send any type of file as a download you just need to set the mime type. That’s the “Content-type” header.

      header(“Content-type: image/jpeg”);

      You can detect the mime type dynamically with either (PHP < 5.3) or (PHP >= 5.3)

      If you’re doing just pdf’s, the mime type is “application/pdf”.

      If you want a download progress bar to appear you can also send the Content-Length header, otherwise browsers will show “Downloading .2MB of ???” etc.

      $fsize = filesize($filepath);
      header(“Content-Length: “.$fsize);

Leave a Reply

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