By David Kim · Oct 23, 2026

How I Stopped IPv6 Downtime From Wrecking My Server (A Practical Prevention Guide)

If you read my previous post about IPv6-only users and hidden downtime, you know the backstory. I ran a game server community site for years. One day I found out a huge chunk of mobile users on T-Mobile and Jio couldn't even load the page. My monitoring said 100% uptime. My browser showed a perfectly working website. But for people on IPv6-only networks, the site was dead.

That discovery was a gut punch. I had been running a site that was broken for millions of potential visitors and had zero idea. After fixing the immediate problem (a stale AAAA record from a server migration I did months earlier), I sat down and asked myself: how do I make sure this never happens again?

This post is the answer. Everything I actually did, step by step, to build a system that catches IPv6 problems before my users do. No theory. No "you should consider." Just the stuff I put in place and why it works.

The First Thing I Changed: My Mental Model

Before the IPv6 disaster, I thought of my server as one thing. One IP, one config, one set of firewall rules. If the site loaded, the site worked. Simple.

That mental model is wrong. And it's the reason I missed the problem for months.

Your server exists in two parallel worlds: IPv4 and IPv6. Different DNS records. Different firewall rule sets. Different network paths. Different listener configs. A change that fixes something in one world can break the other, and you will not see it unless you are explicitly looking at both.

Once I internalized that, the prevention steps became obvious. I needed to treat IPv4 and IPv6 as two separate systems that happen to share the same box. Every check, every change, every deploy needed to account for both.

Step 1: I Audited Every DNS Record I Own

The original problem was a stale AAAA record. So I started there. I pulled up every domain and subdomain I manage and ran two commands for each one:

dig A mydomain.com +short
dig AAAA mydomain.com +short

For my main domain, the A record pointed to my current server. Good. The AAAA record? It pointed to an IP that belonged to my old VPS. The one I decommissioned three months ago. That was the bug that started this whole journey.

But the audit found more problems. My API subdomain had no AAAA record at all. My CDN-fronted assets domain had both records, but the AAAA pointed to a Cloudflare IP that was different from what Cloudflare currently assigned. And a staging subdomain I forgot about still had AAAA records pointing to a development machine that had been wiped.

What I did about it

For every domain where I intentionally support IPv6, I made sure the AAAA record pointed to the correct, current IPv6 address. For domains where I don't need IPv6 (like internal staging), I removed the AAAA record entirely. That way, IPv6-only users get routed through NAT64 cleanly instead of trying to connect to a dead address.

I wrote all of this down in a spreadsheet. Domain, A record value, AAAA record value, date last verified. It takes 10 minutes to check every quarter. I set a calendar reminder.

The trap I almost fell into

My first instinct was to just remove all AAAA records and let NAT64 handle everything. That would technically work, but it's a bad long-term plan. NAT64 adds latency. It creates a dependency on the carrier's translation gateway. And if that gateway is overloaded (which happens), your site is unreachable for IPv6-only users even though your server is fine. Proper dual-stack with correct AAAA records is better.

Step 2: I Fixed My Firewall (Both of Them)

This was embarrassing to discover. I had spent hours configuring iptables rules on my server. Allow 80, 443, SSH. Drop everything else. Rate limiting on SSH. All the standard stuff.

Then I ran ip6tables -L and saw the default policy: ACCEPT on everything. My IPv6 firewall was wide open. Every port, every protocol, no restrictions. I had essentially been running a firewall on one door while leaving the other door propped open with a brick.

What I did about it

I mirrored my iptables rules into ip6tables. Same allowed ports, same drop policy, same rate limiting. But with one important addition: I made sure ICMPv6 was allowed.

This is a mistake I see people make all the time. In IPv4, blocking ICMP is a common (if debatable) security practice. In IPv6, blocking ICMPv6 breaks things. Hard. IPv6 relies on ICMPv6 for Path MTU Discovery and Neighbor Discovery. Block those and your large HTTP responses fail while small health check responses succeed. Your monitoring says "up" while real pages with images and scripts fail to load.

