DEV Community

Fatima Qaisar
Fatima Qaisar

Posted on • Originally published at Medium on

What My Terminal Taught Me About the Web

Disclaimer: This isn’t written by someone who has taken a formal networking course yet, so a few rough edges should be expected.

One thing I’ve realised during my computer science journey is that the deeper you go, the more fundamental questions you start asking.

I’d heard the acronyms — HTTP, TCP, DNS, TLS — countless times. I’d even spent time digging into Apache Tomcat’s codebase. But if someone had asked me what actually happens after typing a URL into a browser and pressing Enter…

I wouldn’t have had a good answer.

For me, this journey began with Apache Tomcat. It was one of the first open-source projects I explored — I had just learned Java OOP, and figured, how hard could a Java web server be? (Spoiler: VERY). It made me actually curious about how web infrastructure really works though.

So instead of jumping straight into networking textbooks, I decided to follow a single browser request from start to finish, asking why? at every step. This article is my attempt to document that journey — not as an expert teaching networking, but as a computer science student trying to understand it one protocol at a time.

The Map

Before I dive in, here’s roughly the shape of the trip: your browser has to find an address, connect to it, secure that connection, then finally ask for the page. Five words, five stops. I’m not going to hand you a diagram of it here — I’d rather you actually watch each stop happen, the way I did, and see the shape for yourself by the end.

Every browser request you make follows this journey in some form. I just decided to stop taking it for granted and follow it myself.

I started at the beginning: DNS (Domain Name System). Because before your browser can talk to a server at all, it first has to figure out where that server actually is.

Step 1: DNS — Finding the Address

Humans remember names. Computers only understand numbers — addresses like 142.250.186.238. So before anything else can happen, something has to translate the name you typed into the number a computer can actually use. That something is DNS — also known as the phonebook of the internet.

I wanted to see this happen instead of just reading about it, so I opened a terminal and ran:

nslookup google.com
Enter fullscreen mode Exit fullscreen mode

Then, curious whether the result would be the same every time, I ran it a few more times in a row. Here’s exactly what I got back, run after run:

Server: UnKnown
Address: fe80::1
DNS request timed out.
 timeout was 2 seconds.
Name: google.com
Address: 2a00:1450:4018:80d::200e
Enter fullscreen mode Exit fullscreen mode

Quick note on those first couple of lines, since they look a little cryptic: fe80::1 is a link-local IPv6 address —an address that's only valid on your local network. On many home networks, it belongs to your router, though that's not guaranteed. UnKnown simply means a reverse DNS lookup (asking "what hostname is associated with this IP address?") didn't return a name. That's fairly common and isn't a problem by itself.

In plain terms, my computer was querying the DNS resolver reachable at fe80::1. Exactly what happened after that depended on how my local network was configured—I was only seeing my side of the conversation.

Server: UnKnown
Address: fe80::1
Non-authoritative answer:
DNS request timed out.
 timeout was 2 seconds.
Name: google.com
Address: 142.250.186.238

Server: UnKnown
Address: fe80::1
Non-authoritative answer:
Name: google.com
Addresses: 2a00:1450:4018:80d::200e
 142.250.186.238
Enter fullscreen mode Exit fullscreen mode

Three runs, three different results. My first reaction was: wait, why did it keep timing out? I just asked for an address, not something complicated.

That’s when I noticed something I’d glossed over at first — Across the three runs, I eventually saw two different addresses for google.com:

- 142.250.186.238  — this is an IPv4 address, the older, more familiar style you’ve probably seen before

- 2a00:1450:4018:80d::200e  — this is an IPv6 address, a newer, much longer format, built because the internet was running out of IPv4 addresses to hand out.

A single website can have both, and your computer can reach it using either one. Looking back at the three runs, I noticed that I eventually received both an IPv4 and an IPv6 address for the same domain. Exactly why the earlier lookups timed out isn’t something I can conclude from this output alone — there are several possible explanations, from packet loss to resolver behaviour. What I could see was that Google publishes both address families, and that my machine eventually learned about both.

I hadn’t planned on stumbling into IPv4 vs IPv6 today — I was just trying to understand why the same command kept behaving differently every time I ran it. But that turned out to be the theme of this whole exercise: asking why something looks inconsistent usually leads somewhere real.

Also notice the phrase “ Non-authoritative answer.” That confused me at first — non-authoritative from what? It turns out this means the answer didn’t come directly from Google’s own servers. It came from a resolver that already had it saved. Which raised the obvious next question: saved how, and for how long?

To actually find out what was going on, I ran a more detailed version of the command:

nslookup -debug google.com
Enter fullscreen mode Exit fullscreen mode

Buried in the output was this:

ANSWERS:
-> google.com
 internet address = 142.250.186.238
 ttl = 260 (4 mins 20 secs)
Enter fullscreen mode Exit fullscreen mode

