nginx rewrite map file, with or without a trailing slash - nginx

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.

Related

Nginx remove trailing slash but not if query string is present

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;
}

Nginx location group capture + rewrite

I'm trying to do a group capture in a Nginx location block and it's not working for me.
Is what I am trying to do even possible?
location ~* /(?<cat>cars|trucks|bikes|motorcycle|quads) {
rewrite ^/$cat/([0-9]+)(.*)$ /page.php?id=$1 last;
}
The error message I am receiving is :
"^/$cat/([0-9]+)(.*)$" does not match "/cars/120/new-car-rentals/"
I have a lot more categories than what I am posting, and trying to prevent writing a rewrite 5x for each specific category name.
Any help would be appreciated.
I'm not familiar with this particular syntax, but based on my experience with others, is it possible that you simply need to escape the forward slashes you're using?
location ~* \/(?<cat>cars|trucks|bikes|motorcycle|quads) {
rewrite ^\/$cat\/([0-9]+)(.*)$ /page.php?id=$1 last;
}
Note the named capture in the location regex: if you want to use a value captured here, you must use the named syntax (?<name>), numbers do not work.
I solved the issue by doing this instead :
location ~* /(cars|trucks|bikes|motorcycle|quad-bikes) {
rewrite ^/([a-zA-Z-]+)/([0-9]+)(.*)$ /page.php?id=$2 last;
...
...
}
The regex ([a-zA-Z-]+) allows me to use characters a-z (case insensitive) with possible dashes in my category / page names.

Nginx rewrite to css factory

I have a rule which is not working correctly.
I need it so that whenever URL xxx.com/forum/css.php is hit, it is re-written to xxx.com/forum/core/css.php.
I have written the following location block for it:
location ~^ /forum/css.php {
rewrite ^ /forum/core/css.php permanent;
}
Also needing to be taken into account is that the file is a factory so it accepts parameters, the url being hit actually looks like xxx.com/forum/css.php?x=123&y=string. Will this also be taken into account in the re-writes or does it need to be specified? Sorry if the question seems silly I am just beginning to work with servers! Thanks fellow coders!
To rewrite a single URI (with or without query string) you could use a location =:
location = /forum/css.php {
rewrite ^ /forum/core/css.php permanent;
}
The rewrite directive appends the query string (unless terminated with ?). See this and this for more.

Nginx Rewrite Issue to External link

I'm trying to set up a permanent redirect from
http://domain.com/member/blog_post_view.php?postId=1
to
http://blog.domain.com/friendly-url-here
The source URL contains both a ? and an = which I think might be the cause but am unsure.
I've tried all sorts of nginx suggestiosn including the one below but can't seem to get the redirection to work and hoped someone can point me in the right direction.
location /blog_post_view.php?postId=1 {
rewrite "/blog_post_view.php\?postId\=1" http://blog.domain.com/friendly-url-here permanent;
}
That part of request line starting from the question mark is called query string, while the location directive matches only path part of URI.
You should use $arg_* variable instead:
location =/blog_post_view.php {
if ($arg_postId = 1) {
return 301 http://blog.domain.com/friendly-url-here;
}
}
Reference:
http://nginx.org/r/location
http://nginx.org/r/if
Embedded Variables

Nginx rewrite with conditional destination

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 "/".

Resources