The specific ICMPv6 types I allow:

  • Type 1: Destination Unreachable
  • Type 2: Packet Too Big (this is the one that breaks everything if blocked)
  • Type 3: Time Exceeded
  • Type 4: Parameter Problem
  • Type 128: Echo Request (ping6)
  • Type 129: Echo Reply

After making the switch, I actually moved to nftables instead of managing two separate rule sets. nftables handles both IPv4 and IPv6 in one config. One file, one set of rules, both protocols. Should have done it years ago.

Step 3: I Made Sure Nginx Actually Listens on IPv6

My Nginx config had this:

listen 80;
listen 443 ssl;

That only binds to IPv4. On most default Nginx installs, this might still work for IPv6 because of how the kernel handles dual-stack sockets. But on my setup, where I had manually configured virtual hosts and was running inside containers, IPv6 was not getting through.

I tested it by running curl -6 https://mydomain.com from a different machine. Connection refused. The server had an IPv6 address, DNS resolved correctly, the firewall allowed traffic, but Nginx was not listening.

What I did about it

I added the IPv6 listen directives to every server block:

listen [::]:80;
listen [::]:443 ssl;

After reloading Nginx, curl -6 worked immediately. The fix took 30 seconds. The problem had existed for months.

If you use Apache instead, the same applies. Make sure you have Listen [::]:80 and Listen [::]:443 in your config. Some Apache setups handle this automatically, some don't. Check. Don't assume.

Step 4: I Checked My CDN and API Subdomains

My main domain goes through Cloudflare, which handles IPv6 automatically. Great. But my API runs on a subdomain that points directly to my origin server. And my asset storage subdomain points to a different server entirely.

Cloudflare was handling IPv6 for mydomain.com. But api.mydomain.com had no IPv6 support at all. Anyone on an IPv6-only network hitting the API got routed through NAT64. When NAT64 worked, they got extra latency. When it didn't (carrier gateway overload during peak hours), API calls just timed out.

What I did about it

I added proper AAAA records for the API subdomain and configured the origin server to accept IPv6 traffic directly. I also put the API behind Cloudflare's proxy, which gives it automatic IPv6 support and the added benefit of DDoS protection.

For the asset storage subdomain, I verified that the storage provider supported IPv6 and that the AAAA record was correct. It was, but only because the provider had set it up by default. I had never verified it myself.

The takeaway: if you have multiple subdomains, check every single one. The main domain being fine doesn't mean subdomains are fine. They often have completely different infrastructure paths.

Step 5: I Verified SSL Works Over IPv6

This one was subtle. My SSL certificate was fine over IPv4. But when I tested over IPv6 with openssl s_client -connect [2001:db8::1]:443, the connection worked but served a different certificate. The default self-signed certificate from the server, not my actual domain cert.

The cause? My Nginx config had the ssl_certificate directive in the IPv4 server block but not in a catch-all block that would also handle IPv6 connections to the same virtual host. Browsers would see a certificate mismatch and either show a scary warning or refuse to connect entirely.

What I did about it

I made sure the SSL certificate configuration was in every server block that handles HTTPS, including the IPv6 listeners. I also set up a default server block that returns a 444 (connection closed) for any requests that don't match a known server name. This prevents the self-signed cert from ever being served to a real user.

Use our SSL Expiry Countdown tool to check your certificate right now. But remember: that tool checks over one protocol. You need to verify SSL works over both IPv4 and IPv6 separately.

Step 6: I Set Up Separate IPv4 and IPv6 Monitoring

This is the big one. All the fixes above are great, but they're point-in-time. I fixed things today. What about next month when I deploy a change? Or next year when I migrate to a new server?

I needed ongoing, automated monitoring that would catch any IPv6 regression the moment it happens. Not after three months of silent failure.

What I set up in UptyBots

For every domain I care about, I created two monitors:

  • An IPv4 HTTP check
  • An IPv6 HTTP check

That's it. Two monitors per domain. They run on separate schedules, and each one only resolves using its specific protocol. If the IPv4 monitor is green but the IPv6 monitor goes red, I know exactly what happened: something broke on the IPv6 side specifically. No guessing. No "works for me" confusion.

