I'm trying to create a rewrite rule in Nginx to remove trailing slashes.
My rule so far is:
rewrite ^/(.*)/$ /$1;
I want to remove trailing slashes for all URL:s except those that has a query string directly after a trailing slash, how do you do that? I mean URLs that look like https://www.domain.tld/page/?query=xxx
When using rewrite, the query string is appended automatically
From http://wiki.nginx.org/HttpRewriteModule:
If in the line of replacement arguments are indicated, then the rest of the request arguments are appended to them. To avoid having them appended, place a question mark as the last character:
Just an FYI if you are using rewrite for a redirect it is not recommended but no one is going to stop you.
This rule removes trailing slash from url on both cases and worked fine for me; you can customize the the if logic by the way
location ~ (.*)/$ {
if ($query_string) {
return 301 $scheme://$host$1?$query_string;
}
return 301 $scheme://$host$1;
}
Related
I want to rewrite an argument given via URL on my nginx server from "my_arg" to "my-arg". The problem is that I can't know how many arguments there will be in advance nor the position of these arguments.
I can only know if the arguments is there using :
if ($args ~ (.*)my_arg=(.*)) {
rewrite ....
}
But i cannot achieve to only change the name of this argument without changing every others.
Thanks by advance
For anything except HTTP 301/302 redirection you'd better just change an $args variable value:
if ($args ~ (^|.*&)my_arg=(.*)) {
set $args $1my-arg=$2;
}
If you want to generate a redirect, things will be more complicated. Since any rewrite directive performs its own URI matching against some regex pattern, all the $1, $2 and so on variables from previous matching will be overwritten, and you can't match a query part of the URI to get them re-evaluated since rewrite directive works with normalized URI that doesn't include a query part. Your only way is to use named capture groups:
if ($args ~ (?<prefix>^|.*&)my_arg=(?<suffix>.*)) {
rewrite ^ $uri${prefix}my-arg=$suffix? <redirect|permanent>;
}
Trailing question mark used intentionally since as rewrite directive documentation says,
If a replacement string includes the new request arguments, the previous request arguments are appended after them. If this is undesired, putting a question mark at the end of a replacement string avoids having them appended, for example:
rewrite ^/users/(.*)$ /show?user=$1? last;
I'm trying to make a rewrite rule in Nginx to remove trialing question mark (?) from urls but i can't get it right. I've done that for trailing slashes like this:
#redirect all trailing slash URL's to the respective non trailing slash
rewrite ^/(.*)/$ /$1 permanent;
so I figured the same would work just replacing the slash with the question mark:
rewrite ^/(.*)?$ /$1 permanent;
but that didn't work, but it occurred to me that the question mark has some significance in the regex so i tried escaping it:
rewrite ^/(.*)\?$ /$1 permanent;
but that didn't work either, I tried also removing the first slash:
rewrite ^(.*)\?$ $1 permanent;
but that was also a bust, and yes i did restart the server in between tests.
Here's what I am trying to do:
www.mysite.com? should redirect to wwww.mysite.com
www.mysite.com/some/path? should redirect to wwww.mysite.com/some/path
www.mysite.com?some=vars should remain unchanged.
www.mysite.com/some/path?some=vars should remain unchanged.
so basically only removing the question mark if there is no query string.
How can i accomplish that?
I've checked other answers but they seem to want to remove the query string entirely, I only want to remove in the case that there is only a question mark and no parameters.
The ? marks the start of the query string and is not part of the normalized URI used by the rewrite or location directives. So you cannot remove it using a rewrite statement.
You will need to look at the original request which is in the $request_uri variable.
For example:
if ($request_uri ~ ^(.*)\?$) { return 301 $1; }
See this caution on the use of if.
When using a map file to rewrite a large number of locations to their destinations:
rewrite ^ $my_redirect_map permanent;
Inside the map file, some redirects look like this (including trailing slash):
/foo/ /bar;
However, if nginx receives a request without a trailing slash, e.g. http://example.com/foo then the redirect doesn't occur.
It can be worked around by including duplicates of every entry in the map file (with and without the trailing slash.)
But is there some way to instruct nginx to ignore the trailing slash when processing the rewrite? It should work the other way too, ie. if the map file says /foo and the request says /foo/ it should match.
The problem is with the initial match in the map file. You cannot fix this problem at the rewrite statement.
The simplest solution would be to use a regular expression in the map directive's include file:
~^/foo/? /bar;
However, a less elegant solution would be to use two map directives, both including the same file of mappings:
map $uri $without {
include /path/to/file;
}
map $uri/ $with {
include /path/to/file;
}
server {
...
if ($with) { return 301 $with; }
if ($without) { return 301 $without; }
...
}
But the include file will need to specify a trailing / in order to match both cases.
See this document for details.
when access url with query string, I want to rewrite to no query string url, like:
/blog/(\d+)/(w+)?a=1$b=2....
rewrite to
/blog/(\d+)/(w+)
If you would like to rewrite all requests a query string, just simply add these to your server block.
if ($query_string != "") {
rewrite ^(.*)$ $uri? last;
}
Explain
The syntax of rewrite directive is
rewrite regex replacement [flag];
First the if statement will match all requests with query strings and rewrite them with the replacement $uri?
According to the documentation, the query strings are dropped because
If a replacement string includes the new request arguments, the previous request arguments are appended after them. If this is undesired, putting a question mark at the end of a replacement string avoids having them appended.
Finally the last flag tells nginx to
stop processing the current set of ngx_http_rewrite_module directives and starts a search for a new location matching the changed URI;
I have a site with two RESTful URL schemes:
foo.com/model/id and
foo.com/model/id/action
The actual pages served by these URLs are in the form of
$model.php?id=$id and
$model_$action.php?id=$id respectively.
I have a single regular expression that will match both cases ^(\w+)s/([A-z0-9]+)(?:/(\w+))?/?$ and I'd like to use a single Nginx rule to rewrite for both types of URLs, but I'm not sure how to do this. In pseudocode I imagine the rule would look something like this
rewrite ^(\w+)s/([A-z0-9]+)(?:/(\w+))?/?$ /(($3) ? $1_$3.php?$id=$2 : $1.php?$id=2)
This isn't valid syntax (so far as I know), but can something like this be done in Nginx?
Rewrite the possible urls in turn starting with the longest to the shortest to that overlapping matching strings, "/model/id" in this case, would be matched in the longer url string first.
location / {
# For "/model/id/action" to "$model_$action.php?id=$id"
rewrite ^/(.+)/(.+)/(.+)(/?)$ $1_$3.php?id=$2 last;
# For "/model/id" to "$model.php?id=$id"
rewrite ^/(.+)/(.+)(/?)$ $1.php?id=$2 last;
}
location ~ .+\.php$ {
# Handle PHP
}
The "(/?)" is just in case the urls sometimes come with an ending slash. If they never do, it can be removed. In this case, it will probably be best to specifically always add a closing slash and have "(/?)" as "/".