Lesson 1-2 covered why the DNS lookup has to happen before the HTTP request.
Because the browser needs the server's IP address before it can send anything to it. The URL contains a name, but messages are delivered to addresses.
DNS turns example.com into an IP address, and only then can the browser open a connection and send its request. This lesson is about what those addresses actually are.
Every machine gets a number
An IP address (Internet Protocol address) is the number that identifies a computer on a network. If a URL is like a business name, the IP address is the street address that mail actually gets delivered to.
The common format, IPv4, is four numbers from 0 to 255 separated by dots:
93.184.215.14
Each of the four parts is called an octet because it fits in 8 bits (2⁸ = 256 possible values, 0 to 255). Four octets means 32 bits total, so an IPv4 address is really just one 32-bit number written in a human-friendly way. There are about 4.3 billion possible IPv4 addresses, which turned out not to be enough for the whole world. The newer IPv6 format fixes that with 128-bit addresses written in hex, like 2606:2800:21f:cb07::1. You will mostly see IPv4 in examples, and everything you learn applies to both.
You will read IP addresses constantly on the job: in server logs, in config files, and in error messages like connection refused to 10.0.5.20:5432. Being able to glance at one and know what kind of machine it points at is a real debugging skill, and the next section gives you the first rule for it.
The number hiding behind the dots
An IPv4 address is one 32-bit number written in a friendly way. This splits it into octets and rebuilds that number.
ip="192.168.1.7" IFS='.' read -r a b c d <<< "$ip" echo "octets: $a $b $c $d" num=$(( (a << 24) + (b << 16) + (c << 8) + d )) echo "as one number: $num"
Output
octets: 192 168 1 7 as one number: 3232235783
IFS='.' tells read to split on dots instead of spaces, which is how one line becomes four variables. The << operator shifts bits left, so a << 24 moves the first octet into the top 8 bits of the 32-bit value.
Each octet occupies its own 8-bit slot, and the shifts of 24, 16, 8, and 0 place them in order without overlapping. That is why the dotted form loses no information: it is the same number, grouped for human eyes.
Seeing the single number explains something practical. Address ranges that look arbitrary in dotted form are contiguous blocks of integers underneath, which is how routers decide where to send a packet with a single numeric comparison.
Private vs public addresses
Not every IP address is reachable from the whole internet. Three ranges are reserved for private networks (your home Wi-Fi, an office LAN):
| Range | Example |
|---|---|
10.x.x.x | 10.0.5.20 |
172.16.x.x to 172.31.x.x | 172.20.1.1 |
192.168.x.x | 192.168.1.7 |
Your laptop probably has a private address like 192.168.1.7 right now. Your home router shares one public address with every device behind it (a trick called NAT, network address translation). Two special addresses worth knowing: 127.0.0.1 always means "this same machine" (called localhost), and 8.8.8.8 is Google's public DNS server, a favorite for connectivity tests.
When a server "cannot be reached", one early question is: am I trying to reach a private address from outside its network? That never works.
Labelling addresses private or public
The condition tests the three reserved ranges from the table above.
for ip in 10.0.5.20 8.8.8.8 192.168.0.9 172.20.1.1 93.184.215.14; do IFS='.' read -r a b c d <<< "$ip" if [ "$a" -eq 10 ] || { [ "$a" -eq 192 ] && [ "$b" -eq 168 ]; } || { [ "$a" -eq 172 ] && [ "$b" -ge 16 ] && [ "$b" -le 31 ]; }; then echo "$ip private" else echo "$ip public" fi done
Output
10.0.5.20 private 8.8.8.8 public 192.168.0.9 private 172.20.1.1 private 93.184.215.14 public
Reading the condition
- Numbers compare with
-eq,-ge, and-lerather than=and<, because[ ]treats those as string comparisons. - The two-part tests are grouped in braces, as in
{ [ "$a" -eq 192 ] && [ "$b" -eq 168 ]; }, so the||applies to the whole pair rather than to the last test alone. Without the braces, any address starting with 192 would be called private. - The 172 range needs two comparisons,
-ge 16and-le 31, because only 16 of the 256 possible second octets are reserved. That range trips people up precisely because it is partial, unlike the clean 10 and 192.168 cases.
Reading a localhost connection failure
The program was trying to reach the very machine it was running on.
127.0.0.1 is localhost, and it always means this same machine. So the program expected a database on its own machine, since port 5432 is PostgreSQL, and nothing was listening there.
The fix is to start the local database or point the program at the right host, and not to check the network. No packet ever left the machine, so firewalls, routers, and DNS are all irrelevant to this failure.
That is the practical value of recognizing the address on sight. 127.0.0.1 in an error message rules out most of the chain in one glance, and it usually means a config file still holds a development value in an environment that has no local database.