I also set up separate Ping monitors for both protocols. Ping tells me if the network path is alive. HTTP tells me if the application is responding. When both IPv6 ping and IPv6 HTTP fail, the problem is at the network or firewall level. When IPv6 ping works but HTTP fails, the problem is at the application level (Nginx not listening, SSL misconfigured, etc.).

For a deeper dive into why this dual-protocol approach matters, read why monitoring both IPv4 and IPv6 matters for modern websites.

Alert configuration

I configured Telegram alerts for both IPv4 and IPv6 monitors. When an alert fires, the message tells me which protocol failed. I don't have to log in to a dashboard and figure it out. The notification says "IPv6 HTTP check failed for api.mydomain.com" and I immediately know where to look.

I set the IPv6 monitors to alert after 2 consecutive failures rather than 1. IPv6 routing can have brief hiccups (especially through NAT64 paths) that resolve on their own. Two consecutive failures means something is actually broken, not just a momentary blip.

Step 7: I Built a Deployment Checklist

Most of my IPv6 problems happened after changes. Server migration. Nginx config update. Firewall rule change. New subdomain. Every one of these is a chance to break IPv6 while leaving IPv4 untouched.

So I wrote a checklist that I go through after every infrastructure change:

  • Run dig AAAA for every affected domain and verify the record
  • Run curl -6 https://domain.com and verify a proper response
  • Run curl -4 https://domain.com and compare
  • Check ip6tables -L (or nft list ruleset) to verify firewall rules
  • Verify Nginx/Apache is listening on [::]:443 with ss -tlnp | grep 443
  • Test SSL over IPv6: openssl s_client -connect [ipv6-addr]:443
  • Check UptyBots dashboard to confirm both IPv4 and IPv6 monitors are green

The whole checklist takes about 5 minutes. That's 5 minutes that saves me from discovering a problem three months later when a user finally complains.

Step 8: I Started Comparing IPv4 vs IPv6 Metrics Weekly

With both monitors running, I now have side-by-side data for both protocols. Every week I spend about 2 minutes glancing at the comparison:

  • Are response times similar? If IPv6 is consistently 50-100ms slower, that points to a routing issue worth investigating.
  • Are there any IPv6 failures that didn't affect IPv4? Even if the alerts caught and resolved quickly, a pattern of IPv6-only failures tells me something is unstable.
  • Has uptime percentage diverged? If IPv4 shows 99.99% and IPv6 shows 99.5%, I have a 0.5% gap that only affects IPv6 users. That's real downtime for real people.

This weekly review has caught two problems early that I would have missed otherwise. Once, a hosting provider routing change added 120ms to IPv6 response times. The site still "worked," but mobile users on IPv6-only carriers were getting noticeably slower loads. I contacted the provider and they fixed the routing within a day.

The other time, I noticed IPv6 uptime had dipped to 98.7% over a two-week period, while IPv4 was 100%. Turned out an upstream transit provider had intermittent IPv6 issues during nighttime maintenance windows. I wouldn't have caught that from a dashboard glance. I only saw it because I was comparing the two numbers.

The Business Case (Why This Matters Beyond Geek Stuff)

I know, I know. IPv6 monitoring sounds like a nerd hobby. But let me give you some numbers that changed my perspective.

T-Mobile has over 100 million subscribers in the US. Over 90% of their mobile traffic is IPv6. Reliance Jio has over 400 million subscribers in India, running IPv6-only from day one. SK Telecom in South Korea has shifted mobile data to IPv6. Google reports over 45% of their global traffic arrives over IPv6. In India, France, and the US, it exceeds 50%.

If your site is broken for IPv6 users, you are invisible to a massive and growing segment of internet users. And you won't know it, because your IPv4 monitoring shows green.

SEO impact

Google crawls over both IPv4 and IPv6. If your site responds slowly or returns errors over IPv6, Googlebot may record higher error rates and slower response times. Over time, this can hurt your search rankings. Google doesn't tell you which protocol it used for a specific crawl, so you can't diagnose this without monitoring both protocols yourself.

