How to Force File Downloads with Content-Disposition Headers in Apache 2 Configuration


2 views

When serving text files through Apache, browsers typically display the content directly rather than triggering a download prompt. This behavior applies to common text formats like .txt, .csv, .json, and others with recognizable MIME types. For web applications where file downloads are preferred over inline viewing, we need server-side intervention.

The most straightforward approach uses Apache's mod_headers to inject Content-Disposition headers. Here's a complete virtual host configuration example:

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/textfiles
    
    <Directory /var/www/textfiles>
        Header set Content-Disposition "attachment"
        # Alternative for dynamic filename matching:
        # Header edit Content-Disposition "^$" "attachment; filename=\"%f\""
    </Directory>
</VirtualHost>

For more granular control, you can target specific file extensions using FilesMatch:

<FilesMatch "\.(txt|csv|json|log)$">
    ForceType application/octet-stream
    Header set Content-Disposition "attachment; filename=\"%f\""
</FilesMatch>

When working with complex directory structures, consider these approaches:

# For files in specific subdirectories
<DirectoryMatch "/var/www/(downloads|exports)/.*">
    Header set Content-Disposition "attachment"
</DirectoryMatch>

# For files with numeric names (e.g., report_123.txt)
<FilesMatch "[0-9]+\.txt$">
    Header set Content-Disposition "attachment"
</FilesMatch>

After implementing these changes:

  1. Run sudo apachectl configtest to verify syntax
  2. Reload Apache: sudo systemctl reload apache2
  3. Test with curl: curl -I http://yoursite.com/example.txt

For shared hosting environments where you can't modify the main config:

# In the text files directory's .htaccess
<IfModule mod_headers.c>
    <FilesMatch "\.(txt|text)$">
        Header set Content-Disposition "attachment"
    </FilesMatch>
</IfModule>

While these solutions work well, be aware that:

  • Header manipulation adds slight processing overhead
  • Complex regex patterns in FilesMatch may impact performance
  • Caching behavior might change with forced downloads

When serving files via Apache, browsers typically display text-based files (like .txt, .csv, or .log) directly in the browser window. However, there are cases where you want users to download these files instead. This is where the Content-Disposition header comes into play.

The most straightforward way to implement this is using Apache's Header directive in your configuration:



    Header set Content-Disposition "attachment; filename=\"%f\""

This configuration will:

  • Match files with .txt, .log, or .csv extensions
  • Set the Content-Disposition header to "attachment"
  • Include the original filename using the %f variable

If you want to apply this to a specific directory (as mentioned in your example), you can use:



    
        Header set Content-Disposition "attachment; filename=\"%f\""
    

For more complex scenarios where you want to conditionally force downloads, you can combine mod_rewrite with environment variables:


RewriteEngine On
RewriteCond %{QUERY_STRING} download=1
RewriteRule \.(txt|log|csv)$ - [E=FORCE_DOWNLOAD:1]


    Header set Content-Disposition "attachment; filename=\"%f\""

This allows you to control downloads via URL parameters (e.g., file.txt?download=1).

On Debian systems, you'll typically want to place these configurations in either:

  1. The virtual host configuration in /etc/apache2/sites-available/
  2. A .htaccess file in your target directory (if AllowOverride is enabled)

Remember to enable the headers module if it's not already active:


sudo a2enmod headers
sudo systemctl restart apache2

After implementing these changes, verify the headers are being set correctly using curl:


curl -I http://yourserver.com/example.txt

You should see a response header containing:


Content-Disposition: attachment; filename="example.txt"