49

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
11

3 Answers 3

64

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.

16
  • 1
    Interesting approach! May I recommend $args ~ "service=git-send-pack" instead of $args = "service=git-send-pack"? This form accommodates multiple query parameters. Commented Oct 28, 2016 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. Commented Oct 28, 2016 at 21:57
  • 1
    By the way, it should be $args ~ and $arg_somerandomfield =. Commented Oct 28, 2016 at 22:02
  • 2
    One can also use nginx map feature for this purpose, which is faster. Commented Oct 29, 2016 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.
    Commented Sep 11, 2017 at 16:26
11

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;
    # ...

}
2
  • 3
    As NGINX uses PCRE library, I would suggest to use \w instead of [A-Za-z0-9_] to diminish verbosity.
    – Stphane
    Commented Nov 29, 2020 at 22:41
  • 1
    In my example, I've explicitly excluded underscores, so \w wouldn't work in my case. I upvoted the comment anyway, and I agree that this would be better in general. Commented Sep 10, 2023 at 20:09
-1

There is another way this can be done, if you are using nginx as a proxy.

set a variable in the server block:

set $nocache="0";

Inside the location block, add the if:

if ( $arg_<query string to match> = "<query string value>") { set $nocache "1"; }

And add two new proxy directives:

proxy_cache_bypass $nocache ;
proxy_no_cache $nocache ;

it will always forward to the upstream server with no cache

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .