I'd like to route all requests sent to my nginx server to my backend application by default, but selectively send API requests with GET HTTP verbs to a OpenResty Lua based REST API backed by a content_by_lua
nginx directive.
I'm successfully able to route all API requests to the Lua API based on their URL prefix using the following configuration (note that this does not take into account the HTTP Verb):
http {
upstream backend {
server localhost:8080;
}
server {
listen 80;
location / {
# Send all requests to the backend application
proxy_pass http://backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_set_header HTTP_CLIENT_IP $remote_addr;
proxy_redirect off;
}
location /api {
# Send any URL with the /api prefix to the nginx Lua API application
content_by_lua '
require("lapis").serve("app")
';
}
}
}
But, as I stated above, I'd like to further restrict API requests such that any requests with HTTP Verbs other than GET (like POST, PUT, DELETE, etc) are still routed to the backend, while the GET requests alone are routed to the Lua API location.
Based on some other posts, blogs, and documentation (and hearing that the if
directive is frowned upon), I tried using a limit_except
directive, but then the nginx server crashed upon startup as it seems the content_by_lua
directive was not designed for limit_except
blocks. Here was my attempt:
http {
upstream backend {
server localhost:8080;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_set_header HTTP_CLIENT_IP $remote_addr;
proxy_redirect off;
}
location /api {
# Default the non-get API requests back to the backend server
proxy_pass http://backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_set_header HTTP_CLIENT_IP $remote_addr;
proxy_redirect off;
# Select requests that *aren't* a PUT, POST, or DELETE, and pass those to the Lapis REST API
limit_except PUT POST DELETE {
content_by_lua '
require("lapis").serve("app")
';
}
}
}
}
which promptly crashed with
nginx: [emerg] "content_by_lua" directive is not allowed here in nginx.conf:46
What is the best way to selectively route in nginx based on both URL prefix and HTTP verb when delegating to the content_by_lua
directive?
I implemented conditional routing of particular URL GET actions by using the
if
directive, even though it's evil according to the nginx devs. It does seem like this might one of the few desirable use cases for theif
directive, but if it's not or someone has a better approach, please let me know (which is why I haven't accepted my own answer).Here is the nginx configuration file that currently implements the solution: