# Nginx Try_Files Not Working
The try_files directive should gracefully handle missing files and fallback to alternative content, but instead you get 404 errors, infinite redirect loops, or all requests going to the fallback. Understanding how try_files evaluates its arguments and interacts with other directives is critical for proper fallback behavior.
Understanding Try_Files Behavior
- 1.
try_fileschecks each path in order: - 2.Checks if file exists (using
$urias path) - 3.Checks if directory exists (using
$uri/as path) - 4.Falls back to the last argument (URI or named location)
Basic example:
``nginx
location / {
root /var/www/html;
try_files $uri $uri/ /index.html;
}
- 1.Request
/about: - 2.Check
/var/www/html/about- file? No - 3.Check
/var/www/html/about/- directory? No - 4.Internal redirect to
/index.html
Common Cause 1: Order of Arguments Matters
Arguments are checked in sequence; wrong order breaks logic.
Problematic config:
``nginx
location / {
try_files /index.html $uri $uri/; # Wrong order
}
Every request immediately falls back to /index.html. $uri never checked.
Solution: Check specific first, fallback last:
``nginx
location / {
try_files $uri $uri/ /index.html;
}
Common Cause 2: Named Location vs URI Fallback
The last argument behavior differs.
URI fallback (starts internal redirect):
``nginx
try_files $uri $uri/ /index.html;
# Falls back to /index.html, processed by location matching
Named location fallback: ```nginx try_files $uri $uri/ @fallback;
location @fallback { proxy_pass http://backend; } ```
Named location bypasses normal location matching, goes directly to the named block.
Problematic config: ```nginx location / { try_files $uri $uri/ @backend; }
location @backend { # Named location doesn't inherit settings from parent proxy_pass http://localhost:3000; # Missing proxy_set_header Host $host; } ```
Solution: Configure named location fully:
``nginx
location @backend {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
Common Cause 3: Infinite Redirect Loop
Fallback URI triggers same location block recursively.
Problematic config:
``nginx
location / {
root /var/www/html;
try_files $uri $uri/ $uri.html =404;
}
- 1.If
/aboutdoesn't exist and/about/doesn't exist: - 2.Check
/about.html- doesn't exist - 3.Return 404
This works. But problematic:
location / {
try_files $uri $uri/ /; # / redirects back to this location
}- 1.Request
/missing: - 2.Check
/missing- no - 3.Check
/missing/- no - 4.Internal redirect to
/ - 5.
/matches same location - 6.Check
/- directory exists - 7.Serve index
Worse - actual loop: ```nginx location / { try_files $uri $uri.html /index.html; }
location = /index.html { try_files $uri $uri/ /; # / triggers location / # Loop! } ```
Solution: Ensure fallback doesn't trigger same logic:
``nginx
location / {
try_files $uri $uri/ =404; # End with 404, not redirect
}
Or use named location: ```nginx location / { try_files $uri $uri/ @fallback; }
location @fallback { proxy_pass http://backend; } ```
Common Cause 4: Try_Files with Alias
try_files uses $uri which doesn't account for alias mapping.
Problematic config:
``nginx
location /assets/ {
alias /var/www/files/;
try_files $uri $uri/ =404;
}
Request /assets/logo.png:
- try_files checks /assets/logo.png using $uri
- But file is at /var/www/files/logo.png (alias maps this)
- $uri path doesn't match alias path
**Solution: Use $request_filename or adjust logic:**
``nginx
location /assets/ {
alias /var/www/files/;
try_files $request_filename =404;
}
$request_filename reflects the actual file path after alias mapping.
Or simpler - alias handles files directly:
``nginx
location /assets/ {
alias /var/www/files/;
# Don't need try_files - alias handles file resolution
}
Common Cause 5: Try_Files Ignoring Index Files
Directory fallback doesn't serve index files automatically.
Problematic config:
``nginx
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
- 1.Request
/docs/(directory exists): - 2.
$uri=/docs/ - 3.Check
/docs/- is directory? Yes - 4.Nginx needs to serve directory index
- 5.But no index directive set!
Solution: Add index directive:
``nginx
location / {
root /var/www/html;
index index.html index.htm;
try_files $uri $uri/ =404;
}
Now /docs/ serves /docs/index.html.
Common Cause 6: SPA Routing with API Conflicts
Single-page apps need all routes to hit index.html, but API routes need to bypass.
Problematic config: ```nginx location / { try_files $uri $uri/ /index.html; }
location /api/ { proxy_pass http://backend; } ```
This works, but /api/users hits /api location first.
Problem: ```nginx location / { try_files $uri $uri/ /index.html; }
# API location missing - all /api requests go to index.html ```
Solution: Define API location with priority: ```nginx location ^~ /api/ { proxy_pass http://backend; }
location / { try_files $uri $uri/ /index.html; } ```
^~ ensures /api prefix stops further location searching.
Common Cause 7: Try_Files with Rewrite
Rewrite changes $uri before try_files evaluates.
Problematic config:
``nginx
location / {
rewrite ^/old/(.*)$ /new/$1 break;
try_files $uri $uri/ =404;
}
- 1.Request
/old/page: - 2.Rewrite changes
$urito/new/page - 3.
try_fileschecks/new/page - 4.Works correctly
But with last:
``nginx
location / {
rewrite ^/old/(.*)$ /new/$1 last; # restarts location matching
try_files $uri $uri/ =404;
}
Rewrite with last restarts location matching - try_files may not be reached.
Common Cause 8: Try_Files Not Serving Actual File
File exists but 404 returned anyway.
Diagnosis: ```bash # Check if file exists ls -la /var/www/html/about
# Check Nginx error log tail -f /var/log/nginx/error.log ```
Common cause: Permission denied:
``nginx
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
File /var/www/html/about exists, but www-data can't read it.
# Check permissions
ls -la /var/www/html/about
sudo -u www-data cat /var/www/html/aboutSolution: Fix permissions:
``bash
chmod 644 /var/www/html/about
chown www-data:www-data /var/www/html/about
Common Cause 9: Static Files Going to Backend
All requests, including static files, hit the fallback.
Problematic config: ```nginx location / { try_files $uri @backend; # Missing $uri/ for directories }
location @backend { proxy_pass http://backend; } ```
Directories (like /css/) don't exist as files, go directly to backend.
Solution: Include directory check: ```nginx location / { root /var/www/html; try_files $uri $uri/ @backend; }
location @backend { proxy_pass http://backend; } ```
Verification Steps
- 1.Test file resolution:
- 2.```bash
- 3.curl -I http://localhost/about
- 4.curl -I http://localhost/about/
- 5.curl -I http://localhost/missing
- 6.
` - 7.Add diagnostic headers:
- 8.```nginx
- 9.location / {
- 10.root /var/www/html;
- 11.try_files $uri $uri/ @fallback;
add_header X-Checked-Path $request_filename always; }
location @fallback { add_header X-Fallback "triggered" always; return 200 "fallback"; } ```
- 1.Check file existence:
- 2.```bash
- 3.ls -la /var/www/html/$uri
- 4.
` - 5.Monitor error log:
- 6.```bash
- 7.tail -f /var/log/nginx/error.log | grep "open()"
- 8.
` - 9.Debug internal redirects:
- 10.```nginx
- 11.error_log /var/log/nginx/debug.log debug;
- 12.
`
Complete Working Configurations
Static site with fallback:
``nginx
location / {
root /var/www/html;
index index.html;
try_files $uri $uri/ $uri.html =404;
}
Single-page application: ```nginx # API routes first location ^~ /api/ { proxy_pass http://backend; }
# Static assets location ^~ /static/ { root /var/www/html; expires 1y; }
# SPA fallback location / { root /var/www/html; try_files $uri $uri/ /index.html; } ```
With named location fallback: ```nginx location / { root /var/www/html; index index.html; try_files $uri $uri/ @backend; }
location @backend { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } ```
Quick Reference
| Issue | Cause | Fix |
|---|---|---|
| All requests go to fallback | Wrong argument order | Put $uri first |
| 404 on existing files | Permission denied | Fix file permissions |
| Infinite redirect loop | Fallback triggers same location | Use named location |
| Alias doesn't work | $uri doesn't match alias path | Use $request_filename |
| Directories return 404 | No index directive | Add index index.html |
| API goes to SPA fallback | Location priority | Use ^~ for API |
| Named location incomplete | Missing proxy headers | Configure named location fully |
try_files requires understanding the argument sequence, fallback behavior, and interaction with other directives. Check order first, then verify permissions and path resolution.