The original configuration uses conditional rewrites to handle two URL patterns:
location / {
if ( !-f $request_filename ) {
rewrite ^/([a-z]*)$ /index.php?action=$1;
rewrite ^/([a-z]*)/(.*)$ /index.php?action=$1&item=$2;
}
}
This handles both simple actions (/contact) and action-item pairs (/products/123). The new version needs to maintain this functionality while using try_files for better performance.
The attempted conversion has several issues:
try_files $uri $uri/ /index.php?action=$uri&item=$args;
1. $uri includes the leading slash which breaks parameter parsing
2. No pattern matching for action-item separation
3. $args contains original query string, not path components
Here's the corrected implementation that matches the original rewrite behavior:
server {
listen 80 default;
root /var/www;
index index.php;
server_name _;
location / {
try_files $uri $uri/ @rewrite;
}
location @rewrite {
rewrite ^/([a-z]+)(?:/(.*))?$ /index.php?action=$1&item=$2 break;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
For even cleaner configuration:
location @rewrite {
rewrite ^/(?[a-z]+)(?:/(?- .*))?$ /index.php?action=$action&item=$item break;
}
The try_files approach is more efficient than if conditions because:
- Reduced directive processing overhead
- Clearer file existence checking flow
- Better Nginx internal optimization
Verify with these test cases:
curl http://localhost/contact # Should map to action=contact
curl http://localhost/products/123 # Should map to action=products&item=123
curl http://localhost/style.css # Should serve static file directly
Many developers transitioning from older Nginx versions often struggle with converting legacy rewrite
rules to the modern try_files
approach. The original configuration was handling two patterns:
rewrite ^/([a-z]*)$ /index.php?action=$1;
rewrite ^/([a-z]*)/(.*)$ /index.php?action=$1&item=$2;
The attempted conversion using try_files
doesn't properly capture these patterns, resulting in 404 errors for dynamic routes.
The current implementation:
try_files $uri $uri/ /index.php?action=$uri&item=$args;
has several issues:
- It passes the full URI as the action parameter
- Doesn't properly separate the action from item components
- Handles query parameters incorrectly
Here's how to properly implement the routing with try_files:
server {
listen 80 default;
root /var/www;
index index.php;
server_name _;
location / {
try_files $uri $uri/ @rewrite;
}
location @rewrite {
rewrite ^/([a-z]+)/?$ /index.php?action=$1 last;
rewrite ^/([a-z]+)/([^/]+)/?$ /index.php?action=$1&item=$2 last;
rewrite ^/([a-z]+)/([^/]+)/(.*)$ /index.php?action=$1&item=$2¶ms=$3 last;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
For better maintainability, consider this pattern:
location / {
try_files $uri $uri/ @phprouter;
}
location @phprouter {
if ($uri ~ ^/([a-z]+)(?:/([^/]+)(?:/(.*))?)?$) {
set $action $1;
set $item $2;
set $params $3;
rewrite ^ /index.php?action=$action&item=$item¶ms=$params break;
}
}
- Always include
fastcgi_param SCRIPT_FILENAME
in PHP location - Use
last
flag for final rewrite rules - Consider security implications of dynamic routing
- Test with various URL patterns:
/about
,/products/item123
,/category/sub/item
For high-traffic sites, add caching:
location @phprouter {
# Cache regex matches
if ($uri ~ ^/([a-z]+)(?:/([^/]+))?$) {
set $action $1;
set $item $2;
rewrite ^ /index.php?action=$action&item=$item break;
}
# Fallback for longer URLs
rewrite ^/([^/]+)(?:/([^/]+)(?:/(.*))?)?$ /index.php?action=$1&item=$2¶ms=$3 break;
}