Nginx - Redirect based on query string parameters

We have a bit of a complicated problem to solve in our nginx configuration. Currently we have a piece of software installed in our document root. This software uses a single entry point (index.php) and query strings in order to show content. Example URLs are:

/index.php?forums/forum-name.1
/index.php?threads/thread-name.1
/index.php?users/user-name.1

Etc...

Now, we are moving this software into a subdirectory of /f/ and installing a new piece of software into the document root. This software ALSO uses index.php (No query strings, though). So we need to come up with a set of rewrite rules to ONLY rewrite the URLs from the old software. At the same time, we are also going to be removing the index.php from the URLs. A set of example mappings is:

/index.php?forums/forum-name.1 --> /f/forums/forum-name.1
/index.php?threads/thread-name.1 --> /f/threads/thread-name.1
/index.php?users/user-name.1 --> /f/users/user-name.1

So basically, I need to redirect a certain subset of index.php requests (Only containing ?forums, ?threads, ?users, etc...), then remove the index.php part and send to the /f/ directory.

I have played with this all morning and just can't get it to work how I need it to.


To manage complex redirections, particularly when query strings are involved, the map directive can be used.

You cannot match the query string (anything from the ? onwards) in location and rewrite expressions, as it is not part of the normalized URI, however, the $request_uri contains the original request complete with query string.

Matching the $request_uri may be problematic if the parameters are not sent in a consistent order, also URIs containing strange characters will be percent encoded.

The map directive can match strings and/or regular expressions. See this document for details.

For example:

map $request_uri $redirect {
    default                                               0;
    ~*^/index\.php\?(?<suffix>(forums|threads|users).*)$  /f/$suffix;
    ...
}

server {
    ...
    if ($redirect) {
        return 301 $redirect;
    }
    ...
}

Always use named captures in a map block regular expression. The mapped expression is evaluated at the return statement. As I understand it, every time nginx encounters a statement containing a regular expression (such as rewrite, some location blocks, and some if statements), the numeric captures are reset. Using named captures ensures that they remain in scope at the return statement.

See this caution on the use of if.