How to Securely Restrict Web Access to Specific Files in Apache While Allowing Server-Side Access


1 views

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:

  1. Create a separate system user for FTP access
  2. Set strict umask (e.g., 027) in your FTP server configuration
  3. 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';"