Configuring multiple subdomains on an NGINX webserver

TLDR:  In this post I'm going to detail an optimal NGINX webserver configuration for multiple subdomains using a single server block and a single Let's Encrypt SSL certificate.

 

Background

NGINX is the leading web server application powering more than 50% of the busiest sites on the internet.  It's highly configurable and capable of handling thousands of concurrent requests. 

Let's Encrypt is a free, automated SSL certificate service and certificate authority. 

This post is technical and aimed at the DevOps community.  I'm assuming that you're familiar with configuring NGINX, installing SSL certificates, and the command line.

 

Why this Post

Recently, I wanted to serve a new web application at https://app.mezosphere.com.  I also wanted a related (but separate) micro-site at https://www.mezosphere.com and it took some research to arrive at an optimal solution.  The challenge was documentation outlining outdated solutions (including on Stack Overflow).

What I'm documenting here is:

  1. a single Let's Encrypt SSL certificate for mezosphere.com, www.mezosphere.com, & app.mezosphere.com.
  2. a single NGINX server block for mezosphere.com, www.mezosphere.com, & app.mezosphere.com referencing that single SSL certificate.

I consider this an optimal solution because it's 'DRY'.  The domain & it's subdomains share a single NGINX configuration block for a single SSL certificate making maintenance and automated renewal easier.

 

Things you should know beforehand:

  1. the "www" in "www.mezosphere.com" is actually a subdomain (like app.mezosphere.com).  With a few caveats, mezosphere.com can be thought of as a separate domain - the root domain.
  2. NGINX has a master configuration file named "nginx.conf".  This master file typically references other configuration files (typically one per virtual domain).
  3. Let's Encrypt SSL certificates are typically installed and renewed automatically using their 'certbot' (a certificate management agent running on the web server).  This agent supports and uses NGINX to install SSL certificates.
  4. For various reasons it's now generally (but loosely) considered good practice to use the www subdomain for the main site (not the root domain).  So the root domain will usually redirect to the www subdomain.

 

NGINX Configuration

I'm going to assume that NGINX is already installed, and that the main NGINX configuration file "nginx.conf" is already configured (probably to include separate configuration files for each virtual domain).

In this post I'm focussing on configuring the mezosphere.com domain and it's subdomains only.

Since we're strictly enforcing secure HTTPS, I'm going to start off by redirecting all HTTP (insecure traffic on port 80) to HTTPS (secure):

# HTTP server block (redirect HTTP-->HTTPS)
server{
    server_name .mezosphere.com;        # special syntax to match mezosphere.com AND it's subdomains
    
    listen 80;           # IPv4
    listen [::]:80;      # IPv6

    return 301 $https://$host$request_uri;    # permanent redirect to HTTPS (whilst maintaining the original url)
}

 

Here's the NGINX config that I want to highlight:

# HTTPS server block for mezosphere.com, www.mezosphere.com & app.mezosphere.com
server {
    server_name ~^(?[^.]*)\.?mezosphere.com$;
    root /var/www/mezosphere.com/web/public/$subdomain;

    ...

It uses a regular expression to match the domain and it's subdomains - and it captures the subdomain name into a variable: $subdomain.  $subdomain is then used to specify separate "root" locations for each subdomain (for the location of the root index.html among other files). 

It's useful for configuring multiple subdomains that require a similar configuration but require a separate public folder.  The alternative is to setup individual server blocks for each subdomain.

The following lines are specifically for HTTPS/SSL requests (on port 443).  Note that Let's Encrypt's SSL certificate setup (detailed in next section) should automatically add/maintain the latter part of this section:

    ...
    
    listen 443 ssl http2;         # IPv4
    listen [::]:443 ssl http2;    # IPv6            

    #SSL certificates installed by Let's Encrypt service
    ssl_certificate /etc/letsencrypt/live/mezosphere.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/mezosphere.com/privkey.pem; # managed by Certbot

    ...

In our case we don't use the root domain "mezosphere.com" and so we want to redirect it to www.mezosphere.com:

    # redirect mezosphere.com --> www.mezosphere.com           
    if ($subdomain = '') {
        return 301 https://www.mezosphere.com$request_uri;
    }

Some people say that if blocks are evil.  But I'm using an if block in order to do this redirection inside the HTTPS server block (so that we don't have to repeat the SSL configuration in a separate server block).

 

Installing a single, multi-subdomain SSL certificate

You'll need to have already installed Let's Encrypt SSL agent (certbot) for your specific platform before tackling this section.  This is how to invoke the certbot to create a single multiple subdomain certificate (from the command line or terminal):

> sudo certbot certonly -d mezosphere.com -d www.mezosphere.com -d app.mezosphere.com

This creates an SSL certificate for the root domain which includes multiple subdomains. 

Alternatively you can include a subdomain wildcard like this:

> sudo certbot certonly -d mezosphere.com -d *.mezosphere.com

During SSL certificate creation you'll have the option to use (and configure) NGINX.  Choose that option!

Finally, after you're done configuring NGINX and SSL you can test what you've done using:

> sudo nginx -t

 

Additionally watch (or 'tail') your NGINX log file for logs that could help you to debug specific issues while testing your subdomains requests/redirection in your browser address bar.




Comments...

Click here to comment...

Anonymous