Name-Based Virtual Hosts With nginx on IPv6 has recently added native IPv6 support to many of its data centers. Linode hosts the VPS that runs this blog, and it happens to reside in their Dallas data center. I was busy planning my wedding when IPv6 support reached me here, so I only got around to enabling it this week.

So now this blog is available over both IPv4 and IPv6, with a special IPv6-only version running at It took a bit of doing, and a lot of trial-and-error, so let me save you some time by sharing how I succeeded.

From reading the documentation and the various posts around the internet, it first seemed that I needed to add listen [::]:80 ipv6only=on to each of my virtual hosts. This, however, resulted in nginx complaining that the socket was already in use when I tried to do this with more than one of my vhosts. Much tinkering later, and I got it finally worked out.

Before I show you the answer, though, a word of caution: From reading the documentation, I believe that this solution will not work on BSD-based systems, nor will it work on certain Linux-based systems where net.ipv6.bindv6only parameter has been changed from the default. Specifically, the solution I am posting here relies upon your networking stack using hybrid ports instead of separate ports for IPv6 and IPv4; that is, opening a hybrid port will accept traffic on both IPv4 and IPv6, whereas you would have to explicitly open separate ports for each version of the protocol. However, adapting this hybrid port-based solution to a separate port-based system is trivial, and I’ll show that, too.

Now, without further ado, here is how to get nginx running your name-based virtual hosts over IPv6:

Step 1: Enable IPv6 in nginx if you haven’t already. Nothing tricky here, except to make sure that if you have to recompile your server, you remember to include any of your own configure parameters that you need.

Step 2: In your default virtual host, change your listen 80 default_server; directive to listen [::]:80 default_server;. This relies upon your system using those hybrid ports I mentioned earlier to automatically open the same port on IPv4 as well (even though netstat doesn’t show that). (If you are not using a default virtual host, skip this step.)

Step 3: In all of your other virtual hosts, change your listen 80; directives to listen [::]:80;. If you had previously omitted the listen 80; directive (since that’s the default if not specified), you must now add the listen [::]:80; directive.

Note that you can do exactly the same thing with additional ports — I additionally have listen [::]:8080 default_server; in my default vhost, and listen [::]:8080 in my vhost that listens on that port.

Okay, but what about you folks on systems that are using separate, not hybrid, ports? Well, you simply have to specify IPv4 and IPv6 listen directives, and add the parameter ipv6only=on to one of your IPv6 declarations for each port. So in addition to listen 80 default_server; in your default vhost, you would add listen [::]:80 default_server ipv6only=on;. Yes, I’m heavily stressing that this is added — if you have separate ports, you have to explicitly open one on each protocol. (If you are not using a default vhost, then you have to add a listen [::]:80 ipv6only=on; to precisely one of your vhosts; see below for more explanation on this point.)

Then, in each of your vhosts, you would add listen [::]:80; where you have listen 80;, but note that I did not specify ipv6only=on again — this must be specified once and only once, or else nginx will throw errors about the port already being in use.

And, again, you can do this for any and all ports you wish to use, remember that ipv6only=on must be specified once and only once for each port.

Much thanks and many kudos go to kolbyjack on Server Fault for helping me figure this out.

This entry was posted in How-to and tagged , , , , . Bookmark the permalink.

7 Responses to Name-Based Virtual Hosts With nginx on IPv6

  1. Linuxaria says:


    On my Debian 6 it seem that the default it’s the separate ports setup.
    You saved me a lot of time for sure.

    Thanks again from another Linode member 😉

  2. Pingback: » Linuxaria IPv6 Ready !

  3. diego s. says:

    Thank you for sharing this, you just saved my mental health!

  4. chain says:

    Thank you, this really saved my head banging against the wall.

    If you do not specify the listen clauses in the vhosts, you will ALWAYS get the default host for IPv6. Well, first thing was to figure out that it’s actually IPv6 only. Try that in a hybrid network.

    Anyways, thanks a lot. Works like a charm!

  5. Stephane says:

    This post is very useful but unclear in the definition of hybrid and separate ports.

    So to help other users, this is the configuration I’m using in Archlinux and the latest nginx (1.2.1):

    #default server
    server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    # server_name is not to be specified in the default server

    # virtual host
    server {
    listen 80;
    listen [::]:80;




  6. Veg says:

    Thanks – even after RTFM I was still having aggro. You saved me a lot of time!

Comments are closed.