Score:1

rewrite and force redirect on rewrite origin url in nginx

in flag

I have this instance where a html search <form> forces it to go to /search/index.php?q=term and would like to use something like /search/term instead.

This is how the config looks like right now:

location /search/ {
    rewrite ^/search/(.+) /search/index.php?q=$1;
}

Accessing this location directly works, but in the moment of filling out the form, and searching it will use the "origin" rather than the rewrite.

I tried using redirect "backwards", but that crashed the nginx service:

location /search/ {
    rewrite ^/search/(.+) /search/index.php?q=$1;
    redirect ^/search/index.php?q=(.+) /search/$1;
}

I also tried rewrite with permanent flag, which didn't do anything

location /search/ {
    rewrite ^/search/(.+) /search/index.php?q=$1;
    rewrite ^/search/index.php?q=(.+) /search/$1 permanent;
}

I know it's possible to achieve the same thing in PHP with something like this:

// On the top of /search/index.php
if (str_starts_with($_SERVER['REQUEST_URI'], '/search/index.php')){
    $protocol = ($_SERVER['HTTP_X_FORWARDED_PROTO'] == "https") ? "https" : "http";
    $server = $_SERVER['HTTP_HOST'];
    $queries = array();
    parse_str($_SERVER['QUERY_STRING'], $queries);
    $search = $queries['q'] ?? "";
    header("Location: $protocol://$server/search/$search");
}

But think it would be better if nginx could handle this (if it's possible at all).

Does anyone know how to rewrite, and redirect access from the origin ?

Richard Smith avatar
jp flag
Don't know what the command `redirect` is. But can you show the access log entry for the request that fails?
Typewar avatar
in flag
@RichardSmith Ok I see, I got that wrong to begin with. I got it from here https://ubiq.co/tech-blog/rewrite-vs-redirect-nginx/ . Nginx systemd log does indeed say it's an unknown directive
Richard Smith avatar
jp flag
What do you mean by "origin"? Are you saying that the form sends a request to `/search/index.php?...`, and you would like the browser address bar to be changed to `/search/...`?
Typewar avatar
in flag
Yes, or more specifically: `/search/index.php?q=[term]`, and have it redirected to `/search/[term]`
Score:1
jp flag

The existing configuration is something like:

location /search/ {
    rewrite ^/search/(.+) /search/index.php?q=$1;
}
location ~ \.php$ {
    ...
}

The URI /search/index.php is used internally, so attempting to use rewrite ^/search/index.php?q=(.+) /search/$1 permanent; will generate a redirection loop.

To identify the URI as an external request, you need to look at the $request_uri variable. And for that, you will need to use the infamous if statement. For example:

if ($request_uri ~ ^/search/index\.php\?q=(.+)) {
    return 301 /search/$1;
}
location /search/ {
    rewrite ^/search/(.+) /search/index.php?q=$1;
}
location ~ \.php$ {
    ...
}
Typewar avatar
in flag
Ok, I see. I tried putting the if statement inside the scope of `/search/`, but that didn't work. Outside of the scope does work. Thank you so much!
Richard Smith avatar
jp flag
Indeed, the `/search/index.php` URI is handled by the `location ~ \.php$` block, as it ends in `.php`.
Typewar avatar
in flag
Ahh! that makes sense, thank you for clarifying
us flag
One can also use `if ($arg_q ~ (.+))` as the `if` condition. It matches only when query argument `q` is non-empty. Using `$request_uri` is not needed.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.