Fancy Directory Listing (Using htaccess)
In this tutorial I cover the basics of htaccess, how to create a custom Apache directory listing using .htaccess. If you look at the default Apache directory listing you’ll see a plain, boring, dull, hard to read design. Our objective for the course of this tutorial is to spice up that design, and learn some htaccess in the process!
For those that would just like to see the finished results, you can see the before and afters here.
http://samples.thewebsqueeze.com/directory-listing/
The Rules
Before we get started I want to cover the SUPER basics of htaccess. This will help those who aren’t familiar with how Apache and htaccess work understand what we are doing as we go along. (These are generic rules, and may differ from server to server, depending on the configuration. Ask your server administrator if you receive any errors.)
- All htaccess code should be placed in a text document named ‘.htaccess’.
- htaccess works on a recursive basis. So any command you type will work for the current directory, and all sub-directories under that, unless otherwise specified.
- You can have any number of htaccess files on your domain. The root of your domain can have a master htaccess file, and then you can override the master htaccess file within sub-directories off the root of the domain.
- In htaccess, any line prefixed with a “#” symbol is considered to be a comment. You should comment your code so you can refer to that comment 3-4 months down the road when you are trying to figure out why you coded it in that way.
- If ever in doubt you can read the Apache documentation. (http://httpd.apache.org/docs/2.2/) Or, you could ask the kind hearted folks at The Web Squeeze in our forum! (http://www.thewebsqueeze.com/forum/)
Let’s get started!
Here’s the default directory listing for Apache! Now, you might not see this by default, in fact you may see a Forbidden page, however, we’ll fix that with this next bit of code!

I always start my htaccess documents with what I call “General Rewrites”, those are the rewrites that I tend to duplicate throughout all my htaccess documents.
# START :: General Rules RewriteEngine On RewriteBase /
In htaccess any line prefixed with a “#” symbol is considered to be a comment. So, for organizational purposes I have commented the start and end of sections. We are then turning on the RewriteEngine and setting the base for the engine, if it’s not already set.
Options +Indexes
First, as you may already know htaccess has countless options to allow you to control how your domain works and loads without editing server configuration files. The “Options” directive controls which server features are available in a particular directory. You can view all the available options on Apache’s website.
http://httpd.apache.org/docs/2.0/mod/core.html#options
So, we’re saying find the “Indexes” option within the “Options” directive, and turn that on. By adding that “+” sign in-front of “Indexes” you are effectively turning on Apache’s directory index for not only that directory, but all sub-directories below that location.
This means that if a URL which points to a directory is requested, and there isn’t a DirectoryIndex (e.g. index.html) then mod_autoindex will return a formatted listing of the directory.
You could also turn off Apache directory listing by replacing the “+” with a “-”, effectively telling Apache, not to list the contents of that directory and all directories below that.
Options +FollowSymLinks # END :: General Rules
FollowSymLinks tells Apache to follow symbolic links. Wikipedia defines a symbolic link as…
A symbolic link (also symlink or soft link) is a special type of file that contains a reference to another file or directory in the form of an absolute or relative path and that affects pathname resolution.
# START :: Error Documents ErrorDocument 400 default ErrorDocument 401 default ErrorDocument 403 default ErrorDocument 404 default ErrorDocument 500 default # END :: Error Documents
Here, I’m setting all ErrorDocuments to the server default. You could replace ‘default’ with an absolute or relative path to the location of your error documents. You can read more about that here.
http://httpd.apache.org/docs/1.3/mod/core.html#errordocument
# START :: URL Rewrites
RewriteCond %{HTTP_HOST} !^clients.samples\.thewebsqueeze\.com$ [NC]
RewriteRule ^(.*)$ http://clients.samples.thewebsqueeze.com/$1 [R=301,L]
# END :: URL Rewrites
Here, I’m setting a rewrite condition. You can think of it like an “if” statement in PHP. If the URL doesn’t equal ‘clients.samples.thewebsqueeze.com’, with anything on the end, then do whatever the rewrite rule is. In this case, then, redirect them to ‘http://clients.samples.thewebsqueeze.com/’. You’ll want to either delete these lines, or change them to match the location of your fancy directory! You could also alter this to remove or add the ‘www’ for your domain. See below example.
# START :: URL Rewrites
RewriteCond %{HTTP_HOST} !^www\.thewebsqueeze\.com$ [NC]
RewriteRule ^(.*)$ http://www.thewebsqueeze.com/$1 [R=301,L]
# END :: URL Rewrites
Now, this last bit of code is a method of securing our directory’s htaccess file against attacks. Protecting your site’s htaccess file is critical to maintaining a secure environment.
# START :: Strong .htaccess Protection <Files ~ "^.*\.([Hh][Tt][Aa])"> order allow,deny deny from all </Files> # END :: Strong .htaccess Protection
You can read more about the above method at Perishable Press.
Now, let’s recap all the code thus far!
# START :: General Rules
RewriteEngine On
RewriteBase /
Options +Indexes
Options +FollowSymLinks
# END :: General Rules
# START :: Error Documents
ErrorDocument 400 default
ErrorDocument 401 default
ErrorDocument 403 default
ErrorDocument 404 default
ErrorDocument 500 default
# END :: Error Documents
# START :: URL Rewrites
RewriteCond %{HTTP_HOST} !^clients.samples\.thewebsqueeze\.com$ [NC]
RewriteRule ^(.*)$ http://clients.samples.thewebsqueeze.com/$1 [R=301,L]
# END :: URL Rewrites
# START :: Strong .htaccess Protection
<Files ~ "^.*\.([Hh][Tt][Aa])">
order allow,deny
deny from all
</Files>
# END :: Strong .htaccess Protection
So, now we’ve defined some basic rules, and your directory listing should look like mine!

Next, we’re going to get started customizing the htaccess listing! So, what are you waiting for? Let’s go!
Pretty Directory Listing (htaccess)
# START :: Directory Customization <IfModule mod_autoindex.c>
Here we are creating an if module which says, if the Apache Module ‘mod_autoindex.c’ is available and installed, then do whatever is inside. You would end that IfModule like so…
</IfModule> # END :: Directory Customization
But, let’s not end it just yet. This is going to be the core of our directory listing. So, in-between the starting and ending ‘IfModule’, add the following.
# Set Index Options IndexOptions IgnoreCase FancyIndexing FoldersFirst NameWidth=* DescriptionWidth=* XHTML HTMLtable SuppressHTMLPreamble SuppressRules SuppressDescription SuppressLastModified SuppressSize IconHeight=16 IconWidth=16 IconsAreLinks
Here we’re setting our directory index options. That’s a lot to undertake, so I’ll go over it piece by piece.
IndexOptions
The IndexOptions directive specifies how Apache handles the directory indexing. You can see all the ‘IndexOptions’ available to you by seeing the Apache documentation.
http://httpd.apache.org/docs/2.0/mod/mod_autoindex.html#indexoptions
IndexOptions IgnoreCase FancyIndexing FoldersFirst
After ‘IndexOptions’ we are setting all the different options for that directory. We are choosing to ignore case for the directory listing. Next, we are applying ‘FancyIndexing’, and setting folders to display before files using the ‘FoldersFirst’ declaration.
NameWidth=* DescriptionWidth=* XHTML HTMLtable
We continue by setting the width of the name and description to ‘*’ which essentially means that it will grow the column to the necessary width in bytes. This means that no line wrapping will happen for longer file/directory names. We then turn on ‘XHTML’, you can leave this out if you are coding an HTML document. After that, we apply an HTML table around the directory listing using ‘HTMLtable’. This allows us to style the table without messing up the columns.
SuppressHTMLPreamble SuppressRules SuppressDescription SuppressLastModified SuppressSize IconHeight=16 IconWidth=16 IconsAreLinks
‘SuppressHTMLPreamble’ removes the starting , and
tags allowing you to code that in your header and footer documents below. ‘SuppressRules’ removes the horizontal rule lines (). We then remove the description (SuppressDescription), last modified date (SupressLastModified), and size columns (SuppressSize). After which, we set the ‘IconHeight’ and ‘IconWidth’ to 16px, and make the icons links. (IconsAreLinks) Continuing on….
# Set Display Order IndexOrderDefault Ascending Name
Here we set the index order to ascending by the file/folder name. So this means it’s in alphabetical order!
# Specify Header File HeaderName /i/header.shtml # Specify Footer File ReadmeName /i/footer.shtml
This is one of the keys! Here we are setting the location of the header and footer documents. These documents contain the structure of the directory listing. We’re using .shtml so that we can do some server side scripting later on. (Make sure you change the location/name of those documents to where they will be stored on your server.)
# Ignore These Files/Folders IndexIgnore header.shtml footer.shtml i images cgi-bin favicon.ico .htaccess .ftpquota .DS_Store icons *.log *,v *,t .??* *~ *#
Here we are telling Apache not to display any of the files or folders that follow ‘IndexIgnore’, this is great as it allows you to hide your images directory…etc.
</IfModule> # END :: Directory Customization
Finally, we end the ‘IfModule’. Quickly recapping the above code….
# START :: Directory Customization <IfModule mod_autoindex.c> # Set Index Options IndexOptions IgnoreCase FancyIndexing FoldersFirst NameWidth=* DescriptionWidth=* XHTML HTMLtable SuppressHTMLPreamble SuppressRules SuppressDescription SuppressLastModified SuppressSize IconHeight=16 IconWidth=16 IconsAreLinks # Set Display Order IndexOrderDefault Ascending Name # Specify Header File HeaderName /i/header.shtml # Specify Footer File ReadmeName /i/footer.shtml # Ignore These Files/Folders IndexIgnore header.shtml footer.shtml i _htaccess-code clients.zip images cgi-bin favicon.ico .htaccess .ftpquota .DS_Store icons *.log *,v *,t .??* *~ *# </IfModule> # END :: Directory Customization
Now, we need to add the SHTML documents to the mix. You’ll notice I use some server side scripting, however, I won’t go into detail on that as it’s not the objective of this tutorial.
HEADER.SHTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <!--#if expr="$Request_URI == '/'"--> <title>TWS Clients :: Choose A Client</title> <!--#elif expr="$Request_URI == /AOL\/?/"--> <title>America Online, LLC.</title> <!--#elif expr="$Request_URI == /Apple\/?/"--> <title>Apple Inc.</title> <!--#elif expr="$Request_URI == /Cisco\/?/"--> <title>Cisco Systems, Inc.</title> <!--#elif expr="$Request_URI == /Dell\/?/"--> <title>Dell, Inc.</title> <!--#elif expr="$Request_URI == /EA\/?/"--> <title>Electronic Arts, Inc.</title> <!--#elif expr="$Request_URI == /Google\/?/"--> <title>Google, Inc.</title> <!--#elif expr="$Request_URI == /IBM\/?/"--> <title>IBM, Inc.</title> <!--#elif expr="$Request_URI == /Logitech\/?/"--> <title>Logitech, Inc.</title> <!--#elif expr="$Request_URI == /Microsoft\/?/"--> <title>Microsoft, Inc.</title> <!--#else --> <title><!--#echo var="Request_URI" --></title> <!--#endif --> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <link rel="stylesheet" type="text/css" href="/i/style.css" /> <link rel="shortcut icon" href="/i/images/favicon.ico" /> </head> <body> <div id="wrap"> <div id="main"> <!--#if expr="$Request_URI == '/'"--> <h1>TWS Clients</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#elif expr="$Request_URI == /AOL\/?/"--> <h1>America Online, LLC.</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#elif expr="$Request_URI == /Apple\/?/"--> <h1>Apple Inc.</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#elif expr="$Request_URI == /Cisco\/?/"--> <h1>Cisco Systems, Inc.</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#elif expr="$Request_URI == /Dell\/?/"--> <h1>Dell, Inc.</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#elif expr="$Request_URI == /EA\/?/"--> <h1>Electronic Arts, Inc.</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#elif expr="$Request_URI == /Google\/?/"--> <h1>Google, Inc.</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#elif expr="$Request_URI == /IBM\/?/"--> <h1>IBM, Inc.</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#elif expr="$Request_URI == /Logitech\/?/"--> <h1>Logitech, Inc.</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#elif expr="$Request_URI == /Microsoft\/?/"--> <h1>Microsoft, Inc.</h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#else --> <h1><!--#echo var="Request_URI" --></h1> <h3 class="location">Location: <!--#echo var="Request_URI" --></h3> <!--#endif -->
FOOTER.SHTML
<p>Icons By: <a href="http://www.famfamfam.com/lab/icons/silk/">FAMFAMFAM</a></p> <p>&copy; Copyright 2009 <a href="http://www.thewebsqueeze.com/">The Web Squeeze, LLC.</a><br />All Rights Reserved.</p> </div> </div> </body> </html>
STYLE.CSS
html, body, div, span, applet, object, iframe,
header, footer, nav, section, article, aside,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, font, img, ins, kbd, q, s, samp,
small, strike, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin:0;
padding:0;
border:0;
outline:0;
font-weight:inherit;
font-style:inherit;
font-size:100%;
font-family:inherit;
vertical-align:baseline;
}
body {
background:#469bfc;
color:#333;
font-family:Tahoma, Geneva, sans-serif;
width:100%;
float:left;
padding:0 0 30px;
}
#wrap {
width:360px;
margin:30px auto 0;
}
#main {
width:300px;
float:left;
padding:30px;
background:#fafafa;
border:6px solid #007ede;
}
h1 {
font: 2.0em Georgia, serif;
text-align:center;
color:#78D316;
}
h3.location {
font-size:13px;
font-weight: bold;
margin:12px 0 30px;
text-align:center;
color:#4D7EC7;
}
a:link, a:visited {
text-decoration: none;
color: #024591;
}
a:hover, a:active {
text-decoration: underline;
color: #006be6;
}
table {
width:100%;
padding:0;
margin:0;
border:0;
}
tr {
width:100%;
padding:0;
margin:0;
border:0;
}
tr:nth-child(even) {
background:#def2ff;
}
th {
display:none;
}
td {
height:20px;
padding:10px;
margin:0;
}
td:nth-child(1){
width:16px;
}
hr {
display:none;
}
p {
margin:20px 0 0;
font-size:14px;
text-align:center;
}
Your directory listing should look like mine now!
![]()
But, what about the icons? Well, continue on to the next part!
Adding Custom Icons
Almost all of the icons used for directory symbols were downloaded from FAMFAMFAM. You can download the Silk Icon set below.
http://www.famfamfam.com/lab/icons/silk/
Once you’ve downloaded the icons, your ready to start! Find the following section of code.
</IfModule> # END :: Directory Customization
Above the end ‘’ add the following lines of code.
# START :: DEFINE FILE & FOLDER ICONS # Default Icon DefaultIcon /i/images/icons/resultset_next.png # Folder/Directory Icon AddIcon /i/images/icons/folder.png ^^DIRECTORY^^ # Up a directory AddIcon /i/images/icons/arrow_undo.png ..
Again, I heavily commented each line, thereby making it easier to update later. Basically, I’m defining a default icon for files, and a default icon for directories. After which we define the icon used to “Go Back”, or up a directory.
# Directory Icons AddIcon /i/images/client-icons/aol.png AOL AddIcon /i/images/client-icons/apple.png Apple AddIcon /i/images/client-icons/cisco.png Cisco AddIcon /i/images/client-icons/dell.png Dell AddIcon /i/images/client-icons/ea.png EA AddIcon /i/images/client-icons/google.png Google AddIcon /i/images/client-icons/ibm.png IBM AddIcon /i/images/client-icons/logitech.png Logitech AddIcon /i/images/client-icons/microsoft.png Microsoft
Now I’m setting custom icons for my main directory listing. You set a custom icon in the following format….
AddIcon /location/to/graphic.png FILE-NAME
So, you see how I assigned icons to certain folders, but what about file formats? You don’t want to have to assign an icon to every mp3 file using the above method. Here’s a method to assign an icon to file formats.
# START :: DEFINE FILE FORMAT ICONS # PDF Documents AddIcon /i/images/icons/book_open.png .pdf # Word Documents AddIcon /i/images/icons/page_white_word.png .txt .doc .rtf .log .asc # Pictures AddIcon /i/images/icons/picture.png .jpg .jpeg .jpe .png .gif .mpg .ico .psd # Music AddIcon /i/images/icons/music.png .mp3 .wav .vox .wma .ra .ram .ogg .vqf .aac # Video/Movies AddIcon /i/images/icons/film.png .mov .avi .wmv .mpeg # HTML AddIcon /i/images/icons/html.png .html .htm .shtm .shtml # XHTML AddIcon /i/images/icons/xhtml.png .xhtml # CSS AddIcon /i/images/icons/css.png .css # PHP AddIcon /i/images/icons/script.png .php # ASP / JavaScript AddIcon /i/images/icons/script.png .asp .js .xml # Flash AddIcon /i/images/icons/page_white_actionscript.png .swf .fla .as # Compressed Documents AddIcon /i/images/icons/page_white_zip.png .zip .rar .gz .bz2 # Disk Image AddIcon /i/images/icons/drive_web.png .iso .rpm # Torrent Files AddIcon /i/images/icons/application_lightning.png .torrent # END :: DEFINE FILE FORMAT ICONS
This basically works the same way.
AddIcon /location/to/graphic.png .FILE-TYPE
Again, all the icons are from the Silk Icon set by FAMFAMFAM. You can download them and view them here.
http://www.famfamfam.com/lab/icons/silk/
http://www.famfamfam.com/lab/icons/silk/previews/index_abc.png
You can also add file and folder descriptions using this format.
# START :: File Type Descriptions AddDescription "<span class='description'>Music/Sound File</span>" .mp3 AddDescription "<span class='description'>GZIP compressed TAR archive</span>" .tgz .tar.gz AddDescription "<span class='description'>GZIP compressed archive</span>" .Z .z .gz .zip ......etc..... # END :: File Type Descriptions
That adds a description for certain file types, with a span class around the description. You can also target certain directories or files.
# START :: Folder Descriptions AddDescription "[<span class='description'>Go Back..</span>]" .. AddDescription "[<span class='description'>American Online, LLC.</span>]" AOL AddDescription "[<span class='description'>Apple Inc.</span>]" Apple AddDescription "[<span class='description'>Cisco Systems, Inc.</span>]" Cisco AddDescription "[<span class='description'>Dell Inc.</span>]" Dell AddDescription "[<span class='description'>Electronic Arts, Inc.</span>]" EA AddDescription "[<span class='description'>Google, Inc.</span>]" Google AddDescription "[<span class='description'>IBM, Inc.</span>]" IBM AddDescription "[<span class='description'>Logitech, Inc.</span>]" Logitech AddDescription "[<span class='description'>Microsoft, Inc.</span>]" Microsoft # END :: Folder Descriptions
You can also add a default description for other “unknown” file types/folders by using a wildcard.
# Default Description AddDescription "<span class='description'></span>" *
Apache Directory Listing Gallery
I didn’t have time to cover all of the possible options you can use when styling your custom Apache directory listing, however, I hope the samples I designed will help you!
http://samples.thewebsqueeze.com/directory-listing/
Clients Listing
Media Listing
Linux Mirror
You can view all the designs, and download the code here.
http://samples.thewebsqueeze.com/directory-listing/
If you have any questions, ask them below. Now, go out a design your own fancy directory listings and post them in the comments section below!
























