Introduction
When Apache's Options Indexes directive is enabled, visiting a directory URL without an index file (like index.html) displays a listing of all files in that directory. This can expose sensitive files including .env configuration files, .git repositories, database backups, and application source code. A simple test confirms the issue:
bash
curl -s http://example.com/uploads/ | grep -c "<a href"
``
If the count is greater than 0 and shows file listings, directory listing is enabled.
Symptoms
- Browsing to a directory URL shows a file listing instead of a 403 Forbidden error
- Sensitive files (.env, .git/, config/, backups/) are visible to anyone
- Search engine indexes reveal directory contents
- Security scanners flag "Directory Indexing" vulnerability
.git/HEADor.git/configaccessible athttp://example.com/.git/config
Common Causes
Options Indexesset in the main Apache configuration or VirtualHost- Default Apache configuration on Debian/Ubuntu includes
Options Indexes FollowSymLinks .htaccessfile overrides the server-level directive to enable indexing- Application installer scripts add
Options Indexesfor upload directories - Configuration inherited from parent directory context
Step-by-Step Fix
- 1.Disable directory listing at the server or VirtualHost level:
- 2.```apache
- 3.<Directory /var/www/html>
- 4.Options -Indexes +FollowSymLinks
- 5.AllowOverride None
- 6.Require all granted
- 7.</Directory>
- 8.
` - 9.The
-Indexesprefix explicitly disables directory listing even if a parent directory enables it. - 10.Block access to hidden files and sensitive directories:
- 11.```apache
- 12.# Block all hidden files (starting with .)
- 13.<DirectoryMatch "/\.">
- 14.Require all denied
- 15.</DirectoryMatch>
# Block specific sensitive file types <FilesMatch "^\."> Require all denied </FilesMatch>
# Block .git directories specifically <DirectoryMatch "/\.git"> Require all denied </DirectoryMatch> ```
- 1.Block access to common sensitive files explicitly:
- 2.```apache
- 3.<FilesMatch "\.(env|sql|bak|old|orig|save|swp|dist|config|ini)$">
- 4.Require all denied
- 5.</FilesMatch>
- 6.
` - 7.Remove index files that enable directory browsing in specific locations:
- 8.```apache
- 9.# For upload directories, only disable indexing, not access
- 10.<Directory /var/www/html/uploads>
- 11.Options -Indexes
- 12.AllowOverride None
- 13.Require all granted
- 14.</Directory>
- 15.
` - 16.Verify the fix by testing common paths:
- 17.```bash
- 18.curl -s -o /dev/null -w "%{http_code}" http://example.com/uploads/
- 19.# Should return 403
curl -s -o /dev/null -w "%{http_code}" http://example.com/.git/config # Should return 403
curl -s -o /dev/null -w "%{http_code}" http://example.com/.env # Should return 403 ```
Prevention
- Start all new configurations with
Options -Indexesas the default - Include sensitive file blocking rules in your base Apache configuration template
- Run periodic security scans with tools like Nikto or Nuclei to detect directory listing
- Add directory listing checks to your CI/CD pipeline:
curl -sf http://staging/uploads/ | grep "Index of" && exit 1 - Use
mod_securityrules to automatically block requests to.env,.git/, and other sensitive paths - Document the expected directory listing configuration for each virtual host in your runbook
- After deploying changes, manually test directory URLs with a browser and curl