When working with Nginx as a reverse proxy for applications like Redmine, you'll notice that browsers tend to display plaintext files (like .txt) inline rather than triggering a download prompt. This behavior stems from how Nginx handles Content-Disposition headers for different MIME types.
By default, Nginx uses the default_type
directive and its MIME types configuration (/etc/nginx/mime.types
) to determine how to serve files. Text files typically receive a text/plain
Content-Type without a Content-Disposition: attachment
header, which tells browsers to display the content.
Add this to your Nginx server block or location configuration:
location ~* \.(txt|log|ini)$ {
add_header Content-Disposition "attachment";
default_type application/octet-stream;
}
For a Redmine-specific solution handling attachments through Rails, consider this comprehensive approach:
location /attachments {
# Force download for specific text-based formats
if ($request_filename ~* ^.+\.(txt|log|conf|ini|csv)$) {
add_header Content-Disposition "attachment";
}
# Standard proxy configuration
proxy_pass http://redmine_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
After applying these changes:
- Reload Nginx:
sudo nginx -s reload
- Clear your browser cache
- Test with various file types to verify the behavior
Another approach is to override the MIME type completely:
location ~* \.txt$ {
types { }
default_type application/octet-stream;
}
When working with file attachments in web applications, you'll notice browsers handle different MIME types differently. For text files (.txt, .csv, .log, etc.), browsers typically render the content directly in the viewport rather than triggering a download dialog. This behavior stems from the Content-Disposition
header not being properly configured.
Nginx determines whether to serve files inline or as downloads based on two key headers:
1. Content-Type: Identifies the MIME type of the file 2. Content-Disposition: Controls presentation style (inline vs attachment)
Add this to your Nginx configuration (nginx.conf
or site-specific config):
location ~* \.(txt|log|csv)$ { add_header Content-Disposition "attachment"; types { } default_type application/octet-stream; }
For more granular control, consider these variations:
# Force download for all text MIME types location ~* ^/.+\.(txt|log|csv)$ { if ($request_filename ~ ^.*?/([^/]*?)$) { set $filename $1; } add_header Content-Disposition "attachment; filename=\"$filename\""; default_type application/octet-stream; } # Preserve original filename with dynamic content location /downloads/ { add_header Content-Disposition "attachment"; types { text/plain txt log; text/csv csv; } }
After making changes:
sudo nginx -t # Test configuration sudo systemctl reload nginx
Verify with curl:
curl -I http://yoursite.com/path/to/file.txt # Should show: # Content-Disposition: attachment # Content-Type: application/octet-stream
1. Caching issues: Clear browser cache after changes
2. Overriding existing rules: Ensure your new location block takes precedence
3. Proxied content: If using Nginx as reverse proxy, set headers at both levels
The application/octet-stream
MIME type bypasses Nginx's default type processing. For high-traffic sites handling many small text files, consider:
location ~* \.(txt|log|csv)$ { add_header Content-Disposition "attachment"; types { text/plain txt log; text/csv csv; } gzip off; # Disable compression for these files }