Skip to content

HTTP, TCP & SSH — How a Request Actually Travels

Addressing gets a packet to the right host, routing gets it there, DNS turned the name into that address in the first place — this page covers the last leg: how a connection opens, and the two different conversations (HTTP and SSH) that commonly run over it.

TL;DR — in 30 seconds:

  • Nothing application-level moves until the TCP three-way handshake completes — a blocked handshake looks unreachable, not slow.
  • HTTPS adds a second handshake (TLS) between TCP and HTTP — encryption is negotiated before any HTTP data is sent.
  • HTTP and SSH are different conversations that both happen to ride TCP: HTTP is stateless request/response, SSH is one long-lived interactive session.

1. The TCP three-way handshake

TCP is a connection-oriented transport protocol: before either side sends real data, they run a three-way handshake to agree the connection is open and both directions work.

sequenceDiagram
    participant Client
    participant Server
    Client->>Server: SYN (let's connect)
    Server-->>Client: SYN-ACK (agreed, and let's connect back)
    Client->>Server: ACK (confirmed)
    Note over Client,Server: connection established — data can now flow

Nothing application-level (no HTTP request, no SSH banner) is sent until this handshake completes. This is also why a firewall or security group blocking the handshake makes a service look completely unreachable, not just slow.

2. Where TLS fits

For an encrypted connection (HTTPS, not plain HTTP), a second handshake happens immediately after the TCP handshake and before any HTTP data: the TLS handshake. At a high level, it lets the client verify the server's certificate and both sides agree on a shared encryption key — after which every byte that follows (including the HTTP request itself) is encrypted.

flowchart LR
    A["TCP handshake<br/>(SYN / SYN-ACK / ACK)"] --> B["TLS handshake<br/>(certificate + key exchange)"]
    B --> C["HTTP request/response<br/>(now encrypted)"]

3. HTTP — the request/response conversation

Once the connection (and TLS, if present) is established, HTTP is a stateless request/response exchange: the client sends a request (a method, a path, headers, optionally a body), and the server sends back a response (a status code, headers, optionally a body).

Status code range Meaning
2xx success
3xx redirect — go look somewhere else
4xx client error — the request itself was wrong
5xx server error — the server failed to handle a valid request

Each request/response pair is independent — HTTP itself has no memory of the previous exchange, which is why applications that need to remember you (a login session) build state on top of HTTP (cookies, tokens), rather than getting it from the protocol itself.

4. SSH — a different conversation, same transport

SSH also runs over TCP (conventionally port 22) and also negotiates encryption early in the connection — but it isn't request/response like HTTP. SSH opens an interactive, encrypted remote-shell session: authentication typically happens by key pair rather than a password, and once authenticated, the connection stays open for an ongoing two-way byte stream (a shell, a file transfer, a port forward) rather than one request producing one response.

HTTP(S) SSH
Shape request → response, then done one long-lived interactive session
Default port 80 / 443 22
Typical auth tokens/cookies, handled by the application key pair (or password), handled by the protocol itself
Purpose fetch/submit data remote shell, file transfer, port forwarding

5. Putting it together — how a client request reaches a container

flowchart LR
    U["Client types a URL"] --> D["DNS resolves the name<br/>to an IP address"]
    D --> T["TCP three-way handshake<br/>to that address"]
    T --> L["TLS handshake<br/>(if HTTPS)"]
    L --> H["HTTP request sent"]
    H --> GW["Load balancer / Ingress<br/>routes by host or path"]
    GW --> S["Service<br/>picks a healthy Pod"]
    S --> P["Container's listening port<br/>receives the request"]

Every hop in that chain is something this module already covered on its own: addressing and routing got the packet to the load balancer's address, DNS is what resolved the name in the first place, and the handshake/HTTP mechanics above are what happens the instant the connection opens.

6. How this connects

  • The load balancer / Ingress / Service hop is covered in depth in Kubernetes' Networking page (module 04, Kubernetes & Helm, §2–3) and AWS's Edge & DNS page (module 05, AWS, §4 "Choosing a load balancer") — this page is the protocol mechanics underneath both.
  • This closes the module's addressing → routing → DNS → TCP/TLS → HTTP chain from the Learn beat — you now have every layer.

Common gotchas

  • Treating a blocked handshake as "slow" instead of "unreachable." Fix: nothing application-level is sent until the TCP three-way handshake completes — if a firewall or security group blocks it, the service looks completely unreachable, not degraded.
  • Assuming HTTPS is just HTTP with encryption bolted onto the same exchange. Fix: TLS is its own handshake that runs after TCP and before any HTTP data — a stalled TLS handshake blocks the HTTP request from ever being sent, even though the TCP connection itself is already open.
  • Expecting the server to "remember" the previous request. Fix: HTTP itself is stateless — each request/response pair is independent, which is exactly why logins and sessions are built as state on top of HTTP (cookies, tokens), not provided by the protocol.
  • Assuming SSH behaves like HTTP because it also rides TCP. Fix: SSH isn't request/response — it opens one long-lived interactive session (auth typically by key pair) that stays open for an ongoing two-way byte stream, unlike HTTP's one-request-produces-one-response shape.
Check yourself — a security group is blocking a port. Does the client see a slow response or no response at all, and why?

No response at all — the service looks completely unreachable. Nothing application-level (no HTTP request, no SSH banner) is ever sent until the TCP three-way handshake completes, so blocking the handshake itself prevents the connection from opening in the first place, rather than merely delaying it.

Check yourself — an HTTPS connection's TCP handshake succeeds, but the client hangs before any HTTP response arrives. What's most likely stuck, and why does that block everything after it?

The TLS handshake. It runs immediately after the TCP handshake and before any HTTP data, so if it stalls (for example, on certificate verification), the HTTP request itself is never sent — the encrypted HTTP/response layer only starts once TLS has completed.

You can defend this when you can point to exactly where the TCP handshake ends and TLS begins, where TLS ends and the HTTP request begins, and explain the one structural difference between an HTTP exchange and an SSH session even though both can run over the identical TCP handshake.