Camen Design

c share + remix mime-types

Under the Hood #4:
Getting a File’s Mime-Type From Apache

There is no sane way to get a file’s mime-type in PHP.
The mime_content_type command is depreciated and not installed by default in PHP5.
The FileInfo PECL extension is not installed by default and can be insanely difficult to install.

Thirdly, you can use a Unix call:

$mime_type = exec ("file -i -b '$file_path'");

But that only works on Linux / BSD / Mac systems and not for Windows users, and it also requires that if you’re on a shared webhost that they allow you to use the exec command. This method also doesn’t always give accurate results.

Lastly of course, you could write a simple function to look up a file-extension and return the appropriate mime-type:

function mimeType ($extension) {
	switch ($extension) {
		case 'gif':	return 'image/gif';			break;
		case 'jpg':	return 'image/jpeg';			break;
		case 'png':	return 'image/png';			break;
		case 'pdf':	return 'application/pdf';		break;
		case 'zip':	return 'application/zip';		break;
		case 'exe':	return 'application/octet-stream';	break;

But this is hardly automatic, requires constant maintenance when you comes across new types that enter your system and looks plain ugly in your code. It lacks elegance.

I found a cute solution to this problem through looking at the headers returned by Apache. For every file you access, Apache sets various HTTP Headers, here’s an example from a photo.

Date: Sat, 12 Jul 2008 11:24:33 GMT
Server:	Apache/2
Last-Modified: Tue, 17 Jun 2008 13:34:34 GMT
Etag: "2678b24-5edfe-cdfaae80"
Accept-Ranges: bytes
Content-Length: 388606
Content-Type: image/jpeg

And lo, Apache is returning the mime-type automatically for any file-type. Since PHP has the ability to access remote URLs with many of its functions, we could theoretically ask PHP to ping the local file we want to know the mime-type of and retrieve the relevant information from the HTTP headers.

This is indeed possible, the get_headers function will give the HTTP Headers returned from a URL request as an array (if the second parameter is “1”). This function does not work without a full URL (“http://”), and therefore in order to get the mime-type of a local file, it needs to be in a publicly available location (even if only temporarily). You just prepend your domain name to the file path.

Here’s the final example:

$domain = '';
$file_path = 'data/content-media/photo/DSC00013.jpg';

$url_headers = get_headers ("$domain/$file_path", 1);
$mime_type = reset (explode (';', $url_headers['Content-Type']));

echo ($mime_type);

Which correctly outputs:


Apache sometimes returns a Content-Type value with the character set appended, e.g. “text/html;charset=UTF-8”, so “explode (';', …)” is used to break this apart, and reset returns the first array element.


There are a lot of issues with this practice, which is why it is not used on this site:

Update: Using the ‘Mime.types’ File

Hiếu Hoàng writes:

Debian’s default lighttpd.conf executes a “/usr/share/lighttpd/” file to get mime-type assignments. The “/etc/mime.types” which it uses is from the package mime-support.

This would alleviate writing the look-up function.

Hiếu Hoàng

Sample output:

$ /usr/share/lighttpd/
mimetype.assign = (
	".ez" => "application/andrew-inset",
	".anx" => "application/annodex",
	".atom" => "application/atom+xml",
	".atomcat" => "application/atomcat+xml",
	".atomsrv" => "application/atomserv+xml",
	".avi" => "video/x-msvideo",
	".movie" => "video/x-sgi-movie",
	".mpv" => "video/x-matroska",
	".ice" => "x-conference/x-cooltalk",
	".sisx" => "x-epoc/x-sisx-app",
	".vrm" => "x-world/x-vrml",

This file isn’t included in the Mac OS X PHP distribution, but I’ve copied it below with some small modifications (Output a PHP array, and Mac OS X’s ‘mime.types’ file is in ‘/etc/apache2/’ instead of ‘/etc/’).

#!/usr/bin/perl -w
# (I don’t know Perl, and how to colour this right)
use strict;
open MIMETYPES, "/etc/apache2/mime.types" or exit;
print "\$mime_types = array (\n";
my %extensions;
while(<MIMETYPES>) {
	next if /^\w*$/;
	if(/^([a-z0-9\/+-.]+)\s+((?:[a-z0-9.+-]+[ ]?)+)$/) {
		foreach(split / /, $2) {
			# mime.types can have same extension for different
			# mime types
			next if $extensions{$_};
			$extensions{$_} = 1;
			print "\".$_\" => \"$1\",\n";
print ");\n";

This could then be used to quickly put together a comprehensive (though massive) look-up function. I’ve saved a copy of the output of this script, enclosed at at the bottom of this article.

The ‘mime.types’ file is interesting as there may be a way to write a real small function (probably regex) to pull out a mime-type on request, which would be a far more elegant (and practical) than my own solution. I’ll have to give that some thought.