qknight
qknight

Reputation: 924

proxy_pass with nginx variable as argument

i want to use nginx set directive to use a variable as argument of proxy_pass but doing so ends up in a endless 301 Moved Permanently error in chrome when accessing https://foo.de/myleaps with the code below:

example code

user "reverse-proxy" "reverse-proxy";
error_log stderr;
daemon off;
events {}
http {
        server {
                listen 80;
                listen [::]:80;
                server_name foo.de;  
                location /myleaps {
                        rewrite     ^   https://$server_name$request_uri? permanent;
                }
        } server {
                ssl on;
                listen 443 ssl;
                listen [::]:443 ssl;
                server_name foo.de;
                ssl_certificate /var/lib/nixcloud/TLS/foo.de/selfsigned/fullchain.pem;
                ssl_certificate_key /var/lib/nixcloud/TLS/foo.de/selfsigned/key.pem;
                location /myleaps {
                        set $tttt http://127.0.0.1:3031/myleaps;
                        # https default flags
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header X-Forwarded-Proto $scheme;
                        proxy_pass $tttt;
                }
                location /myleaps/leaps/ws {
                        # https websocket default flags
                        proxy_http_version 1.1;
                        proxy_set_header Upgrade $http_upgrade;
                        proxy_set_header Connection "upgrade";
                        proxy_set_header X-Forwarded-For $remote_addr;
                        proxy_read_timeout 36000s;
                        # required because of CORS
                        proxy_set_header Host $host;
                        proxy_pass http://127.0.0.1:3031/myleaps/leaps/ws;
                }
        }
}

using hardcoded values (no set)

when using this:

proxy_pass http://127.0.0.1:3031/myleaps;

instead of:

proxy_pass $tttt;

it works for my example.

so the question is: why does proxy_pass work with an hard coded string but not with using a variable?

motivation

in the reverse-proxy here https://github.com/nixcloud/nixcloud-webservices/blob/be57d526547c66db05595002682525ca62c8f068/modules/services/reverse-proxy/default.nix#L144 the code which generates the proxy_pass is basically hardcoded and if a user wishes to override this it is going to be really complicated. so the idea is to generate 3 variables: ip, port and path and put it into the location scope and let the user later use these. this way i could get rid of the hardcoded proxy_pass directive and the user would be much more flexible in modifying/generating the location record.

so the code could be like this:

set $targetIP = ${location.ip};
set $targetPort = ${toString location.port}
set $targetPath = ${removeSuffix "/" (toString (builtins.toPath (location.path)))};

and the 3 lines above would always be generated into that location record, no matter what. but the code below is dynamically generated and a default implementation could be this:

proxy_pass http://$targetIP:$targetPort$targetPath;

but the user could simply override it by:

  nixcloud.reverse-proxy = {
    enable = true;
    extendEtcHosts = true;
    extraMappings = [
      {  
        domain = "example.com";
        path = "/";
        https = {
          mode = "on";
          basicAuth."joachim" = "foo";
          record = ''
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_pass http://$targetIP:$targetPort$targetPath
          '';
        };
      }
    ];
  };

Upvotes: 0

Views: 4823

Answers (1)

qknight
qknight

Reputation: 924

it is working using $request_uri instead of hardcoding the proxy_pass path using $targetPath:

set $targetIP 127.0.0.1;
set $targetPort 3031;
proxy_pass http://$targetIP:$targetPort$request_uri;

this code will soon be in nixcloud-webservices!

thanks to irc.freenode.net#nginx@benbrown (Ben Brown) for this solution!

Upvotes: 1

Related Questions