When building web applications, we often encounter situations where certain files need to be accessible to server-side scripts (PHP, Python, etc.) but should remain hidden from direct web access. The classic approach of moving files outside the document root isn't always practical, especially when these files need to coexist with publicly accessible content in the same directory structure.
For Apache servers running on Debian/Ubuntu systems, we have several robust methods to implement this restriction:
1. Using .htaccess with Pattern Matching
<FilesMatch "foobar\.txt$">
Require all denied
</FilesMatch>
This solution will block web access to ALL files named "foobar.txt" regardless of their location in your directory structure. The pattern matching makes it dynamic for your use case where the parent directory names change but the filename remains constant.
2. Directory-Level Restriction
For more granular control, place this in your Apache configuration or .htaccess:
<DirectoryMatch "/folder1/[^/]+/foobar\.txt$">
Require all denied
</DirectoryMatch>
To ensure your server-side scripts can still access these files while they're blocked from web access:
<?php
// Secure file reader function
function readProtectedFile($dynamic_folder) {
$base_path = '/var/www/vhosts/example.com/httpdocs/folder1/';
$filename = 'foobar.txt';
$filepath = $base_path . $dynamic_folder . '/' . $filename;
if(file_exists($filepath)) {
return file_get_contents($filepath);
}
return false;
}
?>
To prevent FTP access to these files while maintaining other FTP functionality:
# In vsftpd.conf
hide_file=foobar.txt
deny_file=foobar.txt
Or for ProFTPD:
<Directory "/var/www/vhosts/example.com/httpdocs/folder1/*">
HideFiles "^foobar\.txt$"
</Directory>
For a more maintainable structure:
# Create a non-web-accessible directory
mkdir -p /var/www/private_data/folder1
# For each dynamic folder:
ln -s /var/www/private_data/folder1/car/foobar.txt /var/www/vhosts/example.com/httpdocs/folder1/car/foobar.txt
Then set proper permissions:
chown www-data:www-data /var/www/private_data/folder1 -R
chmod 700 /var/www/private_data
Always verify:
- Your Apache configuration doesn't override .htaccess restrictions
- PHP open_basedir restrictions don't interfere with legitimate access
- File permissions are set correctly (640 for files, 750 for directories)
- Regularly audit access logs for unauthorized attempts
Many developers face situations where they need to store configuration files or sensitive data in web-accessible directories, but want to prevent direct HTTP access while still allowing server-side scripts (PHP, Python, etc.) to read these files. This is particularly common when:
- Configuration files need to be in specific locations for application structure
- Multiple environments (dev/staging/prod) share similar directory layouts
- Dynamic folder creation makes manual .htaccess updates impractical
For Apache servers, we have several approaches to solve this:
1. FilesMatch Directive in .htaccess
This solution works for dynamic subfolders while maintaining server-side access:
<FilesMatch "foobar\.txt$">
Require all denied
</FilesMatch>
2. LocationMatch Directive in Virtual Host
For better performance, add this to your Apache configuration (httpd.conf or site config):
<LocationMatch "/folder1/.*/foobar\.txt$">
Require all denied
</LocationMatch>
3. Combining with Filesystem Permissions
For complete protection, combine Apache rules with filesystem permissions:
# Set owner to www-data but remove world read permissions
chown www-data:www-data /var/www/path/to/files/foobar.txt
chmod 640 /var/www/path/to/files/foobar.txt
Server-side scripts can still access these files because they run as the web server user (www-data). Example PHP code:
<?php
// This will work even with HTTP access blocked
$config = file_get_contents('/var/www/vhosts/example.com/httpdocs/folder1/car/foobar.txt');
?>
To prevent FTP downloads while allowing server access:
- Create a separate system user for FTP access
- Set strict umask (e.g., 027) in your FTP server configuration
- Use filesystem ACLs for finer control:
setfacl -R -m u:ftpuser:--- /var/www/vhosts/example.com/httpdocs/folder1/*/foobar.txt
For better organization, store sensitive files outside webroot and create symlinks:
# Move sensitive files
mkdir -p /etc/webapp/configs/
mv /var/www/example.com/httpdocs/folder1/*/foobar.txt /etc/webapp/configs/
# Create symlinks
ln -s /etc/webapp/configs/car_foobar.txt /var/www/example.com/httpdocs/folder1/car/foobar.txt
Always verify your setup works as intended:
# Test HTTP access (should return 403)
curl -I http://example.com/folder1/car/foobar.txt
# Test server-side access (should work)
php -r "echo file_exists('/var/www/example.com/httpdocs/folder1/car/foobar.txt') ? 'OK' : 'Fail';"