Setup SSL certificate for different domains on the same nginx server

Tram Ho


I still occasionally do small pet projects and post them on my VPS. And of course, for every project will be a different domain (usually subdomain) and each will need to setup HTTPS. And this is when the problem arises, how can we set up many different SSL for the same server (because the home has 1 VPS to host everything, but where to get many. This article will go into details of the problem I have encountered and my solution, although not very optimal but acceptable.

The story of wild-card SSL

My problem: set up SSL for two subdomains: and . Because I made these 2 projects in two different time periods, the matrix first, then the blokus, so before that, I had the gen SSL certificate for the domain with Let’s Encrypt.

When a new subdomain appears, the most appropriate solution is to use a wild-card SSL cert. Wildcard SSL is a type of SSL certificate that can be used for the main domain and all subdomains of the website. This type is ideal for customers who use multiple subdomains such as online stores, sub-domains that need SSL for facebook apps .. etc. Thus, only one cert * can be used for as many subdomains as you like, it’s great isn’t it.

And fortunately, Let’s Encrypt has supported the creation of wild-card SSL cert since March 2018. The way to register is also very simple. We run the following command: (here I assume you already know how to install SSL of Lets Encrypt using a certbot with nginx server)

We will choose the --manual mode and authenticate via dns and agree --agree-tos , then the certbot will ask us to add a DNS TXT record to our domain with a predefined value (note The idea is that each run will produce a different value). This is not too difficult, manipulating a little at the domain management screen (my NameCheap), then we can continue. For example:

At this point, a new problem arises:

What the heck is CAA ? 😂 ? Spend some time on Google. We have the following additional information:

  • Recently, due to the fact that people can easily register for SSL cert via Lets Encrypt and other services, it has led to a lot of fraudulent attempts to take place, making the issue of wrong SSL certificate possible. out . To prevent this, the CAB Forum, with the support of the IETF, found a solution: CAA Record

Certification Authority Authorization (CAA) is a DNS record that allows domain owners to specify that Certification Authorities (CAs) can issue SSL certificates for their domain, thus preventing CAs from being able to issue certificates. SSL for their domain.

Therefore, from February 22, 2017, before issuing an SSL cert for a domain, CAs will have to check if that domain has a CAA record, and if the CAA is being setup with another CA, it will have to refuse to issue certificates for this domain. In case there is no CAA, CA can issue certificates. The reason for this is:

  • Domain owners only want to limit certificates to a number of CAs they trust.
  • Adding this CAA also forces the domain to take one more check step, avoiding the issuance of unwanted certificates.

And of course, CAA also has many different types of CAA, and we will be interested in two main types:

the first one allowed Comodo CA (now Sectigo CA) to issue certificates for domain and the second one, which we need, allowed the issuance of wild-card cert. We can test the current CAA through

So all we do is add the CAA record to release the wild-card cert for Lets Encrypt, that’s it. Referring on the net, also saw quite a lot of instructions:

But life is not like a dream, after a fight with all types of CAA, the result is still no, no, and no

Check back and the results are still the same:

Configure SSL cert for a domain

Let’s first take a look at the nginx configuration that comes with SSL for a domain

Basically nothing too confusing, we need to pay attention to the following points:

  • ssl_certificate and ssl_certificate_key are the paths to the certificate file and the private key file for our domain
  • The server will listen on port 443 and if the request to port 80 through the domain will be redirected to https
  • All requests will be proxyed to the real server behind nginx running at port 4001

So if we add another block server and upstream like that for blokus is it possible? I also read some other tuts that do the same, but they don’t work, I don’t understand why, probably because the accommodation is not good. Anw. Read in another place, you see that writing is by the same IP, so when the request comes, so nginx will not know what forward IP, and will choose to take a block server as the default. As a result, when visiting , nginx again issued the certificate of , causing warnings about inappropriate certificates and users can not access.

So the problem cannot be solved at the level of http server because SSL handshake has been done before, we need a different solution.

Solution ???

After consulting and testing many configurations on the network, I came to the following configuration:

Nginx has the ssl_preread module ( ). This module allows us to read information from the ClientHello message (one of the handshake messages that the client sends during the SSL handshake process, you can read more at /first-few-milliseconds-of-https.html to better understand). In this message there is an item called SNI :

Server Name Indication (SNI) is an extension to the Transport Layer Security (TLS) computer networking protocol by which a client indicates which hostname it is attempting to connect to at the start of the handshaking process. [1] This allows a server to present multiple certificates on the same IP address and TCP port number and forth allows multiple secure (HTTPS) websites (or any other service over TLS) to be served by the same IP address without requiring all those sites to use the same certificate. – Wikipedia

It sounds like we need it. Na is the client will specify the domain you want to connect to in the SNI, the server can read this information (without leading to SSL / TLS disconnection) and give the appropriate cert. Check the current nginx build that supports SNI as follows:

There appears TLS SNI support enabled is OK!

Going back to the configuration of nginx.conf we see there are 3 different block maps:

  • map $ssl_server_name $stream_upstream will retrieve $ssl_server_name (variable generated from ssl_preread module) map corresponding server name with upstream (matrix_app or blokus_app).
  • The remaining two map blocks correspond to the map name with each certificate and the private key of each subdomain is different. And we see, 2 upstream are listening at 2 different ports.

At this point, we can put upstream 2 apps directly into these 2 blocks. However, since both of my games use websocket, these http configurations cannot be defined in the block stream, so it is imperative to forward to the new http block.

Check out the new configuration of these 2 servers:

At this point, the new http block will listen on port 4444 instead of 80 as before, and the other configurations are completely the same. The other server blockus is quite similar but listens on port 4445 .


It sounds confusing, but anw, It’s works. Hopefully there will be a higher way to solve the newspaper. This setup can also be useful in case you want an nginx server to be the common gateway for many different services in the background.


Share the news now

Source : Viblo