Serving Images Outside Document Root Via PHP

0
323
PHP on Apache Docroot

Looking to serve up images outside your document root? This article will discuss how to do this with Apache and PHP.

Storing your images away from your server’s document root is a good design decision if you don’t want anyone knowing where the true file path is. Because your HTML markup is exposed, anyone knows where your images are stored.

By default, most people store their images relative to the document root. This poses potential abuses such as someone hot linking to the image fully knowing the location. For people who run image intensive sites like photo galleries and photography websites, you may want to keep this path out of sight by using what I will call a image proxy locator using your favorite programming language.

Here we slide in a PHP file to be executed in the src attribute of an HTML image tag. Along with it a query string that specifies the filename of the image. Hidden is the true location of the image file.

imagelocator.php

  1. <?php
  2. class ImageLocator {
  3.   static $allowedMimeTypes = array("jpg" => "image/jpeg",
  4.                                    "png" => "image/png",
  5.                                    "gif" => "image/gif");
  6.  
  7.   static function ValidateImagePath($basepath, $filename) {
  8.     // Make sure basepath and filename is set
  9.     if (!isset($basepath) || !isset($filename))
  10.       ImageLocator::SendErrorHeader(404);
  11.  
  12.     // Make sure filename has minimum length (x.ext)
  13.     if (strlen($filename) <= 4)
  14.       ImageLocator::SendErrorHeader(404);
  15.  
  16.     // Make sure its a legal image extension
  17.     $pathParts = pathinfo($filename);
  18.     if (!array_key_exists($pathParts["extension"], ImageLocator::$allowedMimeTypes))
  19.       ImageLocator::SendErrorHeader(404);
  20.  
  21.     // Fix up directory separators
  22.     $bLastChar = substr($basepath, -1, 1) == DIRECTORY_SEPARATOR ? true : false;
  23.     $bFirstChar = substr($filename, 0, 1) == DIRECTORY_SEPARATOR ? true : false;
  24.     if ($bLastChar && $bFirstChar)
  25.       $imagepath = substr($basepath, 0, strlen($basepath)-1) . $filename;
  26.     elseif (!$bLastChar && !$bFirstChar)
  27.       $imagepath = $basepath . DIRECTORY_SEPARATOR . $filename;
  28.     else
  29.       $imagepath = $basepath . $filename;
  30.  
  31.     // Make sure the image file exists
  32.     if (!file_exists($imagepath))
  33.       ImageLocator::SendErrorHeader(404);
  34.  
  35.     return($imagepath);
  36.   }
  37.  
  38.   static function SendErrorHeader($error) {
  39.     switch($error) {
  40.       case 400:
  41.         header("HTTP/1.0 400 Bad Request");
  42.         break;
  43.       case 401:
  44.         header("HTTP/1.0 401 Unauthorized");
  45.         break;
  46.       case 403:
  47.         header("HTTP/1.0 403 Forbidden");
  48.         break;
  49.       case 404:
  50.       default:
  51.         header("HTTP/1.0 404 Not Found");
  52.         break;
  53.     }
  54.     // no more processing needed
  55.     exit;
  56.   }
  57.  
  58.   static function SendImageHeader($imagePath) {
  59.     $pathParts = pathinfo($imagePath);
  60.     $extension = $pathParts["extension"];
  61.     $mimeType = ImageLocator::$allowedMimeTypes[$extension];
  62.     header("Content-type: image/" . $mimeType);
  63.     header("Content-length: " . filesize($imagePath));
  64.     readfile($imagePath);
  65.   }
  66. }
  67.  
  68. // In your HTML:
  69. //  <img src="imagelocator.php?img=filename.jpg"/>
  70. //
  71. // Make sure you use DIRECTORY_SEPARATOR to form $path
  72. // Always end the path with a trailing DIRECTORY_SEPARATOR
  73. //
  74. // Examples:
  75. //   E:\images\ on Microsoft Windows
  76. //     $path = "E:" . DIRECTORY_SEPARATOR . "images" . DIRECTORY_SEPARATOR;
  77.  
  78. $path = "E:" . DIRECTORY_SEPARATOR . "images" . DIRECTORY_SEPARATOR;
  79. $imagePath = ImageLocator::ValidateImagePath($path,$_GET["img"]);
  80. ImageLocator::SendImageHeader($imagePath);
  81. ?>

Take the PHP code above and save it to your document root on your Apache server. When you reference the image with the img tag, do so like this:

<img src="imagelocator.php?img="filename.jpg" alt="ALT Image title"/>

In the example, “E:/images” is being used. Make sure you use DIRECTORY_SEPARATOR to separate the folders. This is for portability reasons. In the Microsoft Windows world, if this script is to be executed, it will resolve to the forward slash. If this script is running on a Linux server, it will resolve to a backslash.

Next place filename.jpg file in the images directory. You can make this as flexible as you want and create your own folder hierarchy. You will have to specify this hieararchy in your HTML img src.

Finally, browse to the HTML page and you should see your image appear.

Of Interest