Nginx rewrite based on header value - nginx

I have a nginx.conf which basically looks like (unnecessary parts omitted):
upstream app {
server unix:/tmp/unicorn.myapp.sock fail_timeout=0;
}
server {
listen 80;
location #app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app;
}
}
I want to configure nginx so that the value of a specific header is used to rewrite the url being passed to the upstream.
For example, let's assume that I have a request to /test with the header Accept: application/vnd.demo.v1+json. I'd like it to be redirected to the upstream URL /v1/test, i.e. basically the upstream app will receive the request /v1/test without the header.
Similarly, the request to /test and the header Accept: application/vnd.demo.v2+json should be redirected to the upstream URL /v2/test.
Is this feasible? I've looked into the IfIsEvil nginx module, but the many warnings in there made me hesitant to use it.
Thanks,
r.
edit
In case there's no match, I'd like to return a 412 Precondition Failed immediately from nginx.

If Accept header does not contain required header return error.
map $http_accept $version {
default "";
"~application/vnd\.demo\.v2\+json" "v2";
"~application/vnd\.demo\.v1\+json" "v1";
}
server {
location #app {
if ($version = "") {
return 412;
}
...;
proxy_pass http://app/$version$uri$is_args$args;
}
}

Related

nginx upstream proxy_pass not working for heroku?

The below nginx config is working fine if I hardcode my herokuapp(backend API) in proxy_pass section:
http {
server {
listen 8080;
location / {
proxy_pass http://my-app.herokuapp.com;
}
}
}
events { }
However if I try to add this in the upstream directive, its going to 404 page. I want to add this in upstream directive because I have other herokuapps as well where I want to load balance my requests.
This is the config which is not working:
http {
upstream backend {
server my-app.herokuapp.com;
}
server {
listen 8080;
location / {
proxy_pass http://backend;
}
}
}
events { }
These are all the things I tried after checking other SO answers:
add Host header while proxy passing. proxy_set_header Host $host;
add an extra slash at the end of backend.
In upstream directive, add server my-app.herokuapp.com:80 instead of just server my-app.herokuapp.com
In upstream directive, add server my-app.herokuapp.com:443 instead of just server my-app.herokuapp.com. This gives timeout probably because heroku doesn't allow 443(or maybe I didn't configure it).
Found the Issue: I was adding the wrong host. For heroku, for some reason you need to add host header with value as exactly what your app name is.
If your herokuapp name is my-app.herokuapp.com, then you need to add this line for sure:
proxy_set_header Host my-app.herokuapp.com;
Full working config below:
http {
upstream backend {
server my-app.herokuapp.com;
}
server {
listen 8080;
location / {
proxy_pass http://backend;
proxy_set_header Host my-app.herokuapp.com;
}
}
}
events { }

Nginx Proxy Pass not passing port

I'm working locally for the moment.
I have an NGINX configuration for nuxtwoo.example.com.
Whenver I visit nuxtwoo.example.com, I need it to proxy localhost:3000, which is working fine, however I also need it to pass the port :300.
location / {
proxy_pass http://localhost:3000;
}
What I need,
http://nuxtwoo.example.com -> proxy_pass : localhost: 3000 -> URL in browser, nuxtwoo.example.com:3000.
This will also need to for other params, such as nuxtwoo.example.com/blog, should go proxy_pass localhost:3000/blog, and the browser url should be nuxtwoo.example.com:3000/blog.
Can't seem to figure this one out.
You need to use an upstream
upstream http_backend {
server 127.0.0.1:8080;
keepalive 16;
}
server {
...
location /http/ {
proxy_pass http://http_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
...
}
}

Problem with nginx auth_request directive and location block with set

