# Fix Nginx Location Block Regex Not Matching Query String Parameters

You write an Nginx location block to handle API requests with specific query parameters, but it never matches:

nginx
location ~ ^/api/search\?category=books$ {
    proxy_pass http://127.0.0.1:8080;
}

A request to /api/search?category=books bypasses this location entirely. The reason is fundamental to how Nginx processes URLs: location blocks match against the URI path only, never against the query string.

The query string (?category=books) is stored separately in the $args variable and is not part of the string that location directives match against.

How Nginx Actually Matches Locations

Nginx evaluates location blocks in this order:

  1. 1.Exact match: location = /path
  2. 2.Prefix match with ^~: location ^~ /path
  3. 3.Regex match (in order of appearance): location ~ /pattern
  4. 4.Longest prefix match: location /path

The matching string is the normalized URI without the query string. For /api/search?category=books&page=1, Nginx matches against /api/search only.

Correct Approach: Match Path, Then Check Arguments

```nginx location /api/search { if ($arg_category = "books") { proxy_pass http://books-backend; break; }

if ($arg_category = "electronics") { proxy_pass http://electronics-backend; break; }

proxy_pass http://search-backend; } ```

The $arg_<name> variables are automatically populated from query string parameters. $arg_category contains the value of the category parameter.

Using Map for Cleaner Routing

The if directive inside location blocks is discouraged by the Nginx community. A cleaner approach uses map:

```nginx map $arg_category $search_backend { default "search-backend"; books "books-backend"; electronics "electronics-backend"; clothing "clothing-backend"; }

server { upstream search-backend { server 127.0.0.1:8080; }

upstream books-backend { server 127.0.0.1:8081; }

location /api/search { proxy_pass http://$search_backend; proxy_set_header Host $host; } } ```

This approach is evaluated at the http level, avoiding the pitfalls of if inside location blocks. The map is evaluated once per request and the resulting variable is used in the proxy_pass directive.

Matching Multiple Parameters

For combinations of parameters, concatenate them in the map:

```nginx map $arg_category:$arg_sort $search_params { default "default"; books:relevance "books-relevance"; books:date "books-date"; electronics:price "electronics-price"; electronics:rating "electronics-rating"; }

location /api/search { proxy_pass http://$search_params; } ```

Rewriting Based on Query String

If you need to rewrite the URL based on query parameters:

```nginx location /api/search { if ($arg_cat) { rewrite ^ /api/search?category=$arg_cat&$args permanent; }

proxy_pass http://search-backend; } ```

Note that rewrite directives do see the full URI including query string when used with the full query string in the replacement. However, the location match that got us here was still based on the path alone.

Debugging Location Matches

To see which location block is matching a request, enable the rewrite log:

```nginx error_log /var/log/nginx/rewrite.log notice;

server { rewrite_log on;

location /api/search { add_header X-Location-Matched "search" always; proxy_pass http://search-backend; } } ```

The add_header directive with always shows the response header even on error responses, confirming the location match.

Common Mistake: Using $request_uri in Location

Some administrators try this:

nginx
location $request_uri { ... }

This is invalid syntax. The location directive accepts a static path or a regex pattern, not variables. Variables are only available in directives that execute during request processing, not during the configuration parsing phase when location matching is determined.