0

I'm trying to use a different scheme for the proxy_pass rule based on the value $host variable.

However it doesn't seem to work, here's the behavior we're observing.

If we set proxy_pass with a constant scheme like this proxy_pass https://$upstream; everything works well, but if we try to replace the hardcoded scheme value (https) with a custom variable and then use proxy_pass $myscheme://$upstream; Nginx seems to ignore $myscheme and tries to resolve $upstream without using a scheme, which obviously fails.

This happens even if we set the variable like this set $myscheme https;.

Is this behavior normal? What are we doing wrong? Is there a way to use a different scheme based on the value of a variable set at runtime?

CURRENT (NOT WORKING) CONFIGURATION

Based on our tests, looks like (at the least the version we're running) Nginx is not actually replacing variables in proxy_pass

worker_processes 4;
worker_rlimit_nofile 100000;

events {
    worker_connections 100000;
    multi_accept on;
    use epoll;
}

http {

    log_format timed_combined '$remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" '
    '$request_time $upstream_response_time';

    server {
            listen 443;
            server_name myservername;

            ssl on;
            ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
            ssl_certificate /etc/letsencrypt/live/xxx;
            ssl_certificate_key /etc/letsencrypt/live/xxx;

            gzip on;
            gzip_comp_level 2;
            gzip_min_length 1000;
            gzip_proxied expired no-cache no-store private auth;
            gzip_types application/json text/json text/plain application/x-javascript text/xml text/css application/xml;
    
            access_log /var/log/nginx/access.log timed_combined;
    
            set $myscheme https;
            set $myhost myhostname;
    
            location / {
                     proxy_pass $myscheme://$myhost;
            }
      }

}

NGINX VERSION: 1.10.3

pAkY88
  • 6,262
  • 11
  • 46
  • 58
  • @AD7six sure, in this simple case we could just hardcode the scheme value but that's not our goal. We're implementing a more complex use case and in order to do that we need to set the scheme at runtime using a variable. However for some reason that isn't working, although it should cause it seems very straightforward. – pAkY88 Feb 26 '23 at 22:25
  • @AD7six I agree it sounds impossible but this simple config is not working as expected. Apparently the variable $myscheme isn't replaced and as a consequence every request fails with a 502 error (bad gateway). If instead of the variable we use "https" everything works perfectly. – pAkY88 Feb 27 '23 at 09:23
  • 1
    It is [not reproducible](https://gist.github.com/AD7six/55358375de9a20e4172cd7ada9403567). Good luck. – AD7six Feb 27 '23 at 09:46
  • `proxy_pass $myscheme://$myhost` works as intended on `1.10.3`. Consider troubleshooting your upstream server config. Kindly post something reproducible that is full and complete. – anthumchris Feb 27 '23 at 20:40

1 Answers1

2

This works with aforementioned Nginx 1.10.3 (and latest 1.23.3):

test.sh

docker pull -q nginx:1.10.3 &>/dev/null && echo Docker image pulled
docker run -d --name nginx-test -p 8000:8000 nginx:1.10.3 1>/dev/null && echo Docker container started

echo -e "\nConfiguring Nginx..."

docker exec nginx-test bash -c <<END '
# Restarting Nginx kills the docker container. reload instead.
# Clear existing servers to prevent port binding errors.
NGINX_CONF=/etc/nginx/conf.d/default.conf
nginx -v
rm -f $NGINX_CONF
nginx -s reload 2>/dev/null && echo Nginx reloaded with no listeners

# Write test config
cat <<"ENDGINX" > $NGINX_CONF
server { 
  listen 127.0.0.1:80;

  default_type "text/plain";
  add_header x-scheme $scheme always;
  return 200;
}

server { 
  listen 8000 default_server;
  server_name _;

  set $pxy_proto $arg_proto;
  set $pxy_host  127.0.0.1;

  add_header x-proto $pxy_proto always;

  location / {
    proxy_pass $pxy_proto://$pxy_host;
  }
}
ENDGINX

nginx -s reload 2>/dev/null && echo Nginx reloaded with testing config
'
END

sleep 1 # Await Nginx startup

echo -e "\nTesting URLs...\n"
echo http://localhost:8000/?proto=http ; curl -sSI $_ | egrep '(HTTP|proto|scheme)'
echo
echo http://localhost:8000/?proto=FAIL ; curl -sSI $_ | egrep '(HTTP|proto|scheme)'
echo

docker -v stop nginx-test 1>/dev/null && echo Docker container stopped
docker -v rm nginx-test   1>/dev/null && echo Docker container removed
echo -e "\nDone! "

Results

Testing URLs...

http://localhost:8000/?proto=http
HTTP/1.1 200 OK
x-scheme: http
x-proto: http

http://localhost:8000/?proto=FAIL
HTTP/1.1 500 Internal Server Error
x-proto: FAIL
anthumchris
  • 8,245
  • 2
  • 28
  • 53
  • that configuration seems pretty straightforward and is very similar to the one I'm using, however for some reason it is not working as expected. Maybe it depends on the version of Nginx I'm working on (1.10.3) ? – pAkY88 Feb 26 '23 at 18:11
  • This is not a version error, and the above works with 1.10.3. Consider posting your full *working* config so we can properly test and troubleshoot. – anthumchris Feb 27 '23 at 20:28