Point a Domain at Your Django Server (DNS Setup)

Connect a domain to a Django server: A and AAAA records, www and apex, CNAMEs for platforms, TTLs, verifying propagation with dig, and matching ALLOWED_HOSTS and certificates.

Problem statement

The Django app runs on the server and responds on its IP address, but example.com does not reach it, reaches an old server, works without www but not with it, or shows a certificate error. DNS is the step between "the server works" and "the site works", and it is easy to get subtly wrong because changes take time to propagate and different resolvers can give different answers.

This guide covers the records a Django deployment needs, how to verify them precisely, and the three places in your stack that must agree with DNS: the web server's server_name, Django's ALLOWED_HOSTS, and the TLS certificate.

From domain name to DjangoDNS recordA, AAAA, CNAMEServer IPports 80 and 443Nginxmatching server_nameDjangoALLOWED_HOSTSDNS, Nginx server_name, ALLOWED_HOSTS, and the certificate must all list the same hostnames.
  1. For a VPS with a fixed IP, create an A record for example.com pointing at the IPv4 address, and an AAAA record if the server has IPv6.
  2. Create www.example.com as a CNAME to example.com (or its own A/AAAA records).
  3. For a PaaS, use the CNAME target the platform gives you; for the apex domain, use the provider's ALIAS/ANAME/CNAME flattening or the platform's A records.
  4. Lower the TTL (for example to 300 seconds) a day before moving a live domain.
  5. Verify with dig +short example.com A against public resolvers and the authoritative nameserver.
  6. Add both hostnames to Nginx server_name, Django ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS, and the certificate.

Step-by-step solution

1. Find where DNS is managed

The registrar sells the domain; the DNS provider answers queries for it. They may be the same company or not. Check which nameservers are authoritative:

dig +short NS example.com

Edit records at the provider those nameservers belong to. Changes made at a registrar whose nameservers are not in use have no effect, which is a common source of confusion.

2. Create the records

For a server at 203.0.113.10 with IPv6 2001:db8::10:

NameTypeValueTTL
example.com (often @)A203.0.113.10300
example.comAAAA2001:db8::10300
wwwCNAMEexample.com.300
the records you needValueNoteA example.com203.0.113.10IPv4 of the serverAAAA example.com2001:db8::10!only if IPv6 works end to endCNAME wwwexample.com.follows the apexCAA example.com0 issue "letsencrypt.org"limits who may issue certsA stale AAAA record is worse than nonedjango-deployment.com
The apex cannot be a CNAME at most DNS providers.

Only publish an AAAA record if the server really accepts connections on that IPv6 address (and Nginx has listen [::]:80; and listen [::]:443 ssl;). A wrong or unreachable AAAA record breaks the site for IPv6 users while it works for you on IPv4.

A CNAME cannot coexist with other records of the same name, which is why the apex (example.com) cannot be a plain CNAME. Platforms that give a hostname target (Render, Heroku, Fly.io, App Platform, and others) document either A records for the apex or a provider feature such as ALIAS, ANAME, or CNAME flattening.

3. Understand TTLs before changing live records

TTL is how long resolvers may cache an answer. If a record has a TTL of 86400 (one day), some visitors can keep reaching the old server for a day after you change it. Before a migration:

  1. Lower the TTL to 300 at least one old-TTL period ahead.
  2. Change the record.
  3. Keep the old server running (or redirecting) until traffic stops.
  4. Raise the TTL again once stable.
the TTL is how long mistakes lastTTL 86400 (1 day)up to 24 hTTL 3600 (1 hour)up to 1 hTTL 300 (5 min)up to 5 min0 min720 min1440 minminutesLower the TTL a full old-TTL period before changing a live recorddjango-deployment.com
Raise it again once the change has settled.

4. Verify precisely

Query the authoritative nameserver first; it shows what you configured, with no caching:

