Here’s a cool little hack I wrote: Indices. It takes the “vintage” looking Apache directory listings and gives them a significant facelift. I’ve wanted this for years and finally got around to doing it.
There’s a README in the download, but I thought I’d write a bit about the details of my approach.
Why?
I’ve been using a hacked-up version of Ash Young’s directory listing PHP. It worked pretty well, but I wasn’t able to get it running as the default index, so I had to softlink an index.php to every directory I cared about prettying up. And directory navigation is done with URL parameters (i.e. http://site/url?dir=somedir), which offends my delicate sense of aesthetics.
The softlink part was the real problem though; I had people asking how they could get their directory indexes to look nice, and “softlink this file to each directory” wasn’t going to work for them. So I set about finding a way to make directory listings pretty via Apache.
Getting started: adding CSS
Apache doesn’t give you a lot to work with, but it’s enough. There are a couple of mod_autoindex directives and options that got me started:
HeaderName and ReadmeName These directives allowed me to replace the HTML that appears before and after the file list. By adding IndexOptions SuppressHTMLPreamble, I was able to replace the entire HTML header — so I could use my own title and, more importantly, link to a CSS file. That was my prybar to styling the content.
IndexOptions HTMLTable This experimental option turns the file list itself into an HTML table. There’s (sadly) no semantic markup in the table, but the table itself is sort of semantic: the first column is the icon, the second is the filename, etc. Based on that knowledge, you can use gnarly CSS2 selectors and get a lot of styling.
For example, this snippet styles all of the td elements that are the third one after the first td. In the table Apache generates, that’s the “Size” column. Nice! (in its own way.) See the “table styling” section of style-nojs.css for more.
/* all other SIZE cells */
td:first-child + td + td + td {
padding-right: 5px;
}
Making the header dynamic
One problem with replacing the entire header: the title could no longer contain any information about the directory being browsed (like its name). After some digging I found that you can make the HeaderName file a PHP file with some .htaccess trickery:
# For the PHP file to execute in a header, need to have a major type of text AddType text/html .php AddHandler application/x-httpd-php .php
Another prybar: Now I could put server-side smarts in my listing.
I used this for my title text: urldecode($_SERVER['REQUEST_URI']). Then I added some PHP to look for a readme.html file and insert its contents before the table.
Of course, CSS2 doesn’t work in IE
Yeah, most of those neat CSS selectors don’t do anything in IE. I was still getting a lot of styling, but not everything: the “parent directory” link wasn’t getting hidden; table cell contents weren’t getting styled; etc.
Making the browser part dynamic
Finally it occurred to me: hey, javascript. I could hop around the DOM and insert semantic markup, which would allow for much simpler, universal CSS. The tradeoff is that you’re doing goofy DHTML things that might break in some browsers, and that won’t work at all for angry luddites who have javascript disabled. But everything has to work in IE, right? So I decided to give it a go.
The javascript isn’t too bad, really. It feels its way around the DOM — partially based on table position as semantic information — and adds class attributes, shuffles around some cell contents, and changes a few display things. I should document the resulting CSS so people can go nuts with styling, but for now, read the javascript.
I could have gotten a lot more carried away with the javascript. For instance, one way to attempt Apache 1.x compatibility would be to not use HTMLTable, and instead convert the non-table DOM into a table with javascript. Fortunately, I was only doing this for my Apache installation so I didn’t feel the need to try things like that.
A bit of productization
Finally, I spent some time making my code presentable and configurable. There are PHP variables to control whether you use CSS-only styling (which means you’re leaning on the CSS2 stuff) or the Javascript-and-CSS1 approach; what your title should say; what logo to use; etc. I used a few more Apache IndexOptions to clean up the output: FoldersFirst, IgnoreCase. I also wired up nice icons for most common file types (I grabbed a handful of these from somebody else’s free directory index package; can’t remember which).
The result
Here’s a sample index. It’s much nicer than the stock Apache thing. And the cool part is, you change your Apache config in one place (either a top-level .htaccess file, or in your Apache conf file), and every directory listing on your site dresses up.That’s about it. Hope this is useful to somebody.
36 Comments so far
Leave a reply


I never noticed that the YJM pedal was a DOD and not an MXR. ha!
I’m running PHP 5.1 and MYSQL 5 on my remote server, but it’s kicking back an internal server error 500. It seems like an issue with the .htaccess file.
I’m also running your indices on my local computer running the same configuration, and it works fine.
I checked permissions to be sure, but I can’t figure it out. Any ideas???
In addition to be previous comment, I should also mention my (troubled) remote host is using Apache 1.3.39, unlike my local computer which is using Apache 2.0.59. This might be the problem.
@Scott – that’s definitely it. Indices uses a few Apache 2-only things that won’t work at all with 1.x. For instance, the HTMLTable and FoldersFirst options to IndexOptions are 2.0.23 and later.
There may be other errors… worth checking your apache logs to chase them down.
Your invention is really good! I found something similar before but your script is smaller and the design is great. I wish I could use it on my website. I have PHP Version: 4.4.7 Apache/1.3.39
and I’m getting 500 internal server error.
If I remove this code from the .htaccess file, it almost works:
IndexOptions FancyIndexing
IndexOptions FoldersFirst IgnoreCase XHTML NameWidth=*
IndexOptions SuppressHTMLPreamble SuppressRules HTMLTable
IndexOptions IconHeight=16 IconWidth=16
IndexOptions SuppressDescription
Take a looke here: http://www.chimarrao.com/img/
@Ricardo – yeah, Apache 1.x is going to require some more detailed hacking, and I don’t have time (or reason) to do it. Maybe you can get it working…
Works great! Thank you very much for such a simple solution – I thought I was going to have to strip down one of those fancy file management scripts.
>One problem with replacing the entire header:
>the title could no longer contain any information about
>the directory being browsed (like its name).
Nope. You can use SSI command in your html files (maybe require AddHandler server-parsed .html. I just use .shtml). Just like that:
#echo var=”REQUEST_URI” (with appropriate SSI token)
So, you don`t need PHP to do that.
It’s seems great — just having trouble getting it to work ok:
I’m Apache2.0.54/PHP 5.2.5; the header loads correctly, but what comes out after that is garbage — as if it there was an encoding problem.
If I remove the header the directory listing appears, of course without styling.
Any clue?
Thanks
Thanks for this, it’s amazing. I’ve wanted to style apache directories pages since a long time…
Here’s my take on the css: http://picboost.com/upload/folder.png
Only problem is: as you can see the default .txt icon isn’t been replaced, only resized… path issues?
thanks again anyway ; )
@Jean-Michel: I’m not sure what could be causing that… if you set it up on a page and send me a link, maybe I can help?
@Daniel: what exactly are you trying to do? Replace the text icon? It should work fine if you replace the png, but you want to be sure that the image is 16×16.
This looks like an awesome bit of work, but I’m having a heck of a time getting it to work on my server (Apache 2+ and PHP5+). Here’s a link of what’s getting spit back out: http://www.epicerastudio.com/aero/images/
I’ve gone over it numerous times to check for what might be missing, but it looks like about halfway down the header.php code, it just decides to parse back as comments instead of a function. Any ideas?
Either way, great work… i’m seriously surprised that I can’t find anyone else who’s touching this issue (on Google anyways). Cheers!
@brandon:
It looks like your header isn’t getting parsed as PHP. There’s a .htaccess file that has to go in the same directory as the header.php file. Is that there? It’s possible that you didn’t extract or copy that file for some reason…
@gse – thanks for the quick reply! I double checked to make sure all the files were intact (including the second .htaccess), re-unzipped and resubmitted, etc – still no action. You’re right though, the icons are being grabbed, and if I remove header.php, the rest of the stuff works fine – just no styling.
Any other ideas off the top of your head that might result in the php not being read? What’s odd s that it chooses to not read header.php about halfway down the page (line 37), and it’s not a syntax problem or anything… it just decides to quit at that point.
Either way thanks for checking it out – I’ll keep poking at it myself and I’ll post back if I figure it out :)
Scott,
- Seems to be what I’m looking for !
- Had one problem installing on RHEL3, httpd 2.0.46-61.ent with
“.htaccess: Invalid directory indexing option” in error_log
- in .htaccess changed :
IndexOptions FoldersFirst IgnoreCase XHTML NameWidth=*
to :
IndexOptions FoldersFirst IgnoreCase NameWidth=*
This now appears to work. Checked docs and this option is only
available 2.0.49+
If you are having major problems trying to get the PHP in header.php to parse correctly and have tried “everything”..
Open up your CSS / JS file(s) (if applicable to your config setting) and copy them into the header.php file.
If you know your way around JS, you can use it to set the title of the document as well as move some of your config parameters over to JS.
I was able to completely remove the PHP from my header.php file, and it returns the same result.
NICE WORK!!! Thanks a lot. Saved me some time to do it by myself :-)
Hi,
no luck with Centos 5.1 using SSL protected directories. I get a Error 500 when trying this. Is SSL the problem?
Changed addicon directives to /var/www/html/indices/images
Is there something else I should do?
Got it to work! Had to change httpd.conf to allow indices to work. Adding this solved the problem:
# AllowOverride Options IndexOptions FileInfo AuthConfig Limit
# Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
AllowOverride All
Options All
I couldnt get this to work at all (PHP5 and Apache2), No change at all. I extracted and put the indices directory in /var/www/ renamed main-htaccess to .htaccess.
What else do I have to do?
Great Job!
This works pretty well.
I did however make a few modifications.
One was to check if a readme.php exists if there’s no readme.html and out put that if show_readme is set.
Also I put the indices folder in a central location so I only need one copy in /etc/apache2/htdocs/addons/indices and in my virtual host directive, I have an alias setup: Alias /indices /etc/apache2/htdocs/addons/indices
Each virtual host only needs the directory alias added and the .htaccess file in the parent directory of the document root.
I had to also comment out:
.row_parentdir {
/* display: none;*/
}
It was causing a bug on my system. My setup doesn’t show the parent directory of the document root already.
Seems the demo is now 404? Would be interesting to check out this work..
@Hallvord: Ah, the link didn’t survive my site re-org. Thanks! I updated it, but here’s a working demo: http://antisleep.com/indices-demo
So far, this seems to be the cleanest and simplest yet most flexible way to stylize file listings with Apache and PHP.
The only thing that’s not yet working for me is header.php, which doesn’t seem to be firing. I suspect it’s something to do with the fact that I’m hosting it on Windows because everything works when I upload it to my provider, who uses Linux.
On Windows, it finds and renders the footer.html as well as the icons and IndexOptions.
This is the .htaccess file in the directory that is to be browsed:
# In order for the PHP file to execute in a header, need to have a major type of text
AddType text/html .php
AddHandler application/x-httpd-php .php
Options -Indexes
Did I miss a Windows-specific instruction somewhere?
Thanks,
Craig
@Craig – that .htaccess file should be in the same directory as header.php and footer.html, not in the directory that is to be browsed. In the zipfile that directory is called “indices”.
Oops, yes, what I said was not actually true, which is why everything works when I upload it to my hosting provider: the above listed .htaccess file *is* in the folder that contains header.php and footer.html.
Any other ideas? Thanks!
Sorry, no other ideas other than the usual — be sure that you’re running a recent enough version of Apache, check your logs for telltale warnings and errors, etc.
A little tip to those who can’t get the PHP code to execute, especially on web hosts: Don’t include the AddType and AddHandler lines. I blindly assumed they needed to be there and tried everything else. Also Scott, your post in the Ubuntu forum, where you first mentioned those lines, has another strange thing in it… “Options -Indexes”. Should be “Options +Indexes” , right? :)
Just Indexes doesn’t do it for me; it need to be +Indexes.
Did I write -Indexes somewhere? Hm, that does seem wrong. Yes, plus sign is what I use…
Yep, you did, here. Of course, that was probably a typo but I found that thread when Googling for info on HeaderName/ReadmeName. But it’s still incorrect, and RHF ended up “solving” the problem by doing “Options Indexes” and “AllowOverride None” in the Apache config file.
So, anyway, I managed to do what I wanted, and I actually didn’t use Indices, but I rolled my own. Which was my plan from the beginning. I was just looking for info on how to get everything to work. See my tweet for the URL. (Don’t want to add another URL to the comment so it’s marked for moderation :p )
Ah, that’s a different htaccess file, the “secondary” one. And on my server I do run with Options -Indexes in that file. I don’t even remember why!
As an intellectual exercise, I’m trying to get this working with mod_python instead of php. I’m having trouble getting the “HeaderName header.py” to treat header.py as a cgi script. I’m a bit new to mod_python so I definitely have it set up wrong. Two Questions: 1. how should /indices/.htaccess be setup and 2. Do I need to call “req.write(‘header html here’)” or call “print ‘header html here’”
I came across quite a few solutions to the ugly Apache lists – this is the only one that I got working quickly and effectively. Also, from a tech solution perspective, it is the most elegant!
Many thanks to you sir.
Looks really nice! Trying to get it to work on OS X 10.6 with apache 2.2.17 and php 5.3.4… looks like the header php does not get executed as php though. Any idea where to start looking?
i have a simple .htaccess i been tested:
# For the PHP file to execute in a header, need to have a major type of text
AddType text/html .php
AddHandler application/x-httpd-php .php
IndexOptions FancyIndexing XHTML FoldersFirst
HeaderName /css/header.php
ReadmeName /css/footer.php
# custom error documents
ErrorDocument 401 /css/h.php
ErrorDocument 403 /css/403.php
ErrorDocument 404 /css/404.php
ErrorDocument 500 /css/500.php
# or quick custom error “document”..
ErrorDocument 404 “ERROR – BEIF@servererror – 404 There is nothing here.. go away quickly!