TTL  —  time to live. That number, 260 , is literally a countdown. It means: you’re allowed to remember this answer for 260 seconds before you have to ask again. That’s why my second lookup was instant — I was still inside that 260-second window, so my computer just reused the answer it already had, instead of going out and asking again. It’s less like calling someone to ask their address every time, and more like writing it down on a sticky note and checking the note first.

That one number explained everything I’d just seen: the timeout, the instant retry, all of it.

It’s not just a fun fact, either — TTL is the reason “I updated my DNS record and nothing changed” is a real, common problem developers hit. If you point a domain at a new server, old TTLs mean some visitors keep getting routed to the old one until their cache expires. This is why the standard advice is just “wait” — there’s a literal countdown running somewhere.

A Quick Detour: Who Actually Decided This?

Finding that number made me wonder something I’d never thought about before: who actually decided that DNS answers should be cacheable, and that there should be a specific countdown attached to them? Is this just something engineers agreed on informally somewhere, or is it written down officially?

Turns out the internet does have something like a rulebook. Whenever a protocol like DNS gets built, someone writes an actual specification document describing exactly how it’s supposed to behave — down to the individual bits. These documents are called RFCs (Request for Comments). DNS’s was RFC 1034 and RFC 1035, written by Paul Mockapetris in 1987.

I went and skimmed it. Most of it looked like alien language, but I found something interesting: a little inconsistency in the document related to TTL. One section describes the TTL field as a 32-bit signed integer; another section, later in the same document, describes it as 32-bit unsigned.

It was a neat realization: one of the internet’s most load-bearing systems started with subtle ambiguities right in its core specification. The IETF didn’t tear down and rewrite RFC 1035 when these edge cases surfaced; instead, they published update documents years later — like RFC 2181 — to formally standardize behaviors like TTL values and CNAME rules, while keeping the original wire format running.

That quietly changed how I think about “infrastructure.” It’s not a perfect, static machine sitting underneath everything, finished once and for all. It’s an evolving stack of pragmatic decisions made by people, patched and refined over time, that the internet simply agreed to keep building on top of.

One More Thing I Tried

Since I was already poking at DNS, I tried looking up something that doesn’t exist:

nslookup nonexistentwebsite123456.com

Server: UnKnown
Address: fe80::1
*** UnKnown can’t find nonexistentwebsite123456.com: Non-existent domain
Enter fullscreen mode Exit fullscreen mode

No timeout, no delay — just an immediate, clean “this domain doesn’t exist.” That was actually useful to see side by side with the earlier timeouts, because they feel similar as a user (nothing loads, something’s wrong) but they’re completely different problems under the hood: one is “the answer is slow to arrive,” the other is “there is no answer, and DNS knows that immediately.” A plain browser error alone often can’t tell you which one you’re looking at — though a browser’s dev tools, if you actually open the Network tab, usually can. The terminal just handed it to me directly, without needing to dig.

One more small thing I didn’t fully chase down earlier: back in the DNS section, the first run never said “Non-authoritative answer,” while the second and third both did. I’m not certain why the very first attempt was missing that label.

Step 2: TCP — Making Sure the Connection Actually Holds

DNS gets you an address. But having an address doesn’t mean you’re connected to anything — it’s just a location, like knowing someone’s house number without having knocked on the door yet.

This is where TCP (Transmission Control Protocol) comes in. Its job is to actually establish a reliable connection to that address, and then make sure whatever gets sent over it arrives complete and in order — no missing pieces, nothing scrambled.

I tested this by just checking whether a real TCP connection could actually be established, on port 443 (the standard port for HTTPS, secure web traffic):

Test-NetConnection google.com -Port 443
Enter fullscreen mode Exit fullscreen mode

Here’s what came back:

WARNING: TCP connect to (2a00:1450:4018:80d::200e : 443) failed
ComputerName : google.com
RemoteAddress : 142.250.186.238
RemotePort : 443
InterfaceAlias : Wi-Fi
SourceAddress : 192.168.18.9
TcpTestSucceeded : True
Enter fullscreen mode Exit fullscreen mode

The first line was the interesting one: my computer tried connecting over IPv6 first — the same 2a00:… address from the DNS section earlier — and it failed. But the overall result still says TcpTestSucceeded : True , because it then tried the IPv4 address ( 142.250.186.238 ) instead, and that one worked.

I didn’t have to do anything for that fallback to happen. My computer tried the newer address type, noticed it wasn’t working, and quietly tried the older one instead — all inside a single command. It turns out this kind of behavior has an actual name: Happy Eyeballs , described in RFC 8305. The general idea is that when a client has both an IPv6 and an IPv4 address to try, it doesn’t just gamble on one — it attempts connections and uses whichever one actually succeeds first, so a broken or slow IPv6 path doesn’t leave you stuck.

Why did IPv6 fail for me specifically? I could probably spend an afternoon debugging that — router config, ISP support, something in between — but that’s actually a different question from the one I was chasing. The useful thing here isn’t why IPv6 failed. It’s that the internet has a built-in way of not caring when it does.