NS=$(dig +short NS example.com | head -1)
dig +short @"$NS" example.com A
dig +short @"$NS" example.com AAAA
dig +short @"$NS" www.example.com
ask the authoritative server directlydig +shortNSexample.comdig +short A example.com@ns1.provider.net203.0.113.10dig +short A example.com@1.1.1.11231Which provider is really authoritative.2The source of truth, no caches.3What a public resolver currently returns.If the authoritative answer is right, the rest is waiting for TTLsdjango-deployment.com
Records edited at the wrong provider are a common first-time mistake.

Then check public resolvers, which show what visitors get:

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

Test the server directly before DNS is updated, by forcing the resolution in curl:

curl -sI --resolve example.com:80:203.0.113.10 http://example.com/ | head -1

--resolve sends the right Host header to the chosen IP, which exercises Nginx server_name and Django ALLOWED_HOSTS exactly as real traffic will.

5. Align Nginx, Django, and TLS

Nginx:

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

Django:

ALLOWED_HOSTS = ["example.com", "www.example.com"]
CSRF_TRUSTED_ORIGINS = ["https://example.com", "https://www.example.com"]

Certificate (once DNS resolves to the server, since the HTTP challenge connects to it):

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

Pick one canonical hostname and redirect the other to it, so cookies, analytics, and search engines see one site:

server {
    listen 443 ssl;
    server_name www.example.com;
    # ssl_certificate lines
    return 301 https://example.com$request_uri;
}

6. Email and other records

Adding web records does not affect email as long as you do not delete existing MX, TXT (SPF, DKIM, DMARC), or verification records. When moving DNS to a new provider, copy every record from the old zone first, not only the web ones.

Verification check:

dig +short example.com A
dig +short www.example.com
curl -sI https://example.com/ | head -1
curl -sI https://www.example.com/ | grep -i location

Expect your server's IP, the www alias, a 200 for the canonical host, and a single redirect from the other.

Explanation

When a browser opens https://example.com/, it resolves the name to an IP, connects to port 443, and sends the hostname twice: in the TLS handshake (SNI), to select the certificate, and in the HTTP Host header, to select the Nginx server block and pass Django's ALLOWED_HOSTS check. That is why a hostname has to be present in DNS, in the certificate, in server_name, and in ALLOWED_HOSTS. Missing it from any one of them produces a different error: no resolution, a certificate warning, the default Nginx page, or a 400 Bad Request.

Edge cases / notes

Proxied DNS

With Cloudflare's proxy (orange cloud) or a similar service, DNS returns the proxy's IPs, not yours. dig then shows Cloudflare addresses, which is expected. Certificate issuance and real client IP handling change accordingly.

Stale local caches

Your laptop, router, or ISP may cache old answers. Test with dig @1.1.1.1 or from another network before concluding that DNS is wrong. On Linux with systemd-resolved, resolvectl flush-caches clears the local cache.

Rollback

Keep the old record values written down. Rolling back a DNS change is changing the record back, and it takes effect after the TTL you had set, which is another reason to lower TTLs before migrations.

When to automate this

DNS records can be managed as code with Terraform or the provider's API. That makes environment setup repeatable and gives you history for every change, which matters once several people touch DNS.

Next steps

FAQ

How long does DNS propagation take?

Changes are visible at the authoritative nameserver immediately. Resolvers see them when their cached answer expires, which depends on the previous TTL, typically minutes to a day.

Should www be a CNAME or an A record?

Either works. A CNAME to the apex means you only update one IP when the server changes.

Why does my domain work but www does not?

www has no DNS record, is missing from server_name or ALLOWED_HOSTS, or is not on the certificate. Check each with dig, nginx -T, Django settings, and certbot certificates.

Can I point the apex domain at a Heroku or Render hostname?

Not with a plain CNAME. Use your DNS provider's ALIAS, ANAME, or CNAME flattening, or the IP addresses the platform documents for apex domains.

Learning path · step 6 of 11

Your first production deployment on a VPS

Keep going