Basic Networking Commands (ping, curl, wget)
ping, curl, and wget are the three tools you reach for first whenever something on the network needs checking: is a host even reachable, what does a web server actually respond with, and how do I pull a file down from a URL without opening a browser. They are small, universally available on Linux systems, and scriptable, which is why they show up constantly in troubleshooting, automation, and CI pipelines. This lesson covers all three in depth: how each one works under the hood, their most useful flags, realistic examples, and the mistakes people make when scripting them.
Overview / How It Works
ping tests basic network reachability using the Internet Control Message Protocol (ICMP). It sends an ICMP "echo request" packet to a target host and waits for an "echo reply." If replies come back, the path between your machine and the target is working at the network layer, and you get a round-trip time (RTT) in milliseconds. Because ICMP is a thin, connectionless protocol handled directly by the kernel’s network stack (not by an application), ping can tell you whether a host is alive even before you know anything about what services it runs. The catch: many servers and firewalls deliberately block ICMP for security reasons, so a failed ping does not always mean the host or its web server is down — it may just mean ICMP is filtered.
curl and wget both operate one layer up, at the application layer, most commonly over HTTP and HTTPS (they also support FTP, and curl supports many more protocols). Instead of just checking "is something there," they perform a full request: open a TCP connection to the server (and, for HTTPS, negotiate TLS), send an HTTP request line and headers, and read back a status line, response headers, and a body. This is the same exchange your browser performs when you load a page — curl and wget just do it from the command line, print the result, or save it to disk, without rendering any HTML.
The key difference between curl and wget is philosophy: curl is built to be a general-purpose data-transfer tool and library — by default it prints the response body to standard output, which makes it ideal for inspecting APIs, piping responses into other commands, or scripting HTTP requests with custom methods, headers, and bodies. wget is built around downloading — by default it saves the response to a file, and it has strong built-in support for recursive downloads, resuming interrupted transfers, and mirroring whole directory trees. Both use the same DNS resolution and TCP/TLS machinery as any other Linux program; neither is "faster" in principle, they just default to different behavior for different jobs.
Syntax
General forms:
ping [options] host
curl [options] URL
wget [options] URL
ping options
| Flag | Meaning |
|---|---|
-c N |
Send exactly N packets, then stop (without it, ping runs forever until interrupted with Ctrl+C) |
-i N |
Wait N seconds between packets (default 1) |
-W N |
Timeout in seconds to wait for each reply |
-4 / -6 |
Force IPv4 or IPv6 |
curl options
| Flag | Meaning |
|---|---|
-o file |
Save output to file (you choose the name) |
-O |
Save output using the remote file’s own name |
-I |
Fetch only the response headers (HTTP HEAD request) |
-L |
Follow HTTP redirects (curl does not follow them by default) |
-s |
Silent mode: hide the progress meter |
-X METHOD |
Use a specific HTTP method, e.g. POST, PUT, DELETE |
-d data |
Send data in the request body (implies POST) |
-H "Header: value" |
Add a custom request header |
-w format |
Print extra info after the transfer, e.g. the HTTP status code |
wget options
| Flag | Meaning |
|---|---|
-O file |
Save output to file instead of the default derived name |
-q |
Quiet mode: suppress progress output |
-c |
Continue (resume) a partially downloaded file |
-P dir |
Save downloaded files into directory dir |
-r |
Recursive download (follow links; used for mirroring a site) |
--limit-rate=RATE |
Cap download speed, e.g. 200k |
Examples
Example 1: Checking if a host is reachable with ping
ping -c 4 www.example.com
Output:
PING www.example.com (93.184.216.34) 56(84) bytes of data.
64 bytes from 93.184.216.34: icmp_seq=1 ttl=56 time=11.2 ms
64 bytes from 93.184.216.34: icmp_seq=2 ttl=56 time=10.9 ms
64 bytes from 93.184.216.34: icmp_seq=3 ttl=56 time=11.5 ms
64 bytes from 93.184.216.34: icmp_seq=4 ttl=56 time=11.0 ms
--- www.example.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 10.900/11.150/11.500/0.220 ms
The -c 4 flag limits ping to four packets so it exits on its own instead of running until you press Ctrl+C. Each reply line shows the sequence number (icmp_seq), the time-to-live left on the packet (ttl), and the round-trip time. The summary line reports 0% packet loss, meaning all four replies came back — a strong sign the network path to that host is healthy.
Example 2: Inspecting HTTP headers with curl
curl -I https://www.example.com
Output:
HTTP/2 200
content-type: text/html; charset=UTF-8
content-length: 1256
date: Tue, 04 Aug 2026 14:02:11 GMT
cache-control: max-age=604800
server: ECS (nyb/1D2E)
-I sends an HTTP HEAD request — the server responds with the same headers it would send for a normal page load, but without the body, which is fast and useful for checking whether a site is up, what content type it serves, or how it’s being cached, without downloading the whole page.
Example 3: A connectivity-check script combining ping and curl
#!/usr/bin/env bash
set -euo pipefail
host="example.com"
if ping -c 1 -W 2 "$host" > /dev/null 2>&1; then
echo "$host responded to ping"
else
echo "$host did not respond to ping (may be filtered)"
fi
status=$(curl -s -o /dev/null -w '%{http_code}' "https://$host")
echo "HTTP status for https://$host: $status"
Output:
example.com responded to ping
HTTP status for https://example.com: 200
This script quotes every variable expansion ("$host"), discards ping’s normal output with > /dev/null 2>&1 since only its exit status matters, and uses curl’s -w '%{http_code}' with -s -o /dev/null to fetch only the numeric HTTP status code into a variable instead of printing the whole page body.
Example 4: Downloading a file with wget
wget -O /tmp/report.pdf https://www.example.com/files/report.pdf
Output:
--2026-08-04 14:05:33-- https://www.example.com/files/report.pdf
Resolving www.example.com (www.example.com)... 93.184.216.34
Connecting to www.example.com (www.example.com)|93.184.216.34|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 348211 (340K) [application/pdf]
Saving to: '/tmp/report.pdf'
/tmp/report.pdf 100%[===================>] 340.05K 1.2MB/s in 0.3s
2026-08-04 14:05:33 (1.2 MB/s) - '/tmp/report.pdf' saved [348211/348211]
-O (uppercase) tells wget the exact local filename to save to; without it, wget derives the filename from the URL itself. wget prints DNS resolution, connection, and progress details by default — add -q if you only want the file with no output, which is common in scripts.
How It Works Step by Step
For ping -c 4 www.example.com: the kernel first resolves the hostname to an IP address via DNS, then builds an ICMP echo-request packet and hands it to the network stack, which routes it toward the destination through however many hops (routers) lie in between. Each router along the way decrements the packet’s TTL by one; if a reply comes back, ping calculates the round-trip time from when the request was sent to when the reply arrived. After the requested count, ping prints summary statistics and exits with status 0 if at least one reply was received, non-zero otherwise — which is exactly what the if ping ...; then check in Example 3 relies on.
For curl -I https://www.example.com: curl resolves the hostname, opens a TCP connection on port 443, performs a TLS handshake to establish an encrypted channel, then sends a plain-text HTTP request (HEAD / HTTP/1.1 plus headers) over that encrypted connection. The server’s HTTP layer processes the request and writes back a status line and headers, which curl reads and prints; because it’s a HEAD request, no body follows, and curl closes the connection once the headers are received.
Common Mistakes
Mistake 1: Leaving a URL unquoted when it contains an ampersand
An unquoted & in the shell means "run this in the background," so an unquoted query string silently splits your command in two.
curl https://api.example.com/data?user=alice&format=json
This actually runs curl https://api.example.com/data?user=alice in the background, then tries to run format=json as a separate command, which fails with "command not found." Quote the whole URL:
curl "https://api.example.com/data?user=alice&format=json"
Mistake 2: Confusing curl’s -o and -O, or forgetting the filename
curl -o https://example.com/report.pdf
Here -o expects a filename as its very next argument, so curl treats the URL string itself as the local filename to save to — then finds no URL left to fetch and errors out with "no URL specified." Either give -o an explicit filename followed by the URL, or use uppercase -O to reuse the remote name:
curl -o report.pdf https://example.com/report.pdf
curl -O https://example.com/report.pdf
Mistake 3: Running ping in a script without a packet count
Without -c, ping sends packets forever until interrupted — harmless interactively, but it will hang a script indefinitely.
#!/usr/bin/env bash
ping example.com
The script never reaches any line after this. Always bound it with -c and a timeout with -W:
#!/usr/bin/env bash
ping -c 4 -W 2 example.com
Best Practices
- Always quote URLs and variables (
"$url") in scripts — query strings often contain&,?, and spaces that the shell would otherwise misinterpret. - Use
-cwithpingin any non-interactive context; an unbounded ping will hang a script or CI job forever. - Don’t rely on
pingalone to decide if a web service is "up" — ICMP is frequently blocked by firewalls even when HTTP works fine; check the actual HTTP status with curl instead. - Prefer
curl -s -o /dev/null -w '%{http_code}'in scripts when you only need a status code, to avoid pulling a whole response body into memory. - Use
curl -Lwhen you expect a URL might redirect (e.g. HTTP to HTTPS), since curl does not follow redirects by default. - Use
wget -cto resume large downloads instead of restarting them from scratch after an interrupted connection. - Check
$?(or use the exit status directly in anif) right afterping,curl, orwgetif the script’s next step depends on success.
Practice Exercises
- Write a one-line command that pings
8.8.8.8exactly six times, waiting a maximum of 1 second for each reply. - Use curl to fetch just the HTTP status code (not the body) of
https://www.wikipedia.organd store it in a shell variable, then print "site is up" only if the code is200. - Write a script that downloads a file with
wgetinto/tmp/downloads/(creating the directory first if needed) and prints an error message if the download fails, using the script’s own exit status check — do not assume it always succeeds.
Summary
pingtests basic network reachability using ICMP echo requests and replies, handled directly by the kernel.- Always use
-c(and often-W) withpingin scripts to avoid an indefinite hang. curlis a general-purpose HTTP client that prints responses to standard output by default — ideal for inspecting APIs and headers, and for scripting requests with custom methods, headers, and bodies.wgetdefaults to saving responses to disk and excels at downloads, resuming, and recursive site mirroring.- A failed
pingdoes not prove a web service is down — ICMP is often blocked separately from HTTP; check both when troubleshooting. - Always quote URLs and variables in shell commands to avoid the shell misinterpreting characters like
&and?.
