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.
$ 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.
$ 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.
$ 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.
$ 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.