Peak
Peak

Reputation: 371

How i can translate uppercase to lowercase letters in a rewrite rule in nginx web server?

I need to translate the address:

www.example.com/TEST in ---> www.example.com/test

Upvotes: 37

Views: 39141

Answers (9)

The nothing
The nothing

Reputation: 338

Another way is with module pure c ngx_http_lower_upper_case because load perl is resource usage high:

location ~ [A-Z] {
 lower $uri_lowercase "$request_uri";
 rewrite ^(.*)$ $scheme://$host$uri_lowercase;
}

Upvotes: 1

Jonathan DeMarks
Jonathan DeMarks

Reputation: 2441

I've seen this code all over the internet, and it does work. However, it uses the host name that the nginx server receives. In the case of a Kubernetes infrastructure, this may not be the same as what the client uses so the redirect will not only fail but give away private information about the cluster naming scheme.

This code works for me and is slightly faster because it uses internal redirection

sub {
    my $r = shift;
    my $uri = $r->uri;
    $uri =~ s/\R//; # replace all newline characters
    $uri = lc($uri);
    $r -> internal_redirect($uri)
}

It might be used in an nginx configuration file like this:

user        nginx; # Not important for this answer, use any user you want
                   # but nginx is a sensible default.

# Load the Perl module
load_module /usr/lib/nginx/modules/ngx_http_perl_module.so;

...

http {
    server {
        # Given any location that contains an upper-case letter
        location ~ [A-Z] {
            # Lowercase it and redirect internally
            perl 'sub {
                my $r = shift;
                my $uri = $r->uri;
                $uri =~ s/\R//; # replace all newline characters
                $uri = lc($uri);
                $r -> internal_redirect($uri)
            }';
        }

        # This can be any set of rules, below is a simple one that hosts
        # static content. It will receive the lower-cased location and
        # serve it.
        location / {
            root  /usr/share/nginx/html;
            index index.html;
        }
    }
}

My Dockerfile starts with this line and doesn't require additional packages:

FROM nginx:1.23.4-perl

In this example you will want to copy your static site to /usr/share/nginx/html for example:

COPY nginx.conf /etc/nginx/nginx.conf
COPY src/ /usr/share/nginx/html

Upvotes: 0

Thiago Ganzarolli
Thiago Ganzarolli

Reputation: 1171

Yes, you are going to need perl.

If you are using Ubuntu, instead of apt-get install nginx-full, use apt-get install nginx-extras, which will have the embedded perl module.

Then, in your configuration file:

  http {
  ...
    # Include the perl module
    perl_modules perl/lib;
    ...
    # Define this function
    perl_set $uri_lowercase 'sub {
      my $r = shift;
      my $uri = $r->uri;
      $uri = lc($uri);
      return $uri;
    }';
    ...
    server {
    ...
      # As your first location entry, tell nginx to rewrite your uri,
      # if the path contains uppercase characters
      location ~ [A-Z] {
        rewrite ^(.*)$ $scheme://$host$uri_lowercase;
      }
    ...

Upvotes: 23

Degibons
Degibons

Reputation: 123

Redirect with LUA module.

load_module /usr/lib/nginx/modules/ndk_http_module.so;
load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;

set_by_lua $uri_lowercase "return string.lower(ngx.var.uri)";
location ~[A-Z] {
  return 301 $scheme://$http_host$uri_lowercase$is_args$args;
}

Upvotes: 0

mca
mca

Reputation: 66

I would like to point out that most of Perl answers are vulnerable to CRLF injection.

You should never use nginx's $uri variable in a HTTP redirection. $uri variable is subject to normalization (more info), including:

  • URL encoded characters are decoded
  • Removal of the ? and query string
  • Consecutive / characters are replace by a single /

URL decoding is the reason of CRLF injection vulnerability. The following example url would add a malicious header into your redirect, if you used $uri variable in the redirection.

https://example.org/%0ASet-Cookie:MaliciousHeader:Injected

%0A is decoded to \n\r and nginx will add into headers the following lines:

Location: https://example.org
set-cookie: maliciousheader:injected

The secure Perl redirection requires to replace all newline characters.

perl_set $uri_lowercase 'sub {
    my $r = shift;
    my $uri = $r->uri;
    $uri =~ s/\R//; # replace all newline characters
    $uri = lc($uri);
    return $uri;
}';

Upvotes: 1

Timon de Groot
Timon de Groot

Reputation: 8183

Based on Adam's answer, I ended up using lua, as it's available on my server.

set_by_lua $request_uri_low "return ngx.arg[1]:lower()" $request_uri;
if ($request_uri_low != $request_uri) {
   set $redirect_to_lower 1;
}
if (!-f $request_uri) {
    set $redirect_to_lower "${redirect_to_lower}1";
}
if ($redirect_to_lower = 11) {
    rewrite . https://$host$request_uri_low permanent;
}

Upvotes: 4

Adam Dobrawy
Adam Dobrawy

Reputation: 1215

location /dupa/ {
    set_by_lua $request_uri_low "return ngx.arg[1]:lower()" $request_uri;
    rewrite ^ https://$host$request_uri_low;
}

Upvotes: 10

Adrian Hernandez-Lopez
Adrian Hernandez-Lopez

Reputation: 553

location ~*^/test/ {
  return 301 http://www.example.com/test;
}

A location can either be defined by a prefix string, or by a regular expression. Regular expressions are specified with the preceding “~*” modifier (for case-insensitive matching), or the “~” modifier (for case-sensitive matching).

Soruce: http://nginx.org/en/docs/http/ngx_http_core_module.html#location

Upvotes: 3

Dennis Krupenik
Dennis Krupenik

Reputation: 676

i managed to achieve the goal using embedded perl:

location ~ [A-Z] {
  perl 'sub { my $r = shift; $r->internal_redirect(lc($r->uri)); }';
}

Upvotes: 9

Related Questions