Domain and DNS Configuration

The essential playbook for implementing domain and dns configuration in your SaaS.

Configure your domain before pointing production traffic at your app. For a small SaaS, this usually means setting correct A/AAAA or CNAME records, deciding whether www or apex is canonical, avoiding DNS mistakes that break HTTPS, and verifying requests reach Nginx or your platform correctly.

Quick Fix / Quick Setup

Use this baseline setup for a single VPS behind Nginx:

txt
# Example DNS plan
# Apex/root domain -> server public IP
A     example.com      203.0.113.10

# Optional IPv6
AAAA  example.com      2001:db8::10

# www -> apex
CNAME www              example.com

# API subdomain -> same server or separate service
A     api.example.com  203.0.113.10

# Mail provider examples (set at your DNS host)
MX    example.com      10 mail.provider.com
TXT   example.com      "v=spf1 include:_spf.provider.com ~all"
TXT   default._domainkey "k=rsa; p=PUBLIC_KEY"
TXT   _dmarc           "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

# Verify DNS from terminal
dig example.com A +short
dig www.example.com CNAME +short
dig api.example.com A +short

# Verify traffic reaches server
curl -I http://example.com
curl -I http://www.example.com

# Nginx server_name should match
server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
    }
}

Use A records for root/apex domains and CNAME for subdomains like www when possible. Do DNS first, confirm records resolve, then issue HTTPS certificates.

What’s happening

  • DNS maps your domain name to the IP address or hostname that serves your app.
  • Your registrar controls domain ownership, but DNS may be hosted at the registrar or at a separate provider such as Cloudflare, Route 53, or DigitalOcean.
  • Apex/root domains usually use A or AAAA records. Subdomains often use CNAME.
  • If DNS points to the wrong target, your app may return 404, timeout, show SSL failures, or serve another app on the same server.
  • DNS changes are cached. Old values may persist until TTL expires.
  • DNS only gets traffic to the right destination. Your web server still needs to match the requested hostname.
  • Browser
    DNS provider
    Server IP
    Nginx
    Gunicorn/Uvicorn/App

    request flow diagram showing Browser -> DNS provider -> Server IP -> Nginx -> Gunicorn/Uvicorn/App.

Step-by-step implementation

1. Decide your canonical hostname

Pick one primary host:

  • example.com
  • www.example.com

For most small SaaS apps:

  • use apex for branding simplicity
  • redirect www to apex
  • or choose www if your CDN or DNS provider works better with it

Be consistent. One host should be canonical.

2. Confirm where DNS is actually hosted

Do not assume your registrar hosts DNS.

Check authoritative nameservers:

bash
dig NS example.com +short

If the nameservers point to Cloudflare, Route 53, or another provider, make changes there, not only at the registrar dashboard.

3. Collect your deployment target

You need one of these:

  • VPS public IPv4
  • VPS public IPv6
  • load balancer hostname
  • platform target hostname
  • CDN/proxy origin setup

For a VPS:

bash
curl ifconfig.me
ip -6 addr

Only publish an AAAA record if your server is reachable over IPv6.

4. Create DNS records

Typical small SaaS setup:

txt
A     example.com      203.0.113.10
CNAME www              example.com
A     api.example.com  203.0.113.10

If IPv6 is correctly configured:

txt
AAAA  example.com      2001:db8::10

Set low TTL during setup:

txt
TTL = 300

Do not create conflicting records for the same name and type.

Bad example:

txt
A      example.com  203.0.113.10
A      example.com  203.0.113.20
CNAME  example.com  target.provider.com

Unless you intentionally want multiple A records, remove stale values.

5. Verify propagation

Check records from your local resolver and public resolvers:

bash
dig example.com A +short
dig example.com AAAA +short
dig www.example.com CNAME +short
dig api.example.com A +short

dig @1.1.1.1 example.com A +short
dig @8.8.8.8 example.com A +short

nslookup example.com
host example.com

If public resolvers return the expected values, DNS is usually correct even if your laptop still shows an older result.

6. Align Nginx with DNS hostnames

DNS alone is not enough. Nginx must match the hostnames you plan to serve.

Example Nginx server block:

nginx
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Test config:

bash
sudo nginx -t
sudo systemctl reload nginx

If you run multiple apps on one VPS, make sure each app has a precise server_name. Avoid overly broad defaults.

Related deployment guides:

7. Verify host-based routing before HTTPS

Test via the public domain:

bash
curl -I http://example.com
curl -I http://www.example.com

Test directly against the server IP using the Host header:

bash
curl -H 'Host: example.com' http://203.0.113.10
curl -H 'Host: www.example.com' http://203.0.113.10

This isolates Nginx routing from DNS.

If direct IP + Host works but the domain does not, the problem is usually DNS, caching, or a proxy layer.

8. Update framework host validation

Some frameworks reject unknown hosts.

Examples:

Django

python
ALLOWED_HOSTS = ["example.com", "www.example.com", "api.example.com"]

FastAPI / Starlette trusted hosts

python
from starlette.middleware.trustedhost import TrustedHostMiddleware

app.add_middleware(
    TrustedHostMiddleware,
    allowed_hosts=["example.com", "www.example.com", "api.example.com"]
)

Flask behind proxy

Make sure host and proxy headers are handled correctly if you enforce host validation or URL generation.

