Nginx – Connection Refused While Connecting to Upstream

javascriptnginxsinatra

I have a server on which I've deployed an API (Sinatra). I run that API on port 9292 and proxy requests to it.

I also need to deploy a static javascript file and its supporting resources. I do that on the same server.

All of the information below has been anonymized.

The API access endpoint is this:

server {
    listen 80;
    server_name api.myapp.com;

    location / {
            proxy_pass http://localhost:9292;
    }
}

and the endpoint I'm serving the JS from is:

server {
    listen 80;
    server_name xsa.myapp.com;

    location / {
            root /data/mydir/xsa;
    }
}

The "localhost:3003" server that you see in the error below is where I am referring to the JS from.

2018/11/23 23:31:27 [error] 18720#0: *32 connect() failed 
(111: Connection refused) while connecting to upstream, 
client: 255.255.255.7, server: api.myapp.com,
request: "GET /api/v1/sub/sub_ABCXYZ/info?token%3D56f51132-
406b-4e06-a600-7379876446ed HTTP/1.1", upstream:
"http://[::1]:9292/api/v1/sub/sub_ABCXYZ/info?token%3D56f51132
-406b-4e06-a600-7379876446ed", host: "api.myapp.com",
 referrer: "http://xsa.myapp.com/src/selector.html?
mfid=sub_ABCXYZ&location=http://localhost:3003/"

So to recap:

  • Sinatra API on VM "x" with port 9292
  • nginx server on VM "x" serving up a proxy on port 80 to the API for requests coming in to "api.myapp.com"
  • same nginx server also serving up a javascript file on port 80 for requests coming in to "xsa.myapp.com"

I can view the JS just fine in a browser, so it is indeed being served up. It is in the dynamic stuff it does upon instantiation that is causing the error.

I can also see that the Sinatra server on 9292 is indeed getting the request and returning a 200. It appears that the problem is after this then. So it doesn't even look like the upstream connection is actually refused. I have double-checked that this reply is the exact same response as when the JS is served up locally (and works).

* UPDATE *

The "connection refused" error actually DOES occur even when the JS is served locally and works correctly, so it looks like that error is a red herring.

Best Answer

You're getting this error message because your Sinatra application is not listening on the default IPv6 address for localhost, ::1, but is instead listening on the legacy IPv4 address, 127.0.0.1.

Because you specified that your nginx upstream is localhost, nginx tries the IPv6 address first, but because your app server isn't listening on this address, nginx gets a Connection refused. It then retries the connection on IPv4, which succeeds, and serves your application.

Because this introduces a performance problem, and because IPv6 is preferred over the deprecated IPv4, you should reconfigure your Sinatra application to listen on IPv6. For example,

set :bind, '::1'

In particular, you should not bind to 127.0.0.1.

You can also just remove the bind host, because by default it will listen on IPv6 localhost if you're using thin (as you should in prod) or WEBrick (in dev, if you forget to gem install thin).

(And on a related note, you have explicitly configured nginx to not listen on IPv6, which will cause you similar problems later on, or perhaps even now. You should fix that too, while you're at it.)