Skip to main content

Command Palette

Search for a command to run...

Computer Networks #12: HTTP and HTTP/1.1

Updated
10 min readView as Markdown
Computer Networks #12: HTTP and HTTP/1.1
B
I am a third-year undergrad student at Chandigarh University. I am into building backend and applied AI stuff using the modern technologies. I loves to document my learning through the blogs so that I can contribute to the community.

We have already seen how DNS helps us find the server for a domain.

But finding the server is only the beginning.

Once the client knows where the server is, it needs a way to communicate with it.

This is where HTTP comes in.

HTTP is the protocol that defines how a client and a server communicate on the web.

When your browser loads:

https://example.com

a lot happens behind the scenes, but at the application layer, HTTP is responsible for things like:

"I want this resource."

and the server responds:

"Here is the resource you asked for."

Let's understand how this actually works.


What is HTTP?

HTTP stands for HyperText Transfer Protocol.

It is an Application Layer protocol used for communication between clients and servers.

The client could be:

  • A web browser

  • A mobile application

  • A command-line tool like curl

  • Another server

The server could be:

  • A web server

  • An API server

  • An application server

HTTP defines the structure of requests and responses and the rules for exchanging them.

A very simplified flow looks like this:

Client
  |
  | HTTP Request
  ↓
Server
  |
  | HTTP Response
  ↓
Client

For example:

GET /users HTTP/1.1
Host: example.com

The server might respond:

HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": 1,
    "name": "Alice"
  }
]

The client and server do not need to be written in the same programming language.

A Node.js server can communicate with a Python client.

A Go server can communicate with a Java client.

A browser written using C++ can communicate with a server written in Rust.

As long as both sides understand HTTP, they can communicate.


HTTP is a Stateless Protocol

One important property of HTTP is that it is stateless.

This means that HTTP itself does not remember previous requests.

Suppose a client sends:

Request 1
GET /profile

and later:

Request 2
GET /orders

HTTP does not automatically know that both requests came from the same user.

Each request is treated independently.

This does not mean websites cannot maintain state.

Applications use mechanisms such as:

  • Cookies

  • Sessions

  • Authentication tokens

  • Server-side session stores

to maintain state between requests.

So:

HTTP is stateless, but applications built on HTTP can maintain state.


HTTP is Media Independent

HTTP is not limited to HTML.

It can transfer many different types of data.

For example:

HTML
CSS
JavaScript
JSON
Images
Videos
PDFs
Audio
Files

The Content-Type header tells the receiver what type of data is being transferred.

For example:

Content-Type: text/html

or:

Content-Type: application/json

or:

Content-Type: image/png

This is why HTTP can be used for websites, APIs, file downloads, and many other applications.


HTTP Does Not Encrypt Data

HTTP itself does not provide encryption.

If you send:

GET /login

over plain HTTP, the data can potentially be observed by someone who can intercept the connection.

This is why we normally use:

HTTPS

HTTPS is essentially HTTP running over a connection protected by TLS.

A simplified view is:

HTTP
  ↓
TLS
  ↓
TCP
  ↓
IP

TLS provides properties such as:

  • Encryption

  • Authentication of the server

  • Integrity protection

So it is more accurate to say:

HTTP defines the application-level communication. TLS provides the security when using HTTPS.


HTTP Does Not Handle Reliable Delivery

HTTP does not itself guarantee that packets will reach their destination.

Traditionally, HTTP has been used over TCP, which provides reliable, ordered byte-stream delivery.

The stack looks like:

HTTP
  ↓
TCP
  ↓
IP

TCP handles things such as:

  • Reliable delivery

  • Ordering

  • Retransmission

  • Flow control

  • Congestion control

HTTP focuses on the application-level request and response.

Modern HTTP versions can also use different transports. For example, HTTP/3 uses QUIC, which runs over UDP.

So we should avoid thinking of HTTP as being permanently tied to TCP.


HTTP/1.0

Now let's look at the evolution of HTTP.

One important limitation of early HTTP was how it handled TCP connections.

Consider a webpage containing:

index.html
style.css
script.js
logo.png

The browser needs to make several requests.

With the typical HTTP/1.0 behavior, a connection was closed after the response was completed.

The flow looked roughly like this:

Every new connection requires the TCP three-way handshake.

So if a page needs many resources, repeatedly creating and closing TCP connections adds overhead.

This means more:

  • TCP handshakes

  • Network round trips

  • CPU work

  • Connection management overhead

HTTP/1.0 could support persistent connections using mechanisms such as:

Connection: keep-alive

But persistent connections were not the default behavior in the same way they became in HTTP/1.1.


HTTP/1.1 and Persistent Connections

HTTP/1.1 introduced persistent connections as the default.

This was a major improvement.

Instead of creating a new TCP connection for every request, the same connection could be reused.

For example:

TCP Connection
     |
     +---- Request 1
     |       ↓
     |    Response 1
     |
     +---- Request 2
     |       ↓
     |    Response 2
     |
     +---- Request 3
             ↓
          Response 3

This avoids performing a new TCP three-way handshake for every request.

The connection could remain open and be reused for multiple HTTP requests and responses.

This is called a persistent connection.


But HTTP/1.1 Still Had a Problem

