IPv6 Users Can't Reach Site: AAAA Record Missing or Broken

IPv4 users reach the site fine but IPv6 users get timeouts. Either AAAA is absent, points to a dead address, or your firewall blocks v6. Fix dual-stack.

A user from a mobile carrier or a modern residential ISP reports your site won’t load — connection times out at TCP level. You test from your laptop on cable internet and it loads instantly. The difference is IPv6: mobile carriers (T-Mobile, Verizon Wireless, Vodafone, many APAC carriers) deliver IPv6-only or IPv6-preferred connectivity, and Happy Eyeballs in the browser tries the AAAA record first. If your AAAA points to a dead host, doesn’t exist at all on a CDN-fronted setup that expects it, or your origin’s firewall drops IPv6 packets, that user waits the full TCP timeout (often 75-120s) before maybe falling back to IPv4 — by which time they’ve closed the tab. This is invisible to most monitoring because IPv4 health checks pass.

Common causes

Ordered by what causes the most observable user pain.

1. AAAA record exists but points at an IP that no longer hosts you

You used to host on a server with both A and AAAA. You migrated to a new host (Vercel, Netlify, a different VPS) and updated the A record but forgot AAAA. The old IPv6 address is dead or now belongs to someone else.

How to spot it: dig AAAA yourdomain.com +short returns an address, but curl -6 -I https://yourdomain.com times out or returns wrong content.

2. CDN front exposes IPv4 only, AAAA pointing past it

Your CDN (older Cloudflare config, certain Bunny setups, custom edge) only returns IPv4 in its IP list, but you manually added an AAAA pointing at your origin to “help IPv6 users”. Those users bypass the CDN entirely and hit a misconfigured / unreachable origin.

How to spot it: Your A record returns CDN IPs. Your AAAA returns origin IP. curl -6 hits the origin directly without CDN protection.

3. Origin firewall drops IPv6 packets

ip6tables (or the cloud provider’s security group) is not configured. The default deny rule blocks inbound IPv6 on :443. Origin is technically dual-stack-capable but no one can reach it over v6.

How to spot it: sudo ip6tables -L INPUT -v -n shows no ACCEPT rule for :443 over v6. From outside: nc -6 -zv yourdomain.com 443 hangs.

4. Reverse proxy / nginx not listening on v6

Even if the OS firewall is open, nginx defaults to listen 443 ssl; which is IPv4-only. You need listen [::]:443 ssl; explicitly.

How to spot it: ss -tlnp | grep :443 shows 0.0.0.0:443 but no [::]:443. Or shows both but only IPv4 is bound.

5. Happy Eyeballs falls back, but the AAAA TTL is too high so it stays cached

Even after you remove a bad AAAA, browsers cache it for TTL duration (often 4-24h). Users keep hitting v6 first, timing out, falling back to v4 — slow site experience for hours after the “fix”.

How to spot it: User says “the site eventually loads but takes 30s every time”. Browser DevTools timing shows long DNS / connection delay before TCP success.

6. ISP NAT64 / CLAT introduces SSL SNI issues

Mobile carriers using NAT64 (no native v4 to the device) synthesize AAAA from your A record. If your SNI / Host header logic depends on the original source family, it breaks under NAT64.

How to spot it: Your A is fine, you have no AAAA, but mobile carrier users still complain. SNI gets a well-known-ipv6-pref synthetic. Check carrier-specific.

Before you start

  • Identify which users / regions are affected — mobile carriers vs residential vs corporate networks all behave differently.
  • Confirm your hosting platform’s IPv6 stance: does it expose AAAA? Cloudflare yes by default, raw VPS depends on config.
  • Have an IPv6-capable client to test with. test-ipv6.com confirms your client has working v6.
  • Know your origin server’s IPv6 address (cloud provider control panel shows it).

Information to collect

  • Output of dig A yourdomain.com +short and dig AAAA yourdomain.com +short.
  • Output of curl -4 -I https://yourdomain.com AND curl -6 -I https://yourdomain.com.
  • Output of ss -tlnp | grep :443 on the origin.
  • Output of ip -6 addr show on the origin (shows assigned v6 addresses).
  • A test from an IPv6-only / IPv6-preferred network — mobile data is the easiest source.

Step-by-step fix

Order: kill the bad AAAA fastest, then add proper dual-stack.

Step 1: Test both stacks from outside

curl -4 -v https://yourdomain.com 2>&1 | head -20
curl -6 -v https://yourdomain.com 2>&1 | head -20

If -4 succeeds and -6 hangs or fails, the diagnosis is confirmed. Note WHERE -6 fails: at TCP connect, at TLS handshake, at HTTP response — each implies a different layer broken.

Step 2: Remove a dead AAAA record

