How to force curl to use IPv4 or IPv6

Forcing IPv4 or IPv6 in curl keeps a request on one address family when a dual-stack host behaves differently across network paths. That is useful for troubleshooting, reachability checks, and scripts that should fail explicitly instead of quietly using the other family.

The --ipv4 (-4) and --ipv6 (-6) options limit which resolved addresses curl is willing to connect to. Without either option, curl follows its normal dual-stack behavior and can prefer or fall back between IPv6 and IPv4 depending on resolver results and connection timing.

A forced-family request succeeds only when the hostname has an address in that family and the local host has a working route for it. If the chosen family is unavailable, curl fails the transfer instead of switching families, and on some macOS resolver paths an IPv4-only hostname can still appear as an IPv4-mapped IPv6 value such as ::ffff:192.0.0.170.

Steps to force curl to use IPv4 or IPv6:

  1. Inspect a normal request first to see whether curl is currently considering both address families.
    $ curl --silent --show-error --verbose --output /dev/null https://example.com
    * Host example.com:443 was resolved.
    * IPv6: 2606:4700:10::6814:179a, 2606:4700:10::ac42:93f3
    * IPv4: 104.20.23.154, 172.66.147.243
    *   Trying 104.20.23.154:443...
    * Connected to example.com (104.20.23.154) port 443
    ##### snipped #####

    When both lists are present, curl uses its default dual-stack connection logic. The connected family can change with DNS results, local routing, and timeout behavior.

  2. Force an IPv4-only connection when the request must ignore AAAA answers.
    $ curl --ipv4 --silent --show-error --output /dev/null --write-out 'remote_ip=%{remote_ip}\nresponse_code=%{response_code}\n' https://example.com
    remote_ip=104.20.23.154
    response_code=200

    The remote_ip value should be a dotted-quad IPv4 address when the request actually stayed on IPv4.

  3. Force an IPv6-only connection when the request must not fall back to IPv4.
    $ curl --ipv6 --silent --show-error --verbose --max-time 5 --output /dev/null https://example.com
    * Host example.com:443 was resolved.
    * IPv6: 2606:4700:10::6814:179a, 2606:4700:10::ac42:93f3
    * IPv4: (none)
    *   Trying [2606:4700:10::6814:179a]:443...
    * ipv6 connect timeout after 2495ms, move on!
    *   Trying [2606:4700:10::ac42:93f3]:443...
    * Connection timed out after 5005 milliseconds
    * Closing connection
    curl: (28) Connection timed out after 5005 milliseconds

    On a host with working IPv6, the same command connects to one of the IPv6 addresses and completes without ever listing an IPv4 candidate.

  4. Confirm that --ipv6 blocks fallback by testing an IPv4-only hostname with a short timeout.
    $ curl --ipv6 --silent --show-error --verbose --max-time 5 http://ipv4only.arpa/
    * Host ipv4only.arpa:80 was resolved.
    * IPv6: ::ffff:192.0.0.170, ::ffff:192.0.0.171
    * IPv4: (none)
    *   Trying [::ffff:192.0.0.170]:80...
    * ipv6 connect timeout after 2495ms, move on!
    *   Trying [::ffff:192.0.0.171]:80...
    * Connection timed out after 5006 milliseconds
    * Closing connection
    curl: (28) Connection timed out after 5006 milliseconds

    The mapped addresses in the verbose output still do not create an IPv4 fallback. Pair forced-family probes with --max-time in automation so an unavailable path fails quickly and clearly.