How to Enable IPv6 in Nginx and Make Your Site Reachable on All Networks
If you’ve been running a site that only responds to http://example.com for IPv4 but your visitors keep hitting the IPv6 address and getting nothing, it’s time to patch that gap. Below is a quick walk‑through of turning on IPv6 in Nginx, with a few real‑world hiccups you’ll want to watch out for.
Check Your Server Supports IPv6
Before tinkering with config files, make sure the OS can even bind to IPv6. On most modern Linux boxes this is enabled by default, but if you’re on an older Debian or a minimal container it might be off.
sysctl net.ipv6.conf.all.disable_ipv6
If that returns 1, run:
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0
Why does this matter? Nginx won’t even try to listen on IPv6 if the kernel refuses; you’ll end up with a silent 502 and no clue why.
Add an IPv6 Listen Directive
Open your site’s server block (/etc/nginx/sites-available/example) and add a listen [::]:80 line. The square brackets tell Nginx this is the IPv6 wildcard address; without them it thinks you’re using a literal string.
server {
listen 80;
listen [::]:80;
server_name example.com;
# … rest of config …
}
If you also use TLS, add:
listen 443 ssl; listen [::]:443 ssl;
listen without an address defaults to IPv4. Adding the IPv6 version explicitly tells Nginx to bind two sockets—one for each protocol.
Validate and Reload
Running a syntax check before reloading is a lifesaver; you’ll never have to restart the service on a live site just to discover you misspelled listen.
sudo nginx -t
If it says “syntax OK” and “test failed”, fix whatever is wrong. Once cleared:
sudo systemctl reload nginx
Reloading keeps current connections alive while pulling in the new config, so your users don’t see a hiccup.
Verify IPv6 Is Working
From the server itself, curl the IPv6 loopback to confirm binding:
curl -6 http://[::1]
You should see your page’s HTML. From an external machine, ping an IPv6 address of the host (or use dig AAAA example.com) and then fetch it.
If you get “connection refused” or no reply, double‑check:
- Firewall – On Ubuntu firewalls often block port 80/443 for IPv6 by default. Run sudo ufw allow in on eth0 to any port 80 proto tcp (adjust interface as needed).
- SELinux/AppArmor – These security modules sometimes deny bind on IPv6. Look at /var/log/audit/audit.log or run sudo setenforce 0 temporarily to test.
That’s usually for the kernel, but the same principle applies: a recent OS upgrade can disable IPv6 without telling you. Always re‑check sysctl net.ipv6.conf.all.disable_ipv6 after an OS change.
- Multiple Listen Lines – If you have several listen 80; directives, add [::]:80 to each or just use the one block and remove duplicates. Duplicate listens can cause Nginx to pick the first IPv4 bind and ignore IPv6.
- Port Conflicts – A process like Docker sometimes grabs IPv6 sockets. Use netstat -tuln | grep :80 to confirm nothing else is hogging your port.
Why Bother?
If your visitors come from a mobile network that prefers IPv6, or you’re in a region where IPv4 addresses are scarce, having no IPv6 support can silently drop traffic. Enabling it is just one line of config and keeps everyone happy—no extra cost, no extra maintenance.
That’s all there is to it. If you hit a wall, drop a comment here; I’ve run into a handful of those odd quirks that aren’t in the docs.