Persistent connections solved one major problem, but another problem remained.

In normal HTTP/1.1 request/response usage, the client sends a request and waits for its response before sending the next request on that connection.

For example:

Request 1
   ↓
Response 1
   ↓
Request 2
   ↓
Response 2
   ↓
Request 3
   ↓
Response 3

Suppose the browser needs:

GET /style.css
GET /script.js
GET /logo.png

It cannot simply send all three requests at once on the same connection under the normal request/response behavior.

This leads to another HTTP/1.1 feature.


HTTP/1.1 Pipelining

HTTP/1.1 introduced request pipelining.

With pipelining, the client could send multiple requests without waiting for the previous response.

Instead of:

Request 1
   ↓
Response 1
   ↓
Request 2
   ↓
Response 2

it could send:

Request 1
Request 2
Request 3
   ↓
Response 1
Response 2
Response 3

This sounds like a great solution.

But there was a major problem.

Head-of-Line Blocking

HTTP/1.1 pipelining required responses to be returned in the same order as the requests.

Imagine the client sends:

Request 1 → /large-file
Request 2 → /small-file
Request 3 → /image

Suppose /large-file takes a long time to generate or transfer.

The server cannot simply return:

Response 2
Response 3
Response 1

It has to preserve the ordering.

So the small responses can get stuck behind the large response.

Request 1 ────────────────→
Request 2 ──→
Request 3 ──→

Response 1
████████████████████████████████

Response 2
        Waiting...

Response 3
        Waiting...

This is called Head-of-Line Blocking, or HOL blocking.

The request at the front of the line is blocking the requests behind it.

This made HTTP/1.1 pipelining difficult to use reliably in practice, and it was disabled or unsupported in many common client and server implementations.


Why HTTP/1.1 Still Mattered

Even with its limitations, HTTP/1.1 was a huge improvement over HTTP/1.0.

The most important improvements include:

Persistent connections

Connections could be reused instead of being created for every request.

Better caching

HTTP/1.1 provided stronger and more flexible mechanisms for controlling caches.

For example:

Cache-Control
ETag
If-None-Match
Last-Modified

These mechanisms can prevent unnecessary data from being transferred.

Host header

HTTP/1.1 made the Host header mandatory.

For example:

GET / HTTP/1.1
Host: example.com

This was extremely important for virtual hosting.

A single server IP can host many different domains:

example.com
example.org
example.net

The server can use the Host header to know which website the client wants.


Anatomy of an HTTP/1.1 Request

Let's look at a simple HTTP/1.1 request:

GET /users HTTP/1.1
Host: example.com
Accept: application/json
User-Agent: Mozilla/5.0

An HTTP request contains several important parts.

Request Method

GET

This tells the server what operation the client wants.

Common methods include:

GET
POST
PUT
PATCH
DELETE

Request Target

/users

This tells the server which resource is being requested.

HTTP Version

HTTP/1.1

This tells the server which HTTP version the client is speaking.

Headers

For example:

Host: example.com
Accept: application/json

Headers carry additional information about the request.


HTTP/1.1 Response

The server might respond:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 42

{"id":1,"name":"Alice"}

The first line contains:

HTTP/1.1 200 OK

This contains:

  • HTTP version

  • Status code

  • Reason phrase

The status code tells the client what happened.

Some common status codes are:

200 OK
201 Created
301 Moved Permanently
304 Not Modified
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
500 Internal Server Error

After the status line come the response headers.

For example:

Content-Type: application/json
Content-Length: 42

Then comes the actual response body.


HTTP Request and Response in One Picture

Putting everything together:

                 HTTP/1.1
Client                               Server
  |                                    |
  |  GET /users HTTP/1.1               |
  |  Host: example.com                 |
  |----------------------------------->|
  |                                    |
  |       HTTP/1.1 200 OK              |
  |       Content-Type: application/json
  |       Content-Length: ...          |
  |       { ... }                      |
  |<-----------------------------------|
  |                                    |

The important thing is that HTTP defines the format and semantics of this communication.

TCP handles the reliable transport underneath it.


HTTP/1.1 Connection Lifecycle

A simplified HTTP/1.1 connection looks like:

DNS
 ↓
Find server IP
 ↓
TCP 3-Way Handshake
 ↓
HTTP Request
 ↓
HTTP Response
 ↓
Reuse TCP Connection
 ↓
HTTP Request
 ↓
HTTP Response
 ↓
...

This is much more efficient than creating a new TCP connection for every HTTP request.

However, HTTP/1.1 still has limitations when many requests need to be handled concurrently.

That limitation became one of the major motivations for HTTP/2.


HTTP/1.0 vs HTTP/1.1

Feature HTTP/1.0 HTTP/1.1
Persistent connections Optional Default
Connection reuse Limited Supported by default
Host header Not required Required
Pipelining Not available Supported
Caching Basic Improved
Multiple requests on one connection Limited Better support
Head-of-Line blocking Less relevant Major issue with pipelining

The key idea to remember is:

HTTP/1.0 often created a new connection for each request, while HTTP/1.1 made connection reuse the normal behavior.

But HTTP/1.1 still processes responses in a way that can cause Head-of-Line Blocking, especially when pipelining is involved.

This is one of the problems that HTTP/2 was designed to address.