DNS Best Practices: Should You Include the Trailing Dot in Domain Names?


1 views

html

When working with DNS configurations, you'll frequently encounter domain names ending with a dot (e.g., example.com.). This trailing dot isn't a bug - it's actually a critical DNS specification that indicates a fully qualified domain name (FQDN).

The trailing dot tells DNS resolvers that this is an absolute domain name that shouldn't be modified:

# With trailing dot (FQDN)
dig example.com. +short

# Without trailing dot (may be treated as relative)
dig example.com +short

In most modern DNS implementations, the resolver will append your search domains if you omit the dot. For example, if your search domain is corp.local, querying server1 might resolve to server1.corp.local.

These scenarios require FQDNs with trailing dots:

  • DNS zone files (SOA, NS, MX records)
  • CNAME records pointing to external domains
  • Any DNS configuration where absolute resolution is needed
; Example zone file snippet
@       IN SOA  ns1.example.com. hostmaster.example.com. (
                2023081501 ; serial
                3600       ; refresh
                900        ; retry
                604800     ; expire
                86400 )    ; minimum

        IN NS   ns1.example.com.
        IN NS   ns2.example.com.
www     IN CNAME example.com.

When working with DNS programmatically, you should:

  1. Always use FQDNs in DNS server configurations
  2. Handle both formats in application code (with and without dots)
  3. Be consistent in your own configurations

Here's how you might handle DNS names in Python:

import socket

def ensure_fqdn(hostname):
    if not hostname.endswith('.'):
        return f"{hostname}."
    return hostname

def resolve_host(hostname):
    fqdn = ensure_fqdn(hostname)
    try:
        return socket.gethostbyname(fqdn)
    except socket.gaierror:
        return socket.gethostbyname(hostname)

Major cloud providers handle this differently:

Provider Trailing Dot Behavior
AWS Route53 Requires FQDN for alias targets
Google Cloud DNS Automatically adds if missing
Azure DNS Works with both formats

For AWS Route53 alias records, you must include the trailing dot when pointing to AWS resources:

# Terraform example showing required FQDN
resource "aws_route53_record" "www" {
  zone_id = aws_route53_zone.primary.zone_id
  name    = "www.example.com"
  type    = "A"

  alias {
    name                   = "d123.cloudfront.net." # Note the dot
    zone_id                = "Z2FDTNDATAQYW2"
    evaluate_target_health = true
  }
}

In DNS configuration files and API responses (like AWS Route53's output), you'll often see domain names ending with a dot (e.g., example.com.). This isn't a bug - it's actually the fully qualified domain name (FQDN) notation.

The trailing dot represents the DNS root zone. When present, it tells the resolver this is an absolute domain name that shouldn't be modified:

# Without trailing dot (relative)
example.com
→ Resolver might append search domains: example.com.searchdomain.local

# With trailing dot (absolute)
example.com.
→ Always resolves exactly as written

Best practice dictates you should:

  • Include the dot in DNS configuration files (zone files, named.conf)
  • Include the dot when specifying nameservers
  • Omit the dot in browser URLs and most application configurations

Here's how different systems handle it:

# BIND Zone File Example
@ IN SOA ns1.example.com. hostmaster.example.com. (
  2023081501 ; serial
  3600       ; refresh
  900        ; retry
  604800     ; expire
  86400 )    ; minimum

# AWS CLI Response (Route53)
{
  "NameServers": [
    "ns-123.awsdns-45.com.",
    "ns-123.awsdns-45.net."
  ]
}

Watch for these scenarios:

  • DNS propagation tests failing due to missing trailing dots
  • API calls returning different results with/without dots
  • Inconsistent behavior between DNS providers

When working with DNS programmatically:

# Python example for handling FQDNs
import dns.resolver

def resolve_fqdn(domain):
    if not domain.endswith('.'):
        domain = f"{domain}."
    answers = dns.resolver.resolve(domain, 'A')
    return [str(r) for r in answers]