The NGINX try_files directive routes requests by testing multiple file paths in sequence. It powers WordPress pretty permalinks, Laravel routing, and single-page application (SPA) deployments. However, many tutorials overlook critical performance considerations. This comprehensive guide covers everything you need to know about nginx try_files.
What is NGINX try_files?
The nginx try_files directive checks whether files or directories exist on disk. NGINX tests each path in order until it finds a match. The last parameter serves as the fallback when all previous checks fail.
Here is the basic syntax:
try_files file1 file2 ... fallback;
NGINX processes try_files during the PRECONTENT_PHASE. This means the directive runs after access control but before content handlers. Therefore, try_files can rewrite the request URI before static file serving or FastCGI processing begins.
NGINX try_files Syntax Explained
The try_files directive accepts two or more parameters. You can use it in server and location contexts. Let’s examine each component in detail.
File Paths and Variables
Each parameter can be a static path or contain variables:
location / {
try_files $uri $uri/ /index.php?$query_string;
}
Common variables include:
| Variable | Description |
|---|---|
$uri |
The normalized request URI |
$document_root |
The root directive value |
$query_string |
Query parameters after ? |
$args |
Alias for $query_string |
The $uri variable is the most commonly used parameter. It contains the normalized request path without query string parameters. For example, a request to /blog/post?page=2 sets $uri to /blog/post.
Directory Testing with Trailing Slash
Add a trailing slash to test for directories:
try_files $uri $uri/ =404;
In this example, $uri/ checks if a directory exists. NGINX then looks for an index file within that directory based on the index directive. The index directive specifies which files NGINX should serve when a directory is requested.
Fallback Options
The last parameter is always the fallback. You have three options:
1. Internal Redirect to URI:
try_files $uri $uri/ /index.php;
This pattern redirects internally to index.php when no file or directory matches. NGINX processes the request as if the client originally requested /index.php.
2. Named Location:
try_files $uri $uri/ @backend;
location @backend {
proxy_pass http://upstream;
}
Named locations provide clean separation of concerns. They cannot be accessed directly by clients, which adds a layer of security to your configuration.
3. HTTP Status Code:
try_files $uri =404;
The status code format uses = followed by the code. Valid codes range from 0 to 999. Use this when you want to return an error for missing files.
How NGINX try_files Works Internally
Understanding the internal mechanics helps you write better configurations. NGINX pre-compiles variable references at configuration load time. This optimization eliminates runtime parsing overhead.
For each request, NGINX:
- Calculates the full filesystem path from each parameter
- Uses
ngx_open_cached_file()to check file existence - Returns immediately upon finding a valid file
- Falls back to the last parameter if all checks fail
The open file cache integration is crucial for performance. Without caching, NGINX performs stat() syscalls for every file in the try_files chain on every request. This becomes expensive under high traffic conditions.
Additionally, NGINX handles alias configurations specially when using try_files. The module carefully reconstructs paths to maintain proper alias semantics. This allows try_files to work correctly with both root and alias directives.
Common NGINX try_files Patterns
Let’s explore practical configurations for different use cases. Each pattern addresses specific routing requirements.
Static File Serving with 404 Fallback
The simplest pattern serves static files and returns 404 for missing resources:
server {
listen 80;
server_name example.com;
root /var/www/html;
# Performance optimization - required for try_files
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
location / {
try_files $uri $uri/ =404;
}
}
This configuration first checks if the exact file exists. Then it tests for a directory with an index file. Finally, it returns a 404 error. This pattern works well for static websites and documentation sites.
WordPress NGINX try_files Configuration
WordPress requires all requests for non-existent files to route through index.php. Here is the standard WordPress configuration:
server {
listen 80;
server_name wordpress.example.com;
root /var/www/wordpress;
index index.php index.html;
# Performance optimization
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
The $args variable preserves query string parameters. This ensures WordPress receives pagination and search parameters correctly. Without this, WordPress pretty permalinks would break.
For more WordPress optimization, see our guide on NGINX FastCGI Keepalive for persistent PHP-FPM connections.
Laravel NGINX try_files Configuration
Laravel applications route all requests through public/index.php. The configuration is similar to WordPress but with important differences:
server {
listen 80;
server_name laravel.example.com;
root /var/www/laravel/public;
index index.php;
# Performance optimization
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Notice the root points to Laravel’s public directory. This is essential for security, as it prevents direct access to configuration files. Laravel stores sensitive files like .env outside the public directory.
Single Page Application (SPA) Routing
React, Vue, and Angular applications handle routing client-side. NGINX must serve index.html for all routes:
server {
listen 80;
server_name spa.example.com;
root /var/www/spa/dist;
index index.html;
# Performance optimization
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# Serve static assets directly
location /assets/ {
try_files $uri =404;
}
# All other routes go to index.html
location / {
try_files $uri $uri/ /index.html;
}
}
Static assets like JavaScript and CSS files are served directly from the assets directory. All other requests receive the SPA’s index.html for client-side routing. The JavaScript framework then handles route matching on the client side.
Named Location Pattern
Named locations provide cleaner configurations for complex routing. They begin with the @ symbol:
server {
listen 80;
server_name api.example.com;
root /var/www/api/public;
# Performance optimization
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
location / {
try_files $uri $uri/ @backend;
}
location @backend {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Named locations cannot be accessed directly by clients. They only execute through internal redirects from try_files or error_page directives. This design pattern keeps your configuration organized and maintainable.
For more on proxy configuration, see our NGINX Reverse Proxy guide.
Performance Optimization with open_file_cache
The try_files directive performs filesystem lookups for each path. Without caching, this creates significant overhead. The open_file_cache directive caches file metadata to eliminate redundant syscalls.
Why open_file_cache Matters
Consider this configuration without caching:
location / {
try_files $uri $uri/ /index.php?$args;
}
For a request to /blog/post-title, NGINX executes:
stat("/var/www/html/blog/post-title")– file checkstat("/var/www/html/blog/post-title/")– directory check- Falls back to
/index.php
These syscalls happen on every single request. On a busy site handling thousands of requests per second, this overhead adds up quickly. Moreover, disk I/O becomes a bottleneck even with modern SSDs.
With open_file_cache, results are cached:
# Cache up to 1000 file descriptors
open_file_cache max=1000 inactive=20s;
# Revalidate cached entries after 30 seconds
open_file_cache_valid 30s;
# Cache entries used at least 2 times
open_file_cache_min_uses 2;
# Cache "file not found" errors
open_file_cache_errors on;
The security tool gixy specifically warns when try_files is used without open_file_cache. The warning categorizes this as a MEDIUM severity issue. Consequently, you should always pair these directives together.
Recommended open_file_cache Settings
For most production servers:
http {
# Global open_file_cache settings
open_file_cache max=2000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# ... server blocks
}
Adjust max based on your file count. Busy sites with many static assets may need higher values. Furthermore, monitor your server’s memory usage when increasing cache sizes.
Common NGINX try_files Mistakes
Avoid these frequent configuration errors that can impact performance and functionality.
Missing open_file_cache
The most common mistake is omitting open_file_cache:
# Bad: No caching leads to excessive syscalls
location / {
try_files $uri $uri/ =404;
}
Always include open_file_cache when using try_files. This simple addition dramatically improves performance on high-traffic sites.
Incorrect Path Order
Order matters in try_files. Place more specific paths first:
# Bad: Directory check before file check is inefficient
location / {
try_files $uri/ $uri =404;
}
# Good: File check first, then directory
location / {
try_files $uri $uri/ =404;
}
File checks are faster than directory checks because they require fewer operations. Place them first for better performance.
Using try_files with if Blocks
Combining try_files with if blocks can cause unexpected behavior. The NGINX if is evil guide explains why in detail.
# Risky: if directive with try_files
location / {
if ($request_uri ~ "^/admin") {
# This might not work as expected
}
try_files $uri $uri/ /index.php;
}
Use map directives or separate location blocks instead. These alternatives provide predictable behavior.
Forgetting the Fallback
Every try_files directive needs a fallback:
# Wrong: No fallback - NGINX will reject this
location / {
try_files $uri;
}
# Correct: Always include a fallback
location / {
try_files $uri =404;
}
NGINX requires at least two parameters for try_files. The configuration test will fail without a proper fallback.
Security Considerations
The try_files directive interacts with file permissions and paths. Keep these security points in mind when configuring your server.
Prevent Directory Traversal
The try_files directive uses normalized URIs. NGINX removes .. sequences before processing. However, verify your alias vs root configuration to prevent traversal issues.
Protect Sensitive Files
Exclude sensitive files from being served:
# Block access to hidden files
location ~ /\. {
deny all;
}
# Block access to sensitive extensions
location ~ \.(env|git|svn|htaccess)$ {
deny all;
}
location / {
try_files $uri $uri/ /index.php;
}
Place deny rules before try_files locations. NGINX location priority determines which block handles each request.
Validate Configuration with Gixy
Use gixy to audit your NGINX configuration:
gixy /etc/nginx/nginx.conf
Gixy checks for common security issues including:
- Missing open_file_cache with try_files
- Alias path traversal vulnerabilities
- Regex security issues
- SSL/TLS configuration problems
Running gixy regularly helps catch configuration mistakes before they reach production.
NGINX try_files vs rewrite
Both directives can route requests, but they serve different purposes. Understanding when to use each helps you write cleaner configurations.
When to Use try_files
Use try_files when:
- Testing file or directory existence
- Implementing front controller patterns
- Routing to index files or named locations
# Good use of try_files
location / {
try_files $uri $uri/ /index.php;
}
The try_files directive excels at conditional routing based on filesystem state.
When to Use rewrite
Use rewrite when:
- Changing URLs permanently or temporarily
- Capturing and transforming URI components
- Implementing SEO redirects
# Good use of rewrite
rewrite ^/old-path/(.*)$ /new-path/$1 permanent;
See our NGINX Rewrite Rules guide for detailed examples.
The key difference is that try_files tests file existence while rewrite transforms URIs unconditionally. Choose the appropriate directive based on your requirements.
Debugging NGINX try_files
When try_files doesn’t work as expected, enable debug logging:
error_log /var/log/nginx/error.log debug;
Debug output shows each file NGINX tests:
[debug] try files handler
[debug] trying file: "/var/www/html/blog/post"
[debug] trying file: "/var/www/html/blog/post/"
[debug] trying fallback: "/index.php"
Common issues to check:
- Root path: Verify
rootoraliaspoints to the correct directory - Permissions: Ensure NGINX can read files and traverse directories
- SELinux: On RHEL-based systems, check SELinux contexts with
ls -Z - Index directive: Confirm
indexincludes your default file
For permission issues, see our guide on optimizing NGINX for PHP websites.
Complete Production Configuration
Here is a production-ready configuration combining all best practices:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.php index.html;
# Security headers
server_tokens off;
# Performance: Open file cache for try_files
open_file_cache max=2000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# Block sensitive files
location ~ /\. {
deny all;
}
# Static assets with long cache
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?)$ {
try_files $uri =404;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Front controller pattern
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP-FPM handling
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
This configuration includes:
- File caching for optimal performance
- Security headers and sensitive file blocking
- Optimized static asset handling with browser caching
- Front controller routing for PHP applications
Summary
The NGINX try_files directive is essential for modern web applications. It enables clean URLs for WordPress, Laravel, and single-page applications. However, proper configuration requires attention to performance and security.
Key takeaways:
- Always use open_file_cache with try_files to avoid excessive syscalls
- Order paths correctly – file checks before directory checks
- Include a fallback as the last parameter
- Validate with gixy to catch common misconfigurations
- Use named locations for complex routing scenarios
For additional NGINX optimization, explore our guides on browser caching, gzip compression, and proxy caching.

