When working with Nginx configuration, developers often need to route requests based on query parameters. A common use case is handling Git HTTP requests like:
GET /git/sample-repository/info/refs?service=git-receive-pack HTTP/1.1
Standard location blocks only match against the URI path, not the query string. This limitation requires specific techniques for query parameter-based routing.
Nginx provides the $args
and $query_string
variables for accessing query parameters. While you can't directly match query strings in location blocks, you can use conditional logic:
location /git/ {
if ($args ~* "service=git-receive-pack") {
# Handle Git receive pack requests
auth_basic "Git Receive Pack";
auth_basic_user_file /etc/nginx/git.htpasswd;
}
if ($args ~* "service=git-upload-pack") {
# Handle Git upload pack requests
auth_basic "Git Upload Pack";
auth_basic_user_file /etc/nginx/git.htpasswd;
}
}
For complex routing scenarios, the map
directive offers more maintainable query string handling:
map $args $git_service {
default "";
"~*service=git-receive-pack" "receive";
"~*service=git-upload-pack" "upload";
}
server {
location /git/ {
if ($git_service = "receive") {
# Receive pack configuration
}
if ($git_service = "upload") {
# Upload pack configuration
}
}
}
Here's a complete example for handling Git smart HTTP protocol requests:
server {
listen 80;
server_name git.example.com;
location ~ ^/git/.*/info/refs$ {
if ($args ~* "service=git-upload-pack") {
# Read-only access
auth_basic "Git Read Access";
auth_basic_user_file /etc/nginx/git-read.htpasswd;
include fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
if ($args ~* "service=git-receive-pack") {
# Write access
auth_basic "Git Write Access";
auth_basic_user_file /etc/nginx/git-write.htpasswd;
include fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
}
location ~ ^/git/.*/git-receive-pack$ {
auth_basic "Git Write Access";
auth_basic_user_file /etc/nginx/git-write.htpasswd;
include fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
}
When implementing query string matching:
- Avoid excessive
if
blocks as they're not nginx's preferred conditional - Consider using
map
for complex matching patterns - Regular expressions in query string matching can impact performance
- Cache static resources before they reach query string matching logic
For scenarios requiring more sophisticated query parameter handling:
- Use Lua scripting with OpenResty for advanced routing logic
- Consider API gateway solutions like Kong when query parameters drive complex routing
- Implement a small proxy service for cases requiring extensive query string processing
Nginx location blocks primarily match against the URI part of a request, not including the query string (everything after the ?
). This is a common source of confusion for developers working with REST APIs or dynamic content.
Consider this Git HTTP request:
GET /git/sample-repository/info/refs?service=git-receive-pack HTTP/1.1
A standard location block like:
location /git/ {
# Configuration
}
will match the URI, but won't consider the service=git-receive-pack
query parameter.
1. Using the $args Variable
Nginx provides the $args
variable containing the full query string:
location /git/ {
if ($args ~* "service=git-receive-pack") {
# Specific configuration for git-receive-pack
}
# Default configuration
}
2. Separate Location Blocks for Different Services
For Git's smart HTTP protocol:
location ~ ^/git/.+/info/refs$ {
if ($arg_service = "git-upload-pack") {
# Pull operations
proxy_pass http://git-upload-pack-backend;
}
if ($arg_service = "git-receive-pack") {
# Push operations
proxy_pass http://git-receive-pack-backend;
}
}
3. Using map Directive for Complex Matching
For more complex scenarios:
map $args $git_service {
default "";
"~service=git-upload-pack" "upload";
"~service=git-receive-pack" "receive";
}
server {
location /git/ {
if ($git_service = "upload") {
# Upload pack configuration
}
if ($git_service = "receive") {
# Receive pack configuration
}
}
}
When working with query strings in Nginx:
- Avoid excessive use of
if
directives as they have performance implications - Remember that
$args
contains the raw query string while$query_string
is an alias - For security-sensitive operations, always validate and sanitize query parameters
Here's how you might handle API versioning via query parameters:
location /api/ {
if ($arg_v = "1.0") {
proxy_pass http://api-v1;
}
if ($arg_v = "2.0") {
proxy_pass http://api-v2;
}
# Default to latest version
proxy_pass http://api-v3;
}