If dig AAAA yourdomain.com returns an address that doesn’t respond:

  1. DNS dashboard → find the AAAA record.
  2. Delete it (or update to the correct address).
  3. Lower the TTL of nearby records to 300s temporarily so future changes propagate fast.

Browsers and resolvers will start ignoring v6 for your domain within the old TTL. Happy Eyeballs falls back to v4 cleanly.

Step 3: For dual-stack origins, ensure the server listens on v6

Edit /etc/nginx/sites-available/yourdomain (or wherever):

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name yourdomain.com;
    # ...
}

The [::]:443 line binds IPv6. Restart:

sudo nginx -t && sudo systemctl reload nginx
ss -tlnp | grep :443

Output should now show both 0.0.0.0:443 and :::443.

Step 4: Open IPv6 firewall

sudo ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo ip6tables-save | sudo tee /etc/ip6tables.rules

If you use ufw:

sudo ufw allow 443/tcp
sudo ufw allow 80/tcp

ufw applies to both v4 and v6 by default. Verify:

sudo ip6tables -L INPUT -v -n | grep 443

In cloud provider control panels (AWS Security Groups, GCP firewall rules, DigitalOcean Cloud Firewalls), explicitly add IPv6 source ::/0 for ports 80/443 — many UIs default the source to 0.0.0.0/0 only.

Step 5: Add the correct AAAA record

After verifying the origin accepts v6, get the address:

ip -6 addr show | grep "scope global"

Take the non-temporary, non-link-local address (typically the one without temporary flag). Add it as an AAAA record in DNS with TTL 300 initially:

yourdomain.com.  300  IN  AAAA  2001:db8::1

Test:

dig AAAA yourdomain.com @1.1.1.1 +short
curl -6 -I https://yourdomain.com

Step 6: If using a CDN, prefer the CDN’s IPv6 over manual AAAA

Cloudflare proxied records auto-publish both A and AAAA pointing at Cloudflare edge — never manually add AAAA pointing past Cloudflare for a proxied record. For Vercel / Netlify, check their docs; some publish AAAA automatically, some don’t, and adding your own creates a split.

Verify

  • dig AAAA yourdomain.com +short returns the correct address (or nothing, if intentionally v4-only).
  • curl -6 -I https://yourdomain.com returns HTTP/2 200 or expected response, not timeout.
  • test-ipv6.com (from a network) shows site as v6-reachable.
  • An IPv6-only test from a mobile network (cellular data, hotspot to laptop) loads the site without delay.
  • ss -tlnp on the origin shows both v4 and v6 listeners on 443.

Long-term prevention

  • Add IPv6 health checks to your monitoring (UptimeRobot, Pingdom, Better Stack all support v6 probes — must be explicitly enabled).
  • When migrating hosts, update BOTH A and AAAA records as a single change; don’t leave stale AAAA pointing to an old VPS.
  • Standardize nginx / haproxy / caddy templates to always include [::]:443 from day one.
  • Test from a mobile carrier monthly — most IPv6 issues only show up there.
  • Document in your runbook: every public-facing service must have a working AAAA or no AAAA, never a broken one.

Common pitfalls

  • Leaving a dead AAAA in place because “v4 still works” — you are silently degrading the experience of any user on a v6-preferring network.
  • Adding AAAA but forgetting to open the v6 firewall — record points to a host that drops packets.
  • Using 0.0.0.0/0 everywhere and assuming it covers v6 too — most firewalls treat v4 and v6 as separate rule sets.
  • Setting AAAA TTL to 86400 then trying to fix a mistake — users keep the bad record for a day.
  • Letting cloud provider auto-assign a temporary v6 (privacy address) and adding it to DNS — it changes on reboot.

FAQ

Q: Should I support IPv6 in 2026 even though my users are mostly desktop?

Yes. Mobile traffic is often majority IPv6 even when desktops aren’t. A broken AAAA actively harms users; no AAAA at all is acceptable (Happy Eyeballs handles fallback cleanly within ~300ms in modern browsers).

Q: Can I just delete all AAAA records and forget about IPv6?

Yes, that’s a valid stance. With no AAAA, all clients fall back to IPv4 immediately. The bad failure mode is having an AAAA that doesn’t work, not having none.

Q: My origin is on AWS — does it get IPv6 by default?

Only if you explicitly enable dual-stack on the VPC and assign a v6 address to the instance. Older AWS accounts in legacy VPCs are v4-only by default. Check VPC and subnet settings.

Q: Does Cloudflare orange-cloud automatically give me IPv6?

Yes — proxied records publish both Cloudflare’s IPv4 and IPv6 anycast addresses regardless of your origin’s stack. This is the easiest way to “support IPv6” without touching your origin.

See also A vs CNAME confusion, DNS changed site still down, and subdomain not resolving.

Tags: #Troubleshooting #DNS #ipv6 #Debug #Networking