I am building a domain name management app that handles multiple domain names and serves different landing pages based on the domain name.
I have set up a DNS system that works using PowerDNS, any domain with my NS servers resolves to my server.
I have the following Nginx configuration:
# Define custom log format for domain monitoring
log_format domain_logger '{"time":"$time_local","host":"$host","remote_addr":"$remote_addr","request":"$request"}';
# Define debug log format to include SSL certificate information
log_format ssl_debug '{"time":"$time_local","host":"$host","http_host":"$http_host","ssl_certificate":"$ssl_certificate_by_domain","ssl_key":"$ssl_certificate_key_by_domain","has_cert":"$has_valid_certificate"}';
# Include dynamically generated certificate mappings
include /etc/nginx/conf.d/cert-mappings.conf;
# Include domains with certificates map
include /etc/nginx/conf.d/domains-with-certs.conf;
# Portfolio domains with valid certificates - SSL server (Catch-all for SSL)
server {
listen 443 ssl default_server; # Make this the default SSL server
listen [::]:443 ssl default_server; # IPv6
# This server block will handle all SSL connections
# We use dynamic certificate paths from the map
ssl_certificate $ssl_certificate_by_domain;
ssl_certificate_key $ssl_certificate_key_by_domain;
# Fallback for SNI (Server Name Indication) when no matching server_name is found
# This helps in serving a default certificate when SNI is not provided or
# when the domain doesn't match an explicit server_name.
# ssl_reject_handshake off; # Keep this off here, as we are serving a certificate
# Add debug headers to see which certificate is being used
add_header X-SSL-Certificate-Path $ssl_certificate_by_domain;
add_header X-SSL-Key-Path $ssl_certificate_key_by_domain;
add_header X-Host $host;
add_header X-HTTP-Host $http_host;
add_header X-Has-Certificate $has_valid_certificate;
# Log domain access for certificate generation
access_log /etc/nginx/logs/domains.log domain_logger;
# Add SSL debug logging
access_log /etc/nginx/logs/ssl_debug.log ssl_debug;
location / {
proxy_pass http://app:5555;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host; # Use $http_host for consistency
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
# Handle all domains on HTTP - used for certificate challenges and for domains without certificates
server {
listen 80 default_server; # Catch-all for HTTP
listen [::]:80 default_server; # IPv6
# Log domain access for certificate generation
access_log /etc/nginx/logs/domains.log domain_logger;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
allow all; # Ensure certbot can access this
}
# For domains with certificates, redirect to HTTPS
if ($has_valid_certificate = 1) {
return 301 https://$host$request_uri;
}
# For domains without certificates, show a message or forward to app
location / {
# Option 1: Show a custom message that HTTPS is not yet available
add_header Content-Type text/html;
return 200 '<html><body><h1>Secure connection not yet available</h1><p>Please try again in a few minutes while we set up SSL for this domain.</p></body></html>';
# Option 2: Forward to app (uncomment if you prefer this)
# proxy_pass http://app:5555;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header Host $http_host;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_redirect off;
}
}
The problem I encountered is that Nginix serves the default self-signed certificate even though the domain is available in the mapping:
map $http_host $ssl_certificate_by_domain {
default /etc/nginx/ssl/default.crt;
domainandco.com /etc/letsencrypt/live/domainandco.com/fullchain.pem;
www.domainandco.com /etc/letsencrypt/live/domainandco.com/fullchain.pem;
isa7.com /etc/letsencrypt/live/isa7.com/fullchain.pem;
www.isa7.com /etc/letsencrypt/live/isa7.com/fullchain.pem;
}
map $http_host $ssl_certificate_key_by_domain {
default /etc/nginx/ssl/default.key;
domainandco.com /etc/letsencrypt/live/domainandco.com/privkey.pem;
www.domainandco.com /etc/letsencrypt/live/domainandco.com/privkey.pem;
isa7.com /etc/letsencrypt/live/isa7.com/privkey.pem;
www.isa7.com /etc/letsencrypt/live/isa7.com/privkey.pem;
}
Why does Nginx serve the default certificate instead of the right one for the domain?