“What is my IP?” sounds like a one-line question until the wrong address lands in an allowlist and locks you out. Your laptop can have a Wi-Fi address, a container address, several IPv6 addresses, and a public address observed beyond a router—all at once. The useful first question is gentler and more precise: whose address are you asking for?
For a website or remote API, the answer is normally the source address that service sees for this connection. It may belong to your host, home router, ISP carrier-grade NAT, VPN exit, corporate proxy, cloud host, or SSH jump box. The commands below are simple; interpreting where you ran them is the real skill.
First, separate the addresses on the table
Interface address: assigned to a local interface. Home IPv4 addresses often come from private ranges, while a global IPv6 address may be directly usable beyond the LAN subject to routing and firewall policy.
Chosen source address: the address the kernel selects for a route. It can still be private and translated later.
Observed public address: the source an external endpoint sees after routing, NAT, a tunnel, or a proxy has done its work.
Service ownership: an observed address is not proof that your machine owns it. Many subscribers or containers may share one egress address.
ip -brief address
ip route get 1.1.1.1<interfaces and locally assigned addresses>
1.1.1.1 via <gateway> dev <interface> src <chosen-local-address> ...What those local checks actually establish
ip -brief addressinventories addresses assigned inside the current network namespace; it does not ask the internet for your public identity.ip route getasks the kernel which route and local source it would choose. The destination is used for route selection; this form does not send an application request to that destination.Run the same checks inside a container and on its host and you may get different local answers. That is expected, not a contradiction.
Ask an HTTPS endpoint what reached it
ipify publishes separate endpoints for IPv4, IPv6-only, and whichever family the connection selects. The universal endpoint is a sensible interactive default when you genuinely accept either family.
curl --fail --silent --show-error --max-time 10 https://api64.ipify.org<public IPv4 or IPv6 address seen by ipify>Why this is more than a tiny curl command
--failmakes HTTP 400-or-later responses fail instead of treating an error page as useful data; curl otherwise regards an HTTP response as a successful transfer.--silent --show-errorremoves the progress meter without hiding the reason for a transport failure, and--max-time 10prevents an automation job from waiting forever.The endpoint returns plain text and may omit a trailing newline, so the next shell prompt can appear beside the address. That visual detail is not part of the result.
Do not add
--insecure. A public address is sensitive operational data, and disabling TLS verification removes the identity check on the service answering you.
IPv4 and IPv6 may tell two different stories
curl -4 --fail --silent --show-error --max-time 10 https://api.ipify.org
curl -6 --fail --silent --show-error --max-time 10 https://api6.ipify.org<public IPv4 address>
<public IPv6 address, or a connection error when IPv6 is unavailable>Read a failed IPv6 lookup honestly
-4and-6constrain curl’s connection family; they do not convert one address family into the other.ipify documents
api.ipify.orgfor IPv4 andapi6.ipify.orgfor IPv6-only requests. The IPv6 endpoint is expected to fail when the execution environment has no working IPv6 path.A global IPv6 address can be assigned directly to a host while outbound IPv4 is shared behind NAT. Therefore neither result should be silently substituted for the other in firewall policy.
A DNS lookup is an independent route—with a caveat
dig +short myip.opendns.com @resolver1.opendns.com<public IPv4 address observed by the OpenDNS resolver>What changed when HTTP left the picture
@resolver1.opendns.comsends this query to the named resolver rather than relying on the system’s ordinary recursive resolver.+shortsuppresses DNS metadata, but a short-looking result still needs validation before a script trusts it.This method asks a DNS provider instead of an HTTPS provider. DNS interception, enterprise policy, VPN routing, or blocked external DNS can make it fail or answer from a different network boundary.
Do not confuse this special reflection name with looking up the A record of an ordinary website; a normal DNS lookup returns the website’s address, not yours.
Automation should distrust even a friendly endpoint
#!/usr/bin/env bash
set -euo pipefail
candidate=$(curl \
--fail --silent --show-error \
--max-time 10 \
--user-agent 'public-ip-check/1.0' \
https://api64.ipify.org)
python3 - "$candidate" <<'PY'
import ipaddress
import sys
value = sys.argv[1].strip()
address = ipaddress.ip_address(value)
print(address.compressed)
PYFetch one response, validate it as an IP literal, and print it only after validation.
The safety boundary in this small script
set -euo pipefailstops common shell failures from being mistaken for a valid address; curl’s nonzero exit status also propagates through command substitution.Python’s standard
ipaddress.ip_address()accepts a valid IPv4 or IPv6 literal and rejects HTML, an empty response, a rate-limit message, or extra text.The value is passed as one quoted argument rather than evaluated as shell syntax. Validation happens before it reaches logs, a firewall, DNS, or an allowlist.
The explicit user agent gives a service operator a minimally identifiable client. For production, also set a retry policy with backoff only if the provider’s terms permit it.
Why the address belongs to somewhere else
Home NAT: several devices use private IPv4 addresses while the router translates their outbound connections to one public IPv4 address.
Carrier-grade NAT: an ISP can place the router behind another translation layer. RFC 6598 reserves
100.64.0.0/10as Shared Address Space for this use; it is distinct from the private ranges in RFC 1918.VPN: the observer normally sees the VPN exit address when this traffic follows the tunnel, not the access network’s address.
Explicit or transparent proxy: an HTTP service may see the proxy. Do not assume forwarding headers reveal the original address or that untrusted headers are truthful.
SSH session or remote shell: curl runs from the remote machine and reports that environment’s egress. Your terminal window being local does not make the process local.
Container or network namespace: the process may exit through the host, an overlay gateway, a cluster NAT, or a service mesh. Test at the boundary relevant to the system you are configuring.
When two commands disagree, pause before fixing anything
Confirm the commands ran on the same host, container, namespace, and SSH session.
Check whether one request used IPv4 and the other IPv6.
Inspect active VPN, proxy, split-tunnel, and policy-routing configuration without disabling organizational controls.
Repeat once using the same address family and a second reputable reflection mechanism. Do not loop aggressively.
Ask which observed boundary the downstream rule actually needs: workstation, router WAN, VPN exit, cloud workload, or DNS resolver.
Keep exploring the network boundary
Find listening ports on Linux without confusing them with internet exposure.
Understand DNS filtering, VPN bypasses, and IPv6 on a home router.
Comments and corrections