Comments
Nice article! A couple of comments:
By default Options already applies to all sub-directories. Using the + or – actually means add (or remove) this option to the ones already applied (the Apache doc’s call this merging versus overwriting).
Make sure you have a good reason to enable directory indexes. There’s a reason you’ll probably get a Forbidden page by default.
Watch out for the FollowSymLinks option as well, as it can be mis-used to allow the web server to access files outside of the document root. The SymLinksIfOwnerMatch is safer.
Great information. This will widen my knowledge in htaccess. Thanks Jacob!
Hey great article! I covered this technique in-depth over a year ago at Perishable Press:
http://perishablepress.com/press/2008/11/02/better-default-directory-views-with-htaccess/
Hopefully it will further help your readers create their own fancy directory listings!
Cheers!
Interesting article. I’d love to implement this on my servers
Trackbacks
Toggle Trackbacks
[... Fancy Directory Listing (Using htaccess) In this tutorial I cover the basics of htaccess, how to create a custom Apache directory listing using .htaccess. If you look at the default ...]
[... Fancy Directory Listing (Using htaccess) In this tutorial I cover the basics of htaccess, how to create a custom Apache directory listing using .htaccess. If you look at the default ...]
[... Fancy Directory Listing (Using htaccess) In this tutorial I cover the basics of htaccess, how to create a custom Apache directory listing using .htaccess. If you look at the default ...]
[... Fancy Directory Listing (Using htaccess) In this tutorial I cover the basics of htaccess, how to create a custom Apache directory listing using .htaccess. If you look at the default ...]
[... Fancy Directory Listing (Using htaccess) In this tutorial I cover the basics of htaccess, how to create a custom Apache directory listing using .htaccess. If you look at the default ...]