9. Issue HTTPS only after DNS is correct

Once:

  • DNS resolves to the right target
  • Nginx routes the hostname correctly
  • port 80 is open

Issue certificates.

If you use Let’s Encrypt with Nginx:

bash
sudo certbot --nginx -d example.com -d www.example.com

Then verify:

bash
curl -k -I https://example.com
curl -k -I https://www.example.com

For the full TLS setup, see:

10. Add canonical redirects

If apex is primary:

nginx
server {
    listen 80;
    server_name www.example.com;
    return 301 http://example.com$request_uri;
}

After HTTPS is active:

nginx
server {
    listen 443 ssl http2;
    server_name www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    return 301 https://example.com$request_uri;
}

Also redirect HTTP to HTTPS for the canonical host.

11. Add email-related DNS separately

Do not mix app DNS and mail DNS without documenting both.

Typical mail records:

txt
MX    example.com      10 mail.provider.com
TXT   example.com      "v=spf1 include:_spf.provider.com ~all"
TXT   default._domainkey "k=rsa; p=PUBLIC_KEY"
TXT   _dmarc           "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

If you send transactional email from your app, keep these records documented for future migrations.

12. Document the final DNS plan

Store:

  • registrar
  • nameservers
  • DNS provider
  • canonical host
  • all records
  • server IPs
  • TLS issuance method
  • redirect rules

This avoids breakage during server moves, DNS provider migrations, or team handoff.

Common causes

  • A record points to the wrong server IP
  • DNS changes were made at the registrar while authoritative DNS is hosted elsewhere
  • Old conflicting records still exist for the same hostname
  • Apex domain was configured with an unsupported CNAME at the DNS provider
  • AAAA record exists but the server is not reachable over IPv6
  • Nginx server_name does not include the requested domain
  • Framework host validation blocks the domain
  • Cloudflare or another proxy is enabled before origin routing works
  • Firewall blocks port 80 or 443
  • HTTPS certificate was requested before DNS resolved correctly

Debugging tips

Start from DNS authority, then resolver output, then network reachability, then web server routing, then TLS.

Check authoritative nameservers

bash
dig NS example.com +short

If these are not the provider you edited, you changed the wrong DNS zone.

Check record resolution

bash
dig example.com A +short
dig example.com AAAA +short
dig www.example.com CNAME +short
dig api.example.com A +short
dig @1.1.1.1 example.com A +short
dig @8.8.8.8 example.com A +short

Check reachability

bash
ping example.com
traceroute example.com
sudo ss -tulpn | grep -E ':80|:443'

If the server is not listening on 80 or 443, DNS is not the main issue.

Test HTTP routing

bash
curl -I http://example.com
curl -I http://www.example.com
curl -H 'Host: example.com' http://203.0.113.10

Test Nginx config

bash
sudo nginx -t
sudo systemctl reload nginx

TLS-specific checks

bash
curl -k -I https://example.com

If HTTPS fails right after DNS setup:

  • confirm records resolve publicly
  • confirm the certificate covers the exact hostname
  • confirm any proxy/CDN SSL mode matches your origin
  • confirm 80 is open if using HTTP challenge validation
Domain
DNS record
Resolver
Server reachability
Nginx host routing
TLS

Process Flow

Checklist

  • Registrar access confirmed
  • Authoritative nameservers identified
  • DNS hosted in the expected provider
  • Apex/root record created
  • www record created
  • Required subdomains added
  • TTL reduced during setup
  • No conflicting duplicate records
  • Server public IP verified
  • Nginx server_name matches all domains
  • Framework allowed-host settings updated
  • Port 80 and 443 open in firewall
  • HTTP routing verified
  • HTTPS issued after DNS validation
  • Canonical redirect configured
  • Mail DNS records configured if needed
  • DNS records documented for future migrations

For broader release validation, use:

Related guides

FAQ

Should I use apex or www as the main domain?

Either works. For small SaaS apps, apex is simpler for branding, while www can be easier with some DNS and CDN setups. Pick one canonical host and redirect the other.

Why does my domain resolve on one machine but not another?

DNS is cached by browsers, operating systems, ISPs, and recursive resolvers. Wait for TTL expiry or test against public resolvers like 1.1.1.1 and 8.8.8.8.

Can I use a CNAME on the root domain?

Usually no, unless your DNS provider supports ALIAS, ANAME, or CNAME flattening behavior at the apex. Otherwise use A/AAAA records.

When should I enable HTTPS?

After DNS resolves to the correct origin and your web server responds for the intended hostnames. Then issue certificates and add redirects.

Do I need separate domains for app and API?

Not always. You can serve both from one domain with path routing, or split them into subdomains like app.example.com and api.example.com if that fits your deployment and CORS model better.

Final takeaway

Good domain setup is mostly about consistency:

  • correct DNS records
  • correct Nginx host routing
  • correct TLS order

Do DNS first, verify resolution, confirm the app answers the right Host header, then enable HTTPS and redirects.

Most production domain issues come from:

  • editing the wrong DNS zone
  • conflicting records
  • mismatched server_name settings
  • publishing AAAA when IPv6 is not actually working
  • enabling a proxy before the origin is correct

If you validate each layer in order, domain setup is straightforward and repeatable.