Apache Equivalent of Nginx’s try_files Directive: How to Safely Serve Static Files


2 views

When migrating from Nginx to Apache or working in mixed environments, one frequently encountered pain point is replicating Nginx's elegant try_files behavior. This directive provides a clean way to:

  • Check filesystem paths sequentially
  • Serve the first existing match
  • Fall back to specific handlers

The closest equivalent in Apache is the FallbackResource directive, though it requires careful configuration to match try_files functionality:


<Directory /var/www/static>
    # Basic static file handling
    FallbackResource disabled
    <FilesMatch "\.(gif|jpe?g|css|js)$">
        # Disable fallback for matched extensions
        FallbackResource disabled
    </FilesMatch>
</Directory>

For more complex scenarios similar to Nginx's regex location blocks, combine RewriteCond with filesystem checks:


RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+\.(?:gif|jpe?g|css|js))$ - [L]

The Apache approach requires more directives but offers comparable performance when properly configured:

  • Enable mod_rewrite for complex matching
  • Use Options -MultiViews to prevent content negotiation overhead
  • Consider mod_file_cache for high-traffic static files

Here's a production-ready configuration that closely mimics Nginx's behavior:


<VirtualHost *:80>
    DocumentRoot /var/www/html
    
    <Directory "/var/www/html">
        Options -Indexes -MultiViews
        AllowOverride None
        
        # Handle static files directly
        <FilesMatch "\.(gif|jpe?g|png|css|js|woff2?)$">
            RewriteEngine Off
            FallbackResource disabled
            Header set Cache-Control "max-age=31536000, public"
        </FilesMatch>
        
        # Fallback to front controller
        FallbackResource /index.php
    </Directory>
</VirtualHost>

When migrating from Nginx to Apache (or maintaining both), developers often need to replicate Nginx's elegant try_files behavior that sequentially checks file existence. The Nginx approach is particularly clean for static asset handling:

location ~* \\.(gif|jpe?g|css|js)$ {
    try_files $uri 404;
}

Apache achieves similar functionality through a combination of directives. The most precise equivalent uses RewriteCond with file existence checks:

<FilesMatch "\.(gif|jpe?g|css|js)$">
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ - [R=404,L]
</FilesMatch>

For more complex scenarios, consider these variations:

Basic static file handling:

<LocationMatch "\.(png|jpg|js|css)$">
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
    RewriteRule .* - [R=404]
</LocationMatch>

With fallback to controller:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]

The Apache implementation has slightly different performance characteristics than Nginx's native try_files:

  • Enable RewriteLog for debugging complex rules
  • Consider using FallbackResource for simpler cases
  • Always test with ab or similar benchmarking tools

Here's a complete virtual host configuration for a Laravel application:

<VirtualHost *:80>
    DocumentRoot /var/www/html/public
    
    <Directory /var/www/html/public>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^ index.php [L]
    </Directory>
    
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>