This is also, I think, the kind of thing worth knowing before you’re stuck debugging it under pressure — “site works for some people, not others” or “works on my laptop, not my phone” can genuinely trace back to exactly this: one address family failing silently while the other quietly saves the connection.

Conceptually, once a working address and route are found, what TCP actually does before any of your data goes anywhere is a three-step handshake  —  SYN: one side says “can we talk.” SYN-ACK: the other says “yes, can you hear me,” and finally ACK: the first side confirms “yes, let’s go.” Only after that does actual data start moving, with TCP keeping track of what’s been sent and what’s been confirmed received, so nothing arrives missing or out of order.

Step 3: TLS — Making the Conversation Private

Once you’re connected, there’s still a problem: that connection is readable by anyone who can see the traffic. TLS (Transport Layer Security) is what wraps the conversation so that even if someone intercepts it, all they see is scrambled noise.

This section is different from the two before it because capturing a real handshake is a rabbit hole that probably deserves its own article someday.

Before any of your actual data moves, your browser and the server negotiate an encryption method , the server proves its identity using a certificate , and both sides land on a shared secret key without ever sending that key over the wire — some genuinely clever math makes that possible. The padlock icon next to your address bar is the visible result of that negotiation having just succeeded, fresh, every single time you connect — not a badge that’s checked once and reused.

Step 4: HTTP — Finally Asking for the Page

After all of that — the address found, the connection made, the channel secured — HTTP finally does the part everyone actually associates with “browsing the internet.” I wanted real output here too, so I tried:

curl -v https://google.com
Enter fullscreen mode Exit fullscreen mode

(Note on ‘curl’ or Client URL : It’s a command-line tool that lets you make HTTP requests and see the raw response — headers, status codes, the full HTML — without a browser getting in the way. It’s like being able to read the conversation between your browser and the server directly. (On Windows, ‘curl’ in PowerShell is actually an alias for a different command — you have to use ‘curl.exe’ to get the real one, as I found out the hard way.))

That gave me an entire, genuinely interesting trace:

* Host google.com:443 was resolved.
* IPv6: 2a00:1450:4018:80d::200e
* IPv4: 142.250.186.238
* Trying [2a00:1450:4018:80d::200e]:443…
* Trying 142.250.186.238:443…
* Connected to google.com (142.250.186.238) port 443
* using HTTP/1.x
> GET / HTTP/1.1
> Host: google.com
> User-Agent: curl/8.13.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Location: https://www.google.com/
< Content-Type: text/html; charset=UTF-8
< Server: gws
< Content-Length: 220
< Alt-Svc: h3=":443"; ma=2592000,h3–29=":443"; ma=2592000
<
<HTML><HEAD><meta http-equiv="content-type" content=”text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="https://www.google.com/">here</A>.
</BODY></HTML>
Enter fullscreen mode Exit fullscreen mode

A few things jumped out immediately.

First — there’s that IPv4/IPv6 pattern again, showing up for a third time in this same journey. Both addresses get tried, and the connection lands on IPv4, same as the TCP section earlier. I didn’t go looking for this repetition; it just kept showing up every time I checked.

Second — the actual request is exactly three lines: GET / HTTP/1.1 , a Host header, and a couple of housekeeping headers identifying the client. That’s it. That’s the whole request curl needed to send in this case.

Third, and the part I didn’t expect: the response wasn’t 200 OK at all. It was 301 Moved Permanently, with a Location header pointing to https://www.google.com/. Typing google.com doesn’t actually load google.com — the server immediately redirects you to www.google.com, and your browser follows that redirect silently, so fast you’d never notice it happened. The “one simple request” I imagined at the start of this article is actually, in Google’s case, at least two.

One more small thing worth a mention: that Alt-Svc: h3=… header in the response is Google telling clients it also supports HTTP/3  — a newer version of HTTP that runs over a different, faster transport than the TCP connection this whole request just used (QUIC, which runs on UDP — more protocols yes). I’m not going to open that door today, but it’s a good reminder that even “HTTP” isn’t one fixed, finished thing either.

The Whole Trip

This was it:

You type a URL → DNS finds the address → TCP connects → TLS secures it → HTTP asks for the page → your browser renders it
Enter fullscreen mode Exit fullscreen mode

Five arrows. A few paragraphs ago that would’ve just been a diagram to memorize. Now every single one is something I actually watched happen — DNS finding the address (with a real fallback happening along the way), TCP getting you connected, TLS securing the channel, and HTTP finally asking for the page. Every step here started with an actual command and actual output, not a diagram I was trusting blindly.

What I didn’t expect, going in, was the repetition — the same IPv4/IPv6 pattern showing up in DNS, TCP, and the HTTP connection, every single time I checked, without me going looking for it. But that’s the thing about asking why? all the way down — you don’t just find the answers you were looking for. You find the places where the answers don’t quite agree with themselves, where the internet isn’t a perfect, finished machine humming underneath everything, but a stack of decisions made by people.

That’s not a flaw you learn from a textbook. It’s sitting right there in a terminal — waiting for you to ask.

Top comments (0)