When building RESTful services, we often need to route requests to different backend processors based on HTTP methods. A common scenario involves:
- GET requests going to a caching layer or read-only API
- POST requests routed to write-processing services
- Other methods (PUT/DELETE) handled by specific microservices
Here's how to implement method-based routing in Nginx:
server {
listen 80;
server_name api.example.com;
location /resources {
# Handle GET requests
if ($request_method = GET) {
proxy_pass http://read_backend;
break;
}
# Handle POST requests
if ($request_method = POST) {
proxy_pass http://write_backend;
break;
}
# Default fallback
proxy_pass http://default_backend;
}
}
For more complex routing logic, use the map directive:
map $request_method $backend {
default http://default_backend;
GET http://read_backend;
POST http://write_backend;
PUT http://update_backend;
DELETE http://delete_backend;
}
server {
location /api {
proxy_pass $backend;
}
}
When implementing method-based routing:
- Nginx evaluates if conditions sequentially - place most frequent methods first
- Map directives are evaluated during configuration load - better for performance
- Consider connection pooling between Nginx and backends
Combining proxy and FastCGI routing:
location /data {
if ($request_method = GET) {
proxy_pass http://cache_server;
}
if ($request_method = POST) {
fastcgi_pass unix:/var/run/php-fpm.sock;
include fastcgi_params;
}
# Return 405 for unsupported methods
return 405;
}
Always include proper error responses:
location /api {
# ... method routing logic ...
# Method not allowed
error_page 405 = @method_not_allowed;
location @method_not_allowed {
add_header Allow "GET, POST";
return 405 '{"error": "Method not allowed"}';
}
}
Use curl to verify routing:
# Test GET routing
curl -X GET http://api.example.com/resources
# Test POST routing
curl -X POST http://api.example.com/resources -d '{"data":"value"}'
In RESTful API design, it's common practice to use the same URI with different HTTP methods for different operations. A typical scenario is using:
- GET /resource - for retrieving data
- POST /resource - for creating data
However, these operations might need to be handled by different backend services due to technical requirements (like using HTTP proxy for GETs and FastCGI for POSTs). Nginx provides elegant solutions for this routing requirement.
Here's the simplest way to handle different methods in Nginx:
location /api/resource {
if ($request_method = GET) {
proxy_pass http://get_backend;
}
if ($request_method = POST) {
proxy_pass http://post_backend;
}
# Handle other methods
if ($request_method !~ ^(GET|POST)$ ) {
return 405;
}
}
For more complex scenarios with multiple methods, use the map directive:
map $request_method $backend {
default http://default_backend;
GET http://get_backend;
POST http://post_backend;
PUT http://put_backend;
DELETE http://delete_backend;
}
server {
location /api/ {
proxy_pass $backend;
}
}
When you need to mix proxy and FastCGI based on methods:
location /api/resource {
if ($request_method = GET) {
proxy_pass http://backend_server;
break;
}
if ($request_method = POST) {
fastcgi_pass unix:/var/run/php-fpm.sock;
include fastcgi_params;
break;
}
}
Always include proper error handling:
location /api/resource {
limit_except GET POST {
deny all;
}
if ($request_method = GET) {
proxy_pass http://get_backend;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback;
}
if ($request_method = POST) {
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_intercept_errors on;
error_page 502 503 504 = @fallback;
}
}
location @fallback {
proxy_pass http://fallback_server;
}
When implementing method-based routing:
- Use
map
directives for better performance with many rules - Minimize
if
blocks when possible - they have performance overhead - Consider using separate
location
blocks withlimit_except
when appropriate
Here's a complete example handling a RESTful user resource:
map $request_method $user_backend {
default http://default_api;
GET http://user_service:8001;
POST http://auth_service:8002;
PUT http://user_service:8001;
DELETE http://admin_service:8003;
}
server {
listen 80;
location /users/ {
limit_except GET POST PUT DELETE {
deny all;
}
proxy_pass $user_backend;
proxy_set_header X-Real-IP $remote_addr;
# Common proxy settings
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}