Nginx Proxy – Dynamic proxy_pass DNS Resolution Issues

domain-name-systemnginxPROXYproxypass

I'm having problems configuring nginx, I've searched the web for solutions and I have some bits and pieces but I'm still not able to come up with proper configuration.

I have registered a domain, let's say – www.example.com. I've configured everything on the registrars site, pinging www.example.com and www.*.example.com succeeds.

I have Apache Tomcat running on my machine listening on port 8080. I want to set up dynamic proxy_pass.
So if I have application MyApp running in tomcat and reachable through localhost:8080/MyApp, I want to be able to reach it with www.MyApp.example.com, so basically the subdomain would be the name of the application in tomcat.

Here's my nginx config:

server {
  server_name ~^(www\.)?(?<sub_domain>.+)\.example\.com$;
  listen 80;

  location / {
     proxy_pass http://localhost:8080/$sub_domain/;
  }
}

When I go to www.myapp.example.com I'm being redirected to http://localhost:8080/myapp – I mean I literally end up with http://localhost:8080/myapp in my browser.

If however I change the regex in nginx setup to:

server {
      server_name www.myapp.example.com myapp.example.com
      listen 80;

      location / {
         proxy_pass http://localhost:8080/myapp/;
      }
    }

Then everything works like a charm. I know it has to do something with the resolver, I've already tried putting resolver in the nginx but it changes nothing.

What am I missing here?

//edit:

Here's my config. I'm still getting redirected to www.myapp.example.com/myapp/login instead of www.myapp.example.com/login. I've changed the regex, still the same.

  http {

    upstream backend {

             server 127.0.0.1:8080;

    }

    server {                                                         
          server_name ~^(www\.)?(?<sub_domain>.+)\.example\.com$;
          listen 80;                                                        


           location / {
               proxy_set_header "Host" $host;
               proxy_pass http://backend/prefix-$sub_domain/;

               proxy_redirect http://$host/prefix-$sub_domain/ http://$host;

           }

    }

curl:

* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /prefix-myapp/ HTTP/1.1
> User-Agent: curl/7.35.0
> Accept: */*
> Host: www.myapp.example.com
>
< HTTP/1.1 302 Found
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Set-Cookie: JSESSIONID=E609EB96D8F27FD6F4E7F9ED9ACA5245; Path=/prefix-myapp/; HttpOnly
< Location: http://www.myapp.example.com/prefix-myapp/login;jsessionid=E609EB96D8F27FD6F4E7F9ED9ACA5245
< Content-Length: 0
< Date: Tue, 21 Oct 2014 16:48:05 GMT
<
* Connection #0 to host 127.0.0.1 left intact

Edit://

Many thanks Xavier! Adding following two lines helped:

proxy_pass http://backend/prefix-$domain$request_uri;
proxy_redirect http://$host/prefix-$domain http://$host;

2 More questions though:

  1. Does this configuration has big impact on performance?
  2. Can I filter out something from the $request_uri (e.g. JSESSIONID=1233….)?

Again, many thanks! It took me a week to figure this one out!

Best Answer

When you are using variables in a proxy_pass directive, nginx will use runtime resolving except if :

  • the target server is declared as an IP address
  • the target server name is part of an upstream server group
  • the target server name has already been resolved (e.g. it matches a server name in another server block)

Here, a runtime resolver won't help as localhost may not be resolved by a DNS. Also it's a waste to have runtime resolving as you can clearly avoid it here.

So, two simple solutions :

  • use 127.0.0.1
  • declare an upstream block if you have server names or a pool of target servers

Now you need your proxied server redirection to be correct. So either :

  • your proxy target handles the host header and you pass it through with :

    proxy_set_header "Host" $host;

  • your proxy target can't handle the Host header for redirects and you need to rewrite them with nginx using :

    proxy_redirect http://$proxy_host/$sub_domain http://$host;

However if it doesn't support the Host header at all, links will be broken.

Related Topic