Problem with nginx auth_request directive and location block with set - nginx

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;
}
}

Related

Nginx reverse proxy to frontend and backend

I have two react app running on localhost:3000(frontend) and localhost:3001(backend). I want to serve both backend and front end from same server_name.. For example, if a user hits example.com the Nginx should route the traffic to frontend running on (localhost:3000) and if a user hits example.com/admin/login traffic should get routed to the backend (localhost:3001).
'''
server {
listen 80;
server_name example.com;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
location / {
proxy_pass http://localhost:3001;
}
location /admin-login {
proxy_pass http://localhost:3000;
}
}
'''
Using above configuration. I have frontend running on example.com. However, when I call example.com/admin/login I am getting redirected to app running on frontend (localhost:3000) instead of backend running on (localhost:3001).
Updating as per the answer given below. I have below configuration. it still have the same behavior.
server {
listen 80;
server_name example.com;
location /admin-login {
proxy_pass http://127.0.0.1:3000/admin-login;
}
location / {
proxy_pass http://127.0.0.1:3001;
}
location /home {
proxy_pass http://127.0.0.1:3001/home;
}
location /login {
proxy_pass http://127.0.0.1:3001/login;
}
location /signup {
proxy_pass http://127.0.0.1:3001/signup;
}
location /article {
proxy_pass http://127.0.0.1:3001/article;
}
}
I think the problem here is that
location / {
proxy_pass http://localhost:300
}
matches all queries so will also redirect anything to /admin-login
You could either rearrange your blocks to have the admin-login block above the / block in the config file, or make the adjustment below:
location = / {
proxy_pass http://localhost:300
}
This adjusted block should only redirect queries to / rather than /*
If you want to read more, it's explained in the documentation here - https://nginx.org/en/docs/http/ngx_http_core_module.html#location

Nginx location / overrides all other locations

I'm trying to write a simple nginx config. What I need is:
if file exists in root serve this file
If url is /default/url then show /some/path2/index.html
Otherwise redirect to /default/url
my config is as follows
server {
listen 127.0.0.1:80;
server_name my.domain.com;
root /some/path/html;
location / {
return 302 /default/url;
}
location = /default/url {
rewrite ^/(.*)$/some/path2/index.html;
}
location /default/e_schema {
proxy_pass http://other.host.com;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
It redirects to /default/url instantly regardless of url.
I was trying to put the location / block on the bottom and on the top. I've tried to use location ~ /.* to lower priority but nothing helps. If I remove location / at all everything is fine my requirements 2 and 3 is ok.
According to this answer https://serverfault.com/questions/656628/nginx-catch-all-other-locations-than-given/656634 it should work.
You have put an "=" in the location block
location = /default/url {
Could you try removing this? I believe it may be setting the url
The problem was here
location = /default/url {
rewrite ^/(.*)$/some/path2/index.html;
}
this makes internal redirect to /some/path2/index.html which is within / path so it triggers the location / block which redirects to /default/url and so on.
My solution was to make empty block to exclude the path from location /
location /some/path2/index.html {}

Nginx dynamic proxy configuration

Below is a static config of what I'm trying to do.
server {
listen 80;
server_name browser.shows.this.server.com;
location / {
proxy_set_header Host backend.server.com;
proxy_redirect http://backend.server.com/ http://browser.shows.this.server.com/;
}
}
How can I make backend.server.com dynamic for each request? I'd like to pass the domain somehow in the request. Maybe in a header?
You should use proxy_pass instead of proxy redirect. Hope this helps
alternatively can write a config like this
resolver your-server-ip;
set $upstream_endpoint http://your-url;
location / {
rewrite ^/(.*) /$1 break;
proxy_pass $upstream_endpoint;
}
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass

Nginx: cache only specific urls and named location

I have rails application. There are parts of nginx config of it:
upstream app_server {
server unix:/var/www/app/shared/unicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name app hostname;
keepalive_timeout 5;
root /var/www/app/current/public;
try_files $uri/index.html $uri.html $uri #app;
location #app {
proxy_pass http://app_server;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_cache off;
}
location ~ /assets/*\.(png|gif|jpg|jpeg|css|js|swf|ico|gz)(\?[0-9]+)?$ {
access_log off;
}
}
I want to cache several pages of my apps (for example all *.json urls). How I can do that?
According to nginx docs I can't:
use nested location in named location
use proxy_cache in if block
You can get away with adding Json to the extensions in the static file block if they are not virtual URLs.
If they are, you need to either setup differently by forwarding everything to your app by default and making exceptions, so you avoid having to use named locations. Or you can set variables based on if statements inside the named location:
location #app {
set $proxy_cache_cfg "off";
if($request_uri ~ \.json$) {
set $proxy_cache_cfg "json_zone";
}
}
Untested, not sure if "off" should be quoted and whether it even would work here. If this won't work, you can always use the reverse approach and set proxy_no_cache based on a variable, since that is in effect for anything non-empty and non-zero.

Nginx rewrite based on header value

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;
}
}

Resources