15

Can nginx location blocks match a URL query string?

For example, what location block might match HTTP GET request

GET /git/sample-repository/info/refs?service=git-receive-pack HTTP/1.1
26

Can nginx location blocks match a URL query string?

Short answer: No.

Long answer: There is a workaround if we have only a handful of such location blocks.

Here's a sample workaround for 3 location blocks that need to match specific query strings:

server {
  #... common definitions such as server, root

  location / {
    error_page 418 = @queryone;
    error_page 419 = @querytwo;
    error_page 420 = @querythree;

    if ( $query_string = "service=git-receive-pack" ) { return 418; }
    if ( $args ~ "service=git-upload-pack" ) { return 419; }
    if ( $arg_somerandomfield = "somerandomvaluetomatch" ) { return 420; }

    # do the remaining stuff
    # ex: try_files $uri =404;

  }

  location @queryone {
    # do stuff when queryone matches
  }

  location @querytwo {
    # do stuff when querytwo matches
  }

  location @querythree {
    # do stuff when querythree matches
  }
}

You may use $query_string, $args or $arg_fieldname. All will do the job. You may know more about error_page in the official docs.

Warning: Please be sure not to use the standard HTTP codes.

  • 1
    Interesting approach! May I recommend $args ~ "service=git-send-pack" instead of $args = "service=git-send-pack"? This form accommodates multiple query parameters. – Derek Mahar Oct 28 '16 at 21:52
  • 1
    stackoverflow.com/a/40313590/107158 illustrates the approach that I followed to handle query string arguments. Like your answer, mine uses if and $arg_fieldname, but uses rewrite instead of error_page and location @name. Note that in that example, my attempts at using @name for the replacement parameter in rewrite were unsuccessful. – Derek Mahar Oct 28 '16 at 21:57
  • 1
    By the way, it should be $args ~ and $arg_somerandomfield =. – Derek Mahar Oct 28 '16 at 22:02
  • 1
    One can also use nginx map feature for this purpose, which is faster. – Tero Kilkanen Oct 29 '16 at 10:24
  • 1
    @PothiKalimuthu, thanks for clarifying this. What I have done in the meanwhile is to replace the query parameter by a url path one like this feedback/{auth_key} instead of /feedback?auth_key=abc. This way I don't need to use if, I can define location pattern using regex and that's it. – W.M. Sep 11 '17 at 16:26
4

I know this question is over a year old, but I've spent the last few days destroying my brain over a similar problem. I wanted different authentication and handling rules for public and private repos, including pushing and pulling. This is what I finally came up with, so I figured I'd share. I know if is a tricky directive, but this seems to work for me just fine:

# pattern for all repos, public or private, followed by username and reponame
location ~ ^(?:\/(private))?\/([A-Za-z0-9]+)\/([A-Za-z0-9]+)\.git(\/.*)?$ {

    # if this is a pull request
    if ( $arg_service = "git-upload-pack" ) {

        # rewrite url with a prefix
        rewrite ^ /upload$uri;

    }

    # if this is a push request
    if ( $arg_service = "git-receive-pack" ) {

        # rewrite url with a prefix
        rewrite ^ /receive$uri;

    }

}

# for pulling public repos
location ~ ^\/upload(\/([A-Za-z0-9]+)\/([A-Za-z0-9]+)\.git(\/.*)?)$ {

    # auth_basic "git";
    # ^ if you want

    # ...
    # fastcgi_pass unix:/var/run/fcgiwrap.socket;
    # ...

}

# for pushing public repos
location ~ ^\/receive(\/([A-Za-z0-9]+)\/([A-Za-z0-9]+)\.git(\/.*)?)$ {

    # auth_basic "git";
    # ^ if you want

    # ...
    # fastcgi_pass unix:/var/run/fcgiwrap.socket;
    # ...

}

# for pulling private repos
location ~ ^\/upload\/private(\/([A-Za-z0-9]+)\/([A-Za-z0-9]+)\.git(\/.*)?)$ {

    # auth_basic "git";
    # ^ if you want

    # ...
    # fastcgi_pass unix:/var/run/fcgiwrap.socket;
    # ...

}

# for pushing private repos
location ~ ^\/receive\/private(\/([A-Za-z0-9]+)\/([A-Za-z0-9]+)\.git(\/.*)?)$ {

    # auth_basic "git";
    # ^ if you want

    # ...
    # fastcgi_pass unix:/var/run/fcgiwrap.socket;
    # ...

}

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.