Apache Indexes restrict download file types

.htaccessapache-2.4

Can anyone guide me on how I can limit the file types that can be downloaded in Apache from a directory listing?

I have some Directory definitions in httpd.conf, like the following:

<Directory "/var/www/data01">
    Options Indexes FollowSymLinks
    Require all granted
    AllowOverride none
</Directory>

What I want to let the user do is list ALL of the files in every folder but only download files with specific extensions (e.g. *.txt)

I've tried this:

<Directory "/var/www/data01">
    Options Indexes FollowSymLinks
    IndexOptions ShowForbidden
    Require all granted
    AllowOverride all
    order allow,deny
    <FilesMatch "\.txt$">
        order deny,allow
        allow from all
    </FilesMatch> 
</Directory>

But this gives me a 'You do not have permission to access' error when I browse a folder.

Best Answer

You could use mod_rewrite. Try the following:

<Directory "/var/www/data01">
    Options Indexes FollowSymLinks
    IndexOptions ShowForbidden
    Require all granted
    AllowOverride all

    RewriteEngine On
    RewriteRule !(\.txt$|^$) - [NS,F]
</Directory>

This prevents access (403 Forbidden) if not requesting a .txt file or the bare directory (required in order to get the directory listing). The NS (nosubreq) flag is required in order to skip internal subrequests. When the directory listing is generated, an internal subrequest appears to be triggered for each file in the directory. So, without the NS flag, the directory listing is ultimately blocked.

Require all granted
order allow,deny

You shouldn't really mix Require (Apache 2.4+) and Order allow,deny (Apache 2.2). The Apache 2.2 directives are only available for backwards compatibility. You should be using Require on Apache 2.4+. Incidentally, these two directives do the opposite... the first one allows access, then the second denies it. The net result is that access is denied (but that's not because of the order of these directives, but because "denying" access wins).

UPDATE: To get this to work on subdirectories as well then change the RewriteRule directive to:

RewriteCond %{REQUEST_FILENAME} -f
RewriteRule !\.txt$ - [NS,F]

The RewriteCond (condition) directive ensures that the following directive only applies to actual files (ignoring directories). This allows us to simplify the RewriteRule pattern slightly since we don't need to explicitly check for the base directory.