nginx

When serving web applications from your own server (or a dedicated instance in the cloud) you might want to allow an access to the users from all around the world. Giving out an IP address does not look very professional so you choose to have an awesome domain. What if you have multiple domains, though? Let's configure nginx server so that you don't have to pay extra and have everything in the same place.

DNS configuration

In order to publish a site under a custom domain, you need to point it to a certain server using DNS server configuration. In order to do this, you need to know DNS servers of your hosting provider and add their addresses in some panel on a site that manages your domain (which you chose to purchase it). Then you also add that domain on the hosting side, so that all the traffic is redirected to your machine.

At this point everything seems quite easy and happens almost automatically, but what if you want to have one instance serving multiple domains? In fact, it requires just a couple of lines in your nginx configuration.

For testing purposes we need to imitate DNS server and add a couple of domains, page1.com and page2.com to /etc/hosts/, so that both are pointing to 127.0.0.1 (aka localhost):

# /etc/hosts
127.0.0.1    localhost
...
127.0.0.1    page1.com page2.com

Sample application behind nginx

First, we will set up an environment of a Hello world-like simplicity and nginx server (for details how to do that check out my post on reverse proxying in nginx). The application looks like this:

page1/main.go
...
func main() {
    http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
        fmt.Fprint(rw, "Hello from page1.com!")
    })
    http.ListenAndServe(":3000", nil)
}

As you can see, it responds with Hello from page1.com! whenever it is reached on port 3000. Then the nginx configuration needs to know that this particular port should be proxied. As we are using docker-compose, the application is available on address page1:3000:

# nginx.conf
events {}

http {
    server {
        listen 80;

        root /www;
        index index.html;

        location / {
            proxy_pass http://page1:3000/;
        }
    }
}

When we spin it off, we can see it working properly (nginx:80 is mapped to localhost:10080):

$ curl page1.com:10080
Hello from page1.com!

Second application

Adding another application should not be difficult, right? We'll add another service in docker-compose.yml, that one is also accessible on port 3000, but this time the host (and the response body) would say page2. We need to add another server block to nginx configuration:

#nginx.conf
events {}

http {
    server {
    ...
    }
    server {
        listen 80;

        root /www;
        index index.html;

        location / {
            proxy_pass http://page2:3000/;
        }
    }
}

The problem is, that it doesn't really work:

$ curl page1.com:10080
Hello from page1.com!
$ curl page2.com:10080
Hello from page1.com!

Working solution

The key here is to add server_name property to each server block in the configuration:

# nginx.conf
...
http {
    server {
        ...
        server_name page1.com www.page1.com;
        location / {
            proxy_pass http://page1:3000/;
        }
    }
    server {
        server_name page2.com www.page2.com;
        ...
        location / {
            proxy_pass http://page2:3000/;
        }
    }
}

It's as simple as that:

$ curl page1.com:10080
Hello from page1.com!
$ curl page2.com:10080
Hello from page2.com!

The full source code of this example is available on Github.