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>