70

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?

Vladimir
  • 103
  • 3
Rob
  • 2,303
  • 9
  • 31
  • 50
  • 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 05 '19 at 11:58

11 Answers11

62

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.

kbec
  • 913
  • 1
  • 9
  • 10
46

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

yagmoth555
  • 16,300
  • 4
  • 26
  • 48
haymaker
  • 1,242
  • 9
  • 9
  • Thank you! And thank you for the explanation, as well. Will come in handy in the future. – Rob Apr 03 '12 at 06:08
  • 20
    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
  • 4
    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
12

Why wouldn't you just use

location /phpmyadmin {
    alias /home/phpmyadmin;
}

?

womble
  • 95,029
  • 29
  • 173
  • 228
  • Because that wasn't working for me. See here: http://serverfault.com/questions/375602/why-is-my-nginx-alias-not-working – Rob Apr 03 '12 at 05:35
  • 1
    @Rob Did you try it without the trailing slash on the `alias` directive? – Shane Madden Apr 03 '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 08:52
  • 8
    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.

dlouzan
  • 171
  • 1
  • 3
  • 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: https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/). @kbec answer is better at this. – martin-mystere Jan 24 '21 at 09:08
  • @martin-mystere iirc that only applies when using a prefix string on the location part, but not when using a regex http://nginx.org/en/docs/http/ngx_http_core_module.html#location – dlouzan Mar 12 '21 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 '21 at 09:09
  • @martin-mystere Thanks for the hint! I learned something :-) – dlouzan Mar 16 '21 at 10:09
3

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;
}
Oriol
  • 361
  • 2
  • 3
2

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

Have you tried using try_files directive?

try_files $uri $uri/ =404;
Prasanth
  • 119
  • 3
1

Why not put two rewrites simply:

location ~ ^/phpmyadmin
{
    rewrite /phpmyadmin /home/phpmyadmin/ break;
    rewrite /phpmyadmin/(.*) /home/phpmyadmin/$1 break;
}
千木郷
  • 251
  • 2
  • 6
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)

gdm
  • 419
  • 2
  • 5
  • 15
0

I have added another stackoverflow answer here.

It is summarized here:

  • My setup has a couple of apps sitting behind nginx. I want to redirect /appx-uri/bla/blo/bli to /appx-uri/bla/blo/bli/ instead of /bla/blo/bli/ which is the default nginx behavior.
  • Also, all my static files (for an app) are gathered for nginx to serve /repairapp/static/.

Adding the directive rewrite ^/repairapp/([^static].*[^/])$ /repairapp/$1/ permanent; in the server block fixes it.

I then have

  • To fix the issue: rewrite ^/repairapp/([^static].*[^/])$ /repairapp/$1/ permanent;
  • To have nginx serve resources location /repairapp/static/ {
  • To access an app location /repairapp/ {
0

I did it like that

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