Revenue impact

Use our Downtime Cost Calculator to estimate what even a small percentage of lost mobile visitors costs you. If 20% of your visitors come from IPv6-only mobile networks and your site is broken for them, you're losing 20% of potential revenue without a single alert firing.

Support burden

Users on IPv6-only networks who can't reach your site don't know it's an IPv6 problem. They see a generic timeout. Some contact support. Some just leave. Your support team can't reproduce the issue because the office is on IPv4. Everyone is confused. The ticket sits in a queue for days. I've been there.

My Prevention Checklist (The Short Version)

Here's everything from this post condensed into a reference list:

  • Audit AAAA records for all domains quarterly. Verify they point to current, correct IPv6 addresses.
  • Remove AAAA records for domains that don't need IPv6 (let NAT64 handle them cleanly).
  • Mirror iptables rules in ip6tables or switch to nftables for unified management.
  • Allow ICMPv6 types 1-4 and 128-129. Never block ICMPv6 entirely.
  • Add listen [::]:80; and listen [::]:443 ssl; to every Nginx server block.
  • Verify SSL certificates are served correctly over IPv6 connections.
  • Check all subdomains for IPv6 support, not just the main domain.
  • Set up separate IPv4 and IPv6 monitors in UptyBots for every important domain.
  • Configure alerts to identify which protocol failed.
  • Compare IPv4 vs IPv6 response times and uptime weekly.
  • Run the deployment checklist after every infrastructure change.
  • Test from an IPv6-only network (mobile carrier data) after every migration.

What I'd Do Differently If I Started Today

If I were setting up a new server right now, IPv6 monitoring would go in on day one. Not after a problem. Not after a migration. Day one.

I'd provision the server with dual-stack from the start. I'd configure nftables instead of iptables so both protocols are handled in one place. I'd add the listen [::]:443 ssl; lines before I even add the first virtual host. And I'd set up the UptyBots monitors for both IPv4 and IPv6 before I point the domain's DNS to the new server.

It takes maybe 20 extra minutes during initial setup. Compare that to the months of invisible downtime I suffered because I retrofitted IPv6 support after the fact.

For more on the kinds of problems that IPv6 monitoring catches, check out common IPv6 connectivity issues and how monitoring detects them. And if you want to get hands-on with testing, read how to test IPv6 connectivity manually and compare results with automated monitoring.

FAQ

Do I really need separate IPv4 and IPv6 monitors?

Yes. A single monitor that resolves your domain might use either protocol depending on the monitoring server's configuration. Separate monitors guarantee you are testing each protocol independently. I've seen cases where IPv4 was fine for months while IPv6 had intermittent failures that only showed up in the dedicated IPv6 monitor.

What if my hosting provider doesn't offer IPv6?

Some providers still don't. In that case, don't publish AAAA records. IPv6-only users will go through NAT64 automatically. But if you're choosing a new provider, pick one with native IPv6. It's 2026. This shouldn't still be a question.

Can I just use Cloudflare for IPv6 and ignore my origin server's IPv6 config?

For your main domain, yes, Cloudflare proxies IPv6 traffic to your IPv4 origin. But any subdomain that bypasses Cloudflare (API endpoints, direct-to-origin connections, webhook receivers) still needs proper IPv6 config on the origin. Don't assume Cloudflare covers everything.

How often should I audit DNS records?

I do it quarterly, plus after every infrastructure change. If you automate your infrastructure (Terraform, Ansible), add DNS record validation to your CI pipeline so stale records get caught automatically.

Is blocking ICMPv6 really that bad?

Yes. Blocking ICMPv6 Type 2 (Packet Too Big) breaks Path MTU Discovery. Without it, large responses like your homepage with images and scripts will fail while small responses like health check endpoints succeed. Your monitoring says "up" while real users can't load the page. I've seen this exact scenario in the wild.

See setup tutorials or get started with UptyBots monitoring today.

Ready to get started?

Start Free