Styling Apache directory listings

In this post you will find how to make stylish listing generated by the Apache server. This is not just another CSS solution. The trick is in configuring Apache to generate XML directory list and to use browser (IE, FF, Opera, Safari …) to transform XML to the HTML. You will have full freedom to modify and style auto generated directory listings.

Contents
  1. Edit httpd.conf
  2. Create header.html file
  3. Set execute permission to the header.html
  4. Create stylesheet.xsl
  5. Conclusion

1. Edit httpd.conf
Open httpd.conf file and edit IndexOptions and HeaderName directive.

# create XHTML table 
IndexOptions XHTML HTMLtable
#  avoid duplicated <html><body> and no column sorting
IndexOptions SuppressHTMLPreamble SuppressColumnSorting
# place folders first
IndexOptions FoldersFirst
# add text/xml HTTP header
IndexOptions Type=text/xml  
# define absolute path to the header file
HeaderName /header.html

If you want to have current directory displayed as a page title, please add +Includes to the Options directive – this will enable SSI, but Apache will not parse files until you define SSI files. SSI files can be marked by extension like shtml or by execute bit. In this example, I marked SSI files with execution bit. Don’t forget to set execute permission to the SSI file or it will not be processed.

# parse SSI directives in files with the execute bit set
XBitHack on

Check if directory listing is enabled by Indexes option in Options directive.

# The Options directive is both complicated and important. Please see
# http://httpd.apache.org/docs/2.2/mod/core.html#options
# for more information.
Options Indexes ...

2. Create header.html file
header.html contains beginning of the XML listing. The rest of XML will be generated by Apache. XML has defined stylesheet and defined nbsp entity because Apache writes nbsp in emtpy table cells. Last line is a SSI directive, and if you need any other server variable, just wrap it within XML tag.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#160;"> ]>
<?xml-stylesheet type="text/xsl" href="/stylesheet.xsl"?>
<html><body>
<request_uri><!--#echo var="REQUEST_URI" --></request_uri>  

3. Set execute permission to the header.html
It is important to set execute permission to the header.html because Apache will process header file and execute line with REQUEST_URI. If you don’t need “current directory” info or any other dynamics in header file, you can skip this step and omit +Includes / XBitHack in httpd.conf. On the other hand, if Apache “knows” PHP, and you aren’t satisfied with static header.html, you can write PHP header file and you should name it header.php

<? print '<?xml version="1.0" encoding="utf-8"?>' ?>
<? print '<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#160;"> ]>' ?>
<? print '<?xml-stylesheet type="text/xsl" href="/stylesheet.xsl"?>' ?> 
<html><body>
<request_uri><?= $_SERVER['REQUEST_URI'] ?></request_uri> 

For PHP header file, you don’t need +Includes / XBitHack in httpd.conf because Apache has defined PHP interpreter to handle files with a php extension.

4. Create stylesheet.xsl
Here is a simple stylesheet.xsl to process dynamically generated XML. Please save stylesheet.xsl to the document root or change path in the header file. After directory request, Apache will respond with XML. Browser will start to process XML and make request for stylesheet.xsl file. With requested XML and stylesheet.xsl, browser will render HTML page. If you click on View page source, you will not see finished HTML, but XML generated by Apache.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8"
            doctype-public="-//W3C//DTD HTML 4.01//EN"
            doctype-system="http://www.w3.org/TR/html4/strict.dtd" />

	<xsl:template match="/html/body">
	  <html>
	    <head>
	    	<title>Directory: <xsl:value-of select="request_uri"/></title>
				<style>
					body         {font-family: sans-serif; text-align: center;}
					table        {margin: 0px auto; border-collapse: collapse;}
					td           {border: 1px solid lightgrey;}
					a, a:visited {color: #ff6600; text-decoration: none;}
					a:hover      {text-decoration: underline;}
				</style>
			</head>
			<body>
				<h2>Directory: <xsl:value-of select="request_uri"/></h2>
				<table>
					<!-- define table column width -->
					<colgroup>
						<col width="50"/>  <!-- icon -->
						<col width="400"/> <!-- link -->
						<col width="180"/> <!-- last modified -->
						<col width="50"/>  <!-- size -->
					</colgroup>
					<!-- process 'tr' rows with td only (skip th rows) -->
					<xsl:apply-templates select="table/tr[td]"/>
				</table>
			</body>
	  </html>
	</xsl:template>
	
	<!-- print table rows -->
	<xsl:template match="tr">
		<tr>
			<td align="center"><xsl:copy-of select="td[position()=1]/*"/></td>
			<td align="left"><xsl:copy-of select="td[position()=2]/a"/></td>
			<td align="center"><xsl:value-of select="td[position()=3]"/></td>
			<td align="right"><xsl:value-of select="td[position()=4]"/></td>
		</tr>
	</xsl:template>
</xsl:stylesheet>

5. Conclusion
Described procedure may seem complicated, but it all boils down to the two files and a couple of lines in the httpd.conf file. First file is header file – static HTML or HTML with SSI or PHP (depends what you need). And the second file is XSL. Place files inside document root and make absolute reference to them in HeaderName directive and in header file. After files are created and Apache is configured, you should have a styled directory listing page.

I hope you might find this post useful.
Cheers!

23 thoughts on “Styling Apache directory listings

  1. @Andre – I’m glad that you manage to solve the UTF-8 problem. And regarding parent directory icon issue. Must admit that I didn’t have situation described as yours. I have only post a solution how to hide parent directory with “IndexIgnore ..” settings (but that should not be related to parent directory icon). It’s not excluded that your Apache version doesn’t have such option or you maybe found a bug. Sorry, but I can only ask you to post a comment after you will find the solution – to help the others ;)
    Thanks!

  2. Okay, it seems that I found what was causing this. Apparently when i have a htaccess file with basic authentication required in a directory it is automatically hidden from the directory listing and with it, the “parent directory” icon will become corrupt.

    For example, having a directory tree:

    /files/shared/protected and in it a .htaccess file with basic authentication enabled will not display the “protected” folder on /files/shared/

    Not only that, but it also will not recognize the icon assigned to it. E.g while in /files/shared/ there is an icon displayed with alt=”[IMG]”, but in /files/shared/protected the icon cannot be fetched, having an alt=”[ ]” assigned to it with no attributes.

    Previously I had a more basic styling of directory listing, using only html + css and I did not have a problem displaying protected folders and icons in them. Perhaps the problem lies in the xml? Would be awesome if you got it fixed, but if not – thats okay too and I’m willing to live with that, because ain’t no way I’m going back to the basic styling, which didn’t allow me to define column widths (and some other things) properly.

    —-

    I’m sorry if the text didn’t make any sense, I tried to pass it on as good as I possibly could (not a native speaker).

  3. @Andre – Your comment is welcome and it’s valuable for others with similar problem and how to solve it.
    Once again, thank you very much for feedback.
    Cheers!

Leave a Comment