Why it matters
nginx doesn't pick a location by reading top to bottom. It looks for an exact match first, then remembers the longest prefix, checks any locations nested inside it, then tries regex locations in the order they appear, and the first regex that matches wins over the prefix, unless the prefix was marked ^~. Add try_files falling back to a PHP front controller, an alias with a missing slash or a proxy_pass with a trailing slash, and it's easy to be sure a request goes one place when it goes another.
This tester runs that algorithm on your config. For each URL it shows the location chosen and why, then follows the server and location rewrites, the if blocks it understands, return, try_files and index against a list of files you say exist, and works out the final file path or the upstream URL proxy_pass would request, including internal redirects back through the location search. It's one server block and the common directives, run in your browser; for the last word, nginx -t and a test request still decide.
How it works
- InputA server block and test URLs
- In your browsernginx's own location order, then rewrites and try_files
- OutputThe block, result and every step per URL
Where it fits in a designer's workflow
- Plan
- Design
- Build
- Test
- Launch
By the numbers
Show the numbers as a table
| URL | Location | Result |
|---|---|---|
| / | location = / | 200 file /var/www/shop/public/index.html |
| /static/img/logo.png | location ^~ /static/ | 200 file /var/www/shop/assets/img/logo.png |
| /images/oak-table.webp | location ~* \.(png|jpe?g|webp|avif|svg)$ | 200 file /var/www/shop/public/images/oak-table.webp |
| /images/private/invoice.png | location ~ ^/images/private/ | 403 |
| /api/orders?page=2 | location /api/ | Proxied to 127.0.0.1:3000/v2/orders?page=2 |
| /old-shop/tables?sort=price | — | 301 → /shop/tables?sort=price |
| /guides/oak-care | location ~ \.php$ | Proxied to unix:/run/php/php8.3-fpm.sock (SCRIPT_FILENAME /var/www/shop/public/index.php) |
| /about/ | location / | 200 file /var/www/shop/public/about/index.html |
| /admin.php | location ~ \.php$ | Proxied to unix:/run/php/php8.3-fpm.sock (SCRIPT_FILENAME /var/www/shop/public/admin.php) |
How to use it
- Paste the server block (or a whole nginx.conf: the first server block is used).
- List the URLs to test, and the files that exist under root.
- Read the matched location and result for each URL.
- Pick a URL to see every step nginx takes.
FAQ
In what order does nginx check location blocks?
Exact (=) locations first. Then it finds the longest matching prefix and checks locations nested inside it. If that prefix has ^~, it stops; otherwise it tries regex locations (~ and ~*) in the order they appear in the file and uses the first match. If no regex matches, the longest prefix is used.
Why does my regex location win over a longer prefix?
Because regexes are checked after prefixes and override them, however long the prefix. Mark the prefix location ^~ to stop regex checking for URLs under it.
What does the trailing slash in proxy_pass change?
With a URI part (a path after the upstream address, such as app:3000/v2/) the matched location prefix is replaced by that path. Without one (just app:3000) the original request URI is passed unchanged. In a regex location a URI part is a config error.
Why do I have to list files?
try_files, index and -f checks depend on what's on disk, which this page can't see. List the paths that exist and the tester follows the same branches nginx would.
Does it support every directive?
No. It covers location matching, rewrite, return, set, a subset of if, try_files, index, root, alias, proxy_pass and fastcgi_pass. Includes and maps aren't read; anything else is ignored. Regexes run as JavaScript, and PCRE-only syntax is flagged.