I have a variable called $host_header defined in the server section, which is overridden in location level. It works perfectly fine as all sets are done in the rewrite phase, so before any proxy_pass directive. Eventually, the proxy_pass to the "test-nginx" goes with the Host header "location level". However, (and here comes the magic) when I add the auth_request directive (which in my example always return 200 and it runs in the access phase, so after all sets) to the location block, the proxy_pass goes with the Host header "server-level", which in my opinion is a bug.
It's on nginx 1.19.1
Here is the example code. Any ideas why does it happen? Is it a bug or I'm doing smth wrong?
server {
listen 80;
server_name "local.main.server";
set $host_header "server level";
proxy_set_header Host $host_header;
location /test {
auth_request /auth;
set $host_header "location level";
# call backend server
proxy_pass http://test-nginx;
}
location = /auth {
internal;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
# this simply return 200
proxy_pass http://test-nginx;
}
}
I've found a really nice workaround to this strange problem. With the use of the "js_set" directive from the NJS module, we can create another variable $target_host_header and assign to it the result of the getTargetHeader function:
...
load_module /usr/lib/nginx/modules/ngx_http_js_module.so;
...
http {
...
js_import NJS from /path/to/file/with/functions.js;
...
js_set $target_header NJS.getTargetHeader;
proxy_set_header Host $target_header;
...
}
still in the server section we can set the $host_header variable, but inside a location we have to set different variable $proxy_host_header:
server {
...
set $host_header "server level";
location /test {
set $proxy_host_header "location level";
proxy_pass http://SOMEENDPOINT;
}
...
}
and here comes the njs function:
function getTargetHostHeader(r) {
if (r.variables.hasOwnProperty('proxy_host_header') && r.variables.proxy_host_header.length > 0) {
return r.variables.proxy_host_header;
} else {
return r.variables.host_header;
}
}

Nginx keep-alive w/o upstream

I'm using a variable to define my proxy_pass target, similar to this answer except in my case the value is extracted from a query param on the incoming request, so e.g. /?url=http://example.com will be proxied to example.com. Since the proxy_pass argument is a variable, I have no upstream directive in my config. The proxying works fine. However, the connection is closed after each request and I want to them alive to amortize the TCP handshake. According to these directions, that requires a keepalive directive in the upstream directive. How can I do this without an upstream directive?
Here's my full config:
server {
if ($arg_url ~ \/\/([^\/]*)) {
set $proxy_host $1;
}
if ($proxy_host = "") {
return 404;
}
resolver 8.8.8.8;
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $proxy_host;
proxy_pass $arg_url;
}
}

Dynamic routing with NGINX

I am an amateur of NGINX, I want to setup NGINX as a Reverse Proxy for my web server.
I would like to know that the NGINX these things as listed below:
When a browser send request with URL: http://nginxproxy.com/client/1.2.3.4/, this request should be passed to the client with IP 1.2.3.4 http://1.2.3.4/, the browser should still show the URL nginxproxy/client/1.2.3.4/
And the same for:
nginxproxy.com/client/2.3.4.5 --> //2.3.4.5
nginxproxy.com/client/2.3.4.6 --> //2.3.4.6
All the others requests that doesn't mach the pattern should come to my default server myserver.
Can I do this by using NGINX?
After researching, I tried with the below configuration:
But unlucky, It doesn't work. The address was changed to http:/1.2.3.4 on browser's address bar, instead of http:/nginxproxy.com/client/1.2.3.4 as expected.
server {
listen 80;
location ~ ^/client {
rewrite ^/client/?(.*) /$2 break;
proxy_pass $scheme://$1;
}
location / {
proxy_pass http://myserver.com;
}
}
Any help is much appreciated.
Doing some more research and based on #Cole input, here is my answer:
location ~ ^/client/(?<site>[^/]+)/? {
rewrite ^.*\/client\/(?<site>[^\/]+)\/?(.*) /$2 break; #passing all the remaining request URIs after <site> group to client server
proxy_pass $scheme://$site;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host/client/$site; #this help to keep the address as it is on the browser's address bar
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass $scheme://myserver.com
}
server {
listen 80;
location /client/ {
rewrite ^/client/(?<site>[^/]+)/? $scheme://$site;
}
location / {
proxy_pass $scheme://myserver.com;
}
}

Resources