DEV Community

loading...

Configure nginx to host multiple subdomains

on_stash profile image Santosh Venkatraman πŸ–– ・4 min read

This post has been cross-posted from my blog


Configuring nginx for subdomains in the same machine was confusing for me. So I am writing this tutorial primarily for (self)reference but people can find it useful. Without much ado, let's get started.

For those of you who don't know what nginx is exactly, head over to this freeCodeCamp article as a starting point.

I assume you're on a linux machine and you know what nginx means.

First and foremost, install nginx -

sudo apt-get install nginx

Second head over to /etc/nginx/sites-available/default by opening it on vim/nano. You would come across something like this -

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

# Default server configuration
#
server {
    listen 80 default_server;
    listen [::]:80 default_server;

    # SSL configuration
    #
    # listen 443 ssl default_server;
    # listen [::]:443 ssl default_server;
    #
    # Note: You should disable gzip for SSL traffic.
    # See: https://bugs.debian.org/773332
    #
    # Read up on ssl_ciphers to ensure a secure configuration.
    # See: https://bugs.debian.org/765782
    #
    # Self signed certs generated by the ssl-cert package
    # Don't use them in a production server!
    #
    # include snippets/snakeoil.conf;

    root /var/www/html;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    server_name _;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }

    # pass PHP scripts to FastCGI server
    #
    #location ~ \.php$ {
    #   include snippets/fastcgi-php.conf;
    #
    #   # With php-fpm (or other unix sockets):
    #   fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    #   # With php-cgi (or other tcp sockets):
    #   fastcgi_pass 127.0.0.1:9000;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #   deny all;
    #}
}


# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
#   listen 80;
#   listen [::]:80;
#
#   server_name example.com;
#
#   root /var/www/example.com;
#   index index.html;
#
#   location / {
#       try_files $uri $uri/ =404;
#   }.
#}

From the above you can gather the following points -

  • server {} - that tells nginx that "Hey this is how I think you should configure the server"
  • listen 80 - translates to "Listen to port 80, which is the default port for web clients"
  • root /var/www/html; - nginx understands that HTML file/files from that location can be served
  • index index.html index.htm index.nginx-debian.html; - this indicates that these kind of files should be served in a decreasing order of priority (left to right).
  • server_name _; - this conveys that _ is the server name. Assuming our domain to be example.com, the server name would be example.com. That's because when a request is received for example.com, nginx should whether to process it or not.
  • location / {} - this means that all requests must be handled by this server config. There is an option to handle specific requests as well. For example, requests starting with /server_1 can load server_1.html and all other requests can load default.html.

Next obvious question is - how do I configure subdomains?

It's not that complicated. It involves 7 steps -

  • Copy the default configuration and rename it as subdomain1.example.com.
cp /etc/nginx/sites-available/default /etc/nginx/sites-available/subdomain1.example.com

Note: You don't necessarily have to name the file subdomain1.example.com. It's easier if you follow a convention that makes it easy for everybody involved.

  • Open it and remove the term default_server besides listen 80 and the line below it. This is crucial.
  • Change the server_name as subdomain1.example.com & change the HTML root that's being used
  • Then link that file to a file in nginx's sites-enabled directory. This is for nginx to understand that configuration subdomain1.example.com is enabled in the configuration.
sudo ln -s /etc/nginx/sites-available/subdomain1.example.com /etc/nginx/sites-enabled/subdomain1.example.com
  • Write a HTML file inside /var/www/html for subdomain1.example.com
  • Restart the nginx server
  • If you're using a DNS provider like Cloudflare, add the box's IP to it with an A record subdomain1.example.com. If not, then add the box's IP and subdomain1.example.com to /etc/hosts.
  • Load subdomain1.example.com on the browser to check how it loads. If everything is fine, you will see the HTML that you wrote for this subdomain.

Extend the same approach for any other domain or subdomain that you require.

Discussion

pic
Editor guide
Collapse
kp profile image
KP

Hi Santosh,

I've been struggling with setting up nginx subdomains on my linode instance and setting up CNAME redirects.

What I need is to be able to do:

  1. First set up wildcard subdomains on my server (tinyadults.com), so that users can go to abc.tinyadults.com, xyz.tinyadults.com, etc.
    My server is running nuxt.js on port 4001 (default port is 3000 but I chose to use 4001 as a non-standard port), so I guess I have to use reverse proxies:
    proxy_pass localhost:4001;

  2. Then for my users I need to set up CNAME redirects from domain1.com to abc.tinyadults.com, and from domain2.com to xyz.tinyadults.com, so that if I visit domain1.com , it would serve the contents (without redirecting me) of abc.tinyadults.com. For testing purposes I have an additional domain (passivefinance.com) that we could use.

However, I've not been able to get step 1 working. Any ideas?

Below is my nginx config from sites-available/tinyadults.com.conf:

server {
    index index.html index.htm;
    server_name tinyadults.com www.tinyadults.com;

    location / {
        # WARNING: https in proxy_pass does NOT WORK!! I spent half a day debugging this.
        #proxy_pass https://localhost:4001;
        proxy_pass http://localhost:4001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;

    }

    # Kunal: create a custom 404 nginx page, from https://www.digitalocean.com/community/tutorials/how-to-configure-nginx-to-use-custom-error-pages-on-ubuntu-14-04
    error_page 404 /custom_404.html;

    location = /custom_404.html {
        root /etc/nginx/sites-available/custom_nginx_error_pages;
        internal;
    }

    listen [::]:4001 ssl http2; # managed by Certbot, modified by Kunal to add http2
    listen 4001 ssl http2; # managed by Certbot, modified by Kunal to add http2
    #Install SSL certificates and configure https:// on a per-domain-basis by running:
    #sudo certbot --nginx
    #(when prompted, be sure to select the option to set up redirects from http to https and effectively "disable" http)
    ssl_certificate /etc/letsencrypt/live/tinyadults.com-0001/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/tinyadults.com-0001/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}



server {
    server_name tinyadults.com;
    if ($host = tinyadults.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    listen 80 default_server;
    listen [::]:80 default_server;
    return 404; # managed by Certbot
}
Enter fullscreen mode Exit fullscreen mode