We run a social/local service that benefits from geolocating the IP of users. The problem is that with IPv6, geolocation is quite a bit spottier than with IPv4.
Is there a way to prefer incoming connections over IPv6, on a Ubuntu host with nginx? The config looks like this:
server {
listen 80 default_server;
listen [::]:80 ipv6only=off default_server;
}
IPv6/IPv4 preference is determined by the initiator of a connection, i.e. the web browser. The address selection rules are defined in RFC 6724. While these can be overridden, it is only by the user reconfiguring their operating system.
The only way you can force someone to use IPv4 is to not offer IPv6 at all. Obviously this is not a practical solution even in the medium term...
So, let's go back to the original problem: Geolocation for IPv6 is "quite a bit spottier than with IPv4."
In part this is very dependent on where you get your geolocation data. Maxmind for instance only gives my IPv6 address as "United States" with no city at all and an interesting set of coordinates, while Google at least correctly identifies the metropolitan area they are still about 50 miles off. Both Maxmind and Google allow for reporting corrections, and at least for Maxmind anyone can do this for any IP address.
I would not expect this situation to last very long. As IPv6 usage continues to expand, users of such geolocation services will demand greater accuracy for IPv6 addresses, and they will have to deliver it eventually, at least for paying customers, lest those customers go elsewhere.
In the meantime, you should be sure that your application has other ways to locate users. If they logged in, you could read their existing account for clues as to their location. You could ask the user to explicitly select a country. And so on...
One other thing you can do is to provide an IPv4-only subdomain and an IPv6-only subdomain of your web site, each of which your pages attempt to load. You can then correlate them client side and report back to the server. Not coincidentally Maxmind is already doing this on their own web site.
Such preferences can be expressed using SRV records. Unfortunately those are not supported for HTTP. So you are left with a situation where the client alone is making the choice between IPv4 and IPv6.
Many clients use the roundtrip time of SYN + SYN-ACK to decide which of the two to use. So by slowing down the sending of a SYN-ACK packet on IPv6, you can make most clients prefer IPv4. But deliberately slowing down your site is a terrible approach.
Instead I would take a step back and look at the problem. You want better geolocation data. Each time a visitor access your site, you immediately get to know one of their IP addresses. Whether that will be an IPv4 or IPv6 address depends on which their browser prefer for communicating with your server.
Inside your page you can make use of an AJAX request to get to know another IP address. For clients using IPv4 send the AJAX request to an IPv6-only domain, for clients using IPv6 send the AJAX request to an IPv4-only domain.
As soon as the AJAX request arrives on the server you know both the IPv4 and IPv6 addresses of the user. Knowing this correspondence will allow you to do geolocation better than you could do knowing only one of the two.
You will often see cases where the AJAX request never arrives on the server. For those users you will have to do geolocation as you can best do based on only one IP address. But as long as the reply to that AJAX request isn't used for anything on the client side, the user will not even notice those failing AJAX requests. So no perceived slowdown or erratic behavior will be caused by the AJAX requests.