61

Right now I have this config:

location ~ ^/phpmyadmin/(.*)$
{
        alias /home/phpmyadmin/$1;
}

However, if I visit www.mysite.com/phpmyadmin (note the lack of trailing slash), it won't find what I'm looking for a 404. I assume because I don't include the trailing slash. How can I fix this?

1
  • 2
    I know this question is quite old, nonetheless I'd like to emphasize on the need to change the accepted answer. Unexperienced users (the ones who'll search for this kind of QAs) might create security holes by using the currently accepted answer. – Bozzy Mar 5 '19 at 11:58

10 Answers 10

40

It might be in the regular expression that you're using --

location ~ ^/phpmyadmin/(.*)$

The above will match /phpmyadmin/, /phpmyadmin/anything/else/here, but it won't match /phpmyadmin because the regular expression includes the trailing slash.

You probably want something like this:

location ~ /phpmyadmin/?(.*)$ {
    alias /home/phpmyadmin/$1;
}

The question mark is a regular expression quantifier and should tell nginx to match zero or one of the previous character (the slash).

Warning: The community seen this solution, as is, as a possible security risk

4
  • Thank you! And thank you for the explanation, as well. Will come in handy in the future. – Rob Apr 3 '12 at 6:08
  • 17
    The pattern isn't quite correct. It can also match path like "/phpmyadmin1234", obviously that's no what you want. @kbec's solution is the right one. – Meow Aug 20 '15 at 12:10
  • 3
    I'm sorry for explicitly downvoting this, but @Meow comment is way to important to neglect. This can lead to a misconfigured webserver which could contain security holes. – Daniel F Jan 25 '19 at 20:50
  • does not work for proxy_pass – gdm Jun 25 '20 at 10:39
48

The better solution:

location ~ ^/phpmyadmin(?:/(.*))?$ {
    alias /home/phpmyadmin/$1;
}

Ensure that server has permissions to /home/phpmyadmin first.


Explanation of difference with accepted answer:

It's all about regular expressions.

First of all, the ^ char means that you want to match from beginning of string and not somewhere in the middle. The $ at the end means matching to the end of the string.

The (?:) means non-capturing group - we don't want it in the capturing results, but we want to simple group some chars. We group it like this, because we want the / char to be a nonsignificant part of the child path, and not a significant part of the parent path.

5
11

Why wouldn't you just use

location /phpmyadmin {
    alias /home/phpmyadmin;
}

?

4
  • Because that wasn't working for me. See here: serverfault.com/questions/375602/… – Rob Apr 3 '12 at 5:35
  • 1
    @Rob Did you try it without the trailing slash on the alias directive? – Shane Madden Apr 3 '12 at 17:04
  • This is actually the correct answer and correct way of doing it. Simply exclude the trailing slash and it works for urls typed both with and without the trailing slash. No need for complicating anything on Nginx keep it simple, complicated regex and other funny things will only slow Nginx down. – MitchellK Jun 30 '18 at 8:52
  • 5
    The problem with this is that it will also match on /phpmyadminblahblahblahblah or any other combination – Gary Green Jun 29 '19 at 15:04
7

I know this is an old question, but for anybody that ends up here via Google, I solved it the following way (variation of @kbec's one, which was quite good):

location ~ ^/foo(/.*)?$ {
  proxy_pass http://$backend$1$is_args$args;
}

This will capture any variation of /foo and redirect it to /bar on another url (including parameters). I am showing it with a proxy_pass directive but it would also work with alias.

  • /foo -> /bar
  • /foo/ -> /bar/
  • /foo/sub -> /bar/sub
  • /foo/sub1/sub2/?param=value -> /bar/sub1/sub2/?param=value

It works because $1 will optionally capture the subresources plus the leading slash, so it won't capture things like /fooextra/. It will also redirect a present or non-present ending slash properly.

EDIT: Check comments for caveats about this approach.

5
  • 2
    Thanks for considering the corner case of wanting to use proxy_pass and thus caring about the arguments. You have my upvote. – Daniel F Jan 25 '19 at 21:26
  • The problem I see with this answer is that if you request /foo, you wont get an uri appended to the backend, which means you fall in the specific case documented as follow in nginx : "If the address is specified without a URI, or it is not possible to determine the part of URI to be replaced, the full request URI is passed (possibly, modified)." in other word you will pass /foo to the backend. (source: docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy). @kbec answer is better at this. – martin-mystere Jan 24 at 9:08
  • @martin-mystere iirc that only applies when using a prefix string on the location part, but not when using a regex nginx.org/en/docs/http/ngx_http_core_module.html#location – dlouzan Mar 12 at 15:42
  • Hi @dlouzan, I'm afraid that after testing, my original guess has verified. I spined up two nginx containers, one to emulate the frontend with the configuration you shared (btw it misses a final ; on the proxy_pass line) and the other one as upstream. With request /foo/ on the frontend, upstream get a request to /. With /foo it gets a request to /foo – martin-mystere Mar 15 at 9:09
  • @martin-mystere Thanks for the hint! I learned something :-) – dlouzan Mar 16 at 10:09
2

This redirect will only rewrite URLs with and without the trailing slash. Anything that comes after the slash won't be overwritten.

domain.com/location => redirected to domain.com/new/location
domain.com/location => redirected to domain.com/new/location
domain.com/location/other => not redirected

server {
  # 301 URL Redirect
  rewrite ^/location/?$ /new/location permanent;
}
1

Have you tried using try_files directive?

try_files $uri $uri/ =404;
0

I did it like that

rewrite ^/promo/?$     http://example.com/promo/page43902.html;
location /promo/ {
    root /var/www;
}
0

Why not put two rewrites simply:

location ~ ^/phpmyadmin
{
    rewrite /phpmyadmin /home/phpmyadmin/ break;
    rewrite /phpmyadmin/(.*) /home/phpmyadmin/$1 break;
}
0

If you need also proxy_pass it is better to pass arguments too:

location ~/phpmyadmin/?(.*)$ {                                                                                           
         proxy_pass http://server/$1$is_args$args;

That said, my personal suggestions is to NEVER use phpmyadmin in production. You will understand why by checking the access logs of your public web-server... (lot of bots trying to exploit phpmyadmin)

0

When using proxy_pass with a location given by a prefix string rather than a regular expression Nginx will take care of the trailing slash for you, as described in the docs:

If a location is defined by a prefix string that ends with the slash character, and requests are processed by one of proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass, or grpc_pass, then the special processing is performed. In response to a request with URI equal to this string, but without the trailing slash, a permanent redirect with the code 301 will be returned to the requested URI with the slash appended. If this is not desired, an exact match of the URI and location could be defined like this:

location /user/ {
   proxy_pass http://user.example.com;
}

location = /user {
    proxy_pass http://login.example.com;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.