Why Your Nginx PHP Rewrites Might Fail — and How to Fix Them
Adam C. |

If you're trying to rewrite clean URLs like /results/summer-meet to /results/view.php?slug=summer-meet in Nginx, everything might seem simple... until your .php files start downloading instead of executing.

Here’s what’s going wrong — and how to fix it.

Photo by Elimende Inagella on Unsplash

❌ The Common (Broken) Approach Using Regex

location ~ ^/(results|standards|meet|heat-sheets|psych-sheets)/ {
    try_files $uri $uri/ @rewrite_view;
}

location @rewrite_view {
    rewrite ^/(results|standards|meet|heat-sheets|psych-sheets)/([a-z0-9\-]+)$ /$1/view.php?slug=$2 last;
}

Why it fails:

The location ~ block is a regex match.

Nginx gives regex blocks higher priority, and once it matches this block, it does not fallback to your global .phphandler like:

location ~ \.php$ { ... }

Result: /results/view.php gets matched by the regex block, and since there's no .php handler inside, Nginx serves it as a static file — or downloads it.

✅ Working Alternative (Split Routers, No Regex)

The safest way is to avoid regex and use one location per folder:

location /results/ {
    try_files $uri $uri/ @rewrite_results;
}

location @rewrite_results {
    rewrite ^/results/([^/]+)$ /results/view.php?slug=$1 last;
    return 404;
}

Repeat for each folder (/standards/, /meet/, etc.).

This works because:

location /results/ is a prefix match, not regex

If the file exists (like view.php), it falls through to the global .php$ handler correctly

No more downloads. Clean, clear routing.

✅ Advanced Fix: Keep Regex Router + Add Folder-Specific .php Handler

If you really want to keep the compact regex style, you must re-define .php handling for those folders:

location ~ ^/(results|standards|meet|heat-sheets|psych-sheets)/ {
    try_files $uri $uri/ @rewrite_view;
}

location ~ ^/(results|standards|meet|heat-sheets|psych-sheets)/.*\.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.2-fpm-swimsnap.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

This ensures PHP files inside those folders (like view.php) still get routed through PHP-FPM correctly.

🧠 Final Advice

Prefer prefix-based blocks (location /results/) unless you have a strong reason for regex

If you use location ~, you must handle .php within the same scope or via more specific regex

Always verify with nginx -t and test .php execution directly to avoid silent errors

If you're seeing downloads or blank responses, chances are it's not your rewrite — it's Nginx falling into the wrong block.