After the release of HTTP/0.9 the Web didn't stay in the same state for too long, the team behind the HTTP initiative was already looking for more; this can be evidenced in a mailing list for discussing the Web, when Jean-François Groff, member of the team behind HTTP said:
Your impression may come from the fact that in the current
implementation, as HTTP can only return HTML documents, the distinction between them is not clear. The new phase of HTTP that we are currently designing will be able to handle documents of arbitrary types.
We can find even more emails showing how the protocol needed a better mechanism to exchange more than just HTML documents.
In December 1992, Tim Berners-Lee proposed a "protocol upgrade" to support a bunch of new things, including richer requests, media types, and additional metadata. We are going to talk about this more in detail shortly.
Interesting fact: if you look at the email, you may notice the identifier
HTRQ/1.0in the proposed request format. HTRQ was an experimental proposal for sending additional information from a Web client to a server. The proposal itself didn't become HTTP/1.0, but it gives us a glimpse of the ideas being explored at the time.
The next version started taking shape around a much richer idea: requests and responses would no longer contain only a resource and its contents. They would also carry information describing what was being requested, what was being returned, and what happened during the request.
In 1993, the Web exploded in usage, and a new browser entered the market: Mosaic, the Web browser created by the National Center for Supercomputing Applications (NCSA) that helped to popularize the Web.
Later, in November of that same year, Mosaic 2.0 was released. NCSA's own Mosaic documentation says Mosaic 2.0 "speaks the HTTP/1.0 protocol" and says HTTP/1.0 servers are already fairly widespread. Mosaic 2.0 is also a good example of how much the Web had changed: browsers were now dealing with HTML, images, audio, video, etc.
Notice that HTTP/1.0 was already being used by clients and servers even though no final RFC existed yet.
Between 1993 and 1995, HTTP/1.0 specifications circulated as Internet-Drafts while implementations continued to converge. The IETF HTTP Working Group was eventually created to document and formalize those existing practices.
In May of 1996, RFC 1945 finally appeared as an Informational RFC. This was one of the biggest steps in HTTP's history because the behavior clients and servers had been implementing for years was finally documented in a common specification.
RFC 1945
The following excerpt from section 1.3 of RFC 1945 gives a very good summary of how HTTP/1.0 works:
The HTTP protocol is based on a request/response paradigm. A client establishes a connection with a server and sends a request to the server in the form of a request method, URI, and protocol version, followed by a MIME-like message containing request modifiers, client information, and possible body content. The server responds with a status line, including the message's protocol version and a success or error code, followed by a MIME-like message containing server information, entity metainformation, and possible body content.
My intention is not to dump the entire specification into this article, but to bring out the most important changes in HTTP/1.0 compared with HTTP/0.9.
Headers
This was probably one of the biggest improvements compared with HTTP/0.9.
HTTP had no way to carry extra information in either the request or the response. The client could only ask for a resource, and the server could only return its contents. That was enough for the very first stage of the Web, but it quickly became too limited.
As the Web started dealing with more than simple HTML documents, both sides needed a way to exchange metadata. The client needed to say things such as what kind of content it could accept, and the server needed to describe what kind of content it was returning. HTTP also started needing a way to carry other kinds of information, such as content length, modification dates, authentication data, and more.
Instead of inventing a completely new format for this metadata, HTTP borrowed ideas from something that was already widely used on the Internet: electronic mail.
Internet Mail already had a message format where a set of fields appeared before the actual body of the message. RFC 822, published in 1982, described an email as a group of header fields followed by an optional body, separated by an empty line.
From: jorge@example.com
To: someone@example.com
Subject: Hello
Date: Sat, 29 Aug 2026 15:30:00 -0400
Hello world!
That structure was already a good fit for what HTTP needed.
But traditional Internet Mail had another limitation of its own: it was designed mostly around plain ASCII text. It did not have a standard way to describe arbitrary content such as images, audio, video, or different text encodings.
This is where Multipurpose Internet Mail Extensions (MIME) comes into the story. Its purpose was to extend the existing email format so a message could carry and describe different kinds of content without throwing away the message structure that Internet Mail already used. MIME introduced concepts such as Content-Type, allowing the sender to tell the receiver what kind of data was contained in the body.
For example, an email containing plain text could say:
From: jorge@example.com
To: someone@example.com
Subject: Hello
MIME-Version: 1.0
Content-Type: text/plain
Hello world!
But if the message contained an image, MIME could describe and encode it differently:
MIME-Version: 1.0
Content-Type: image/gif
Content-Transfer-Encoding: base64
R0lGODlhAQABAIAAAAAAAP...
And this was very close to what HTTP needed. Instead of only sending the contents of a document, the protocol could now describe that content before sending it.
RFC 1945 explicitly says that HTTP messages use a format similar to Internet Mail and MIME, and describes both requests and responses as containing a “MIME-like message.” Notice the wording: MIME-like, not simply MIME.
Requests
Requests changed considerably too. In HTTP/0.9, we had something as simple as:
GET /index.htmlCRLF
HTTP/1.0 made the request much richer. The first line, called the Request-Line, now contained three pieces of information: the method, the resource being requested, and the HTTP version. After the Request-Line came the headers we discussed before. An empty line marked the end of those headers and, depending on the request, a body could follow.
GET /index.html HTTP/1.0 | Request Line
--------------------------------|----------------
User-Agent: AnyBrowser/1.0 |
Accept: text/html | Headers
< optional body >
The Request-Line has the following format:
Method SP Request-URI SP HTTP-Version CRLF
Notice that "SP" stands for the ASCII character of space, according to the RFC definition
Methods
In HTTP/0.9 we only had GET, as HTTP evolved, other methods started appearing in proposals and implementations. One method in one of those proposals was PUT, but RFC 1945 defined GET, HEAD, and POST as the common HTTP/1.0 methods.
-
GET: kept its familiar purpose of retrieving a resource. -
HEAD: worked similarly toGET, but the server returned only the response headers, without the response body. -
POST: was more interesting because the client could now send information to the server as part of the request body.
Here's an example of a POST request:
POST /messages HTTP/1.0
Content-Type: text/plain
Content-Length: 12
Hello world!
Response
Responses changed in a very similar way to requests. Remember that in HTTP/0.9 the server basically returned the document itself, there was nothing before the content telling the client what had happened or what kind of data it was receiving.
HTTP/1.0 introduced a more complete specification. It starts with a Status-Line, followed by headers, an empty line, and optionally a response body.
HTTP/1.0 200 OK | Status-Line
-----------------------------------------|----------------
Content-Type: text/html |
Content-Length: 1256 | Headers
Last-Modified: Fri, 28 Aug 2026 |
19:20:00 GMT |
-----------------------------------------|----------------
<html> |
<body> |
Hello! | Response Body
</body> |
</html> |
Status-Line
In the previous article we saw that an HTTP/0.9 server basically returned something like:
GET /index.html
<html>
hello world
</html>
and,
GET /missing.html
<html>
An error has occurred with your request :(
</html>
And we were saying that there wasn't a fully deterministic way for computers to detect errors.
HTTP/1.0 introduced the concept of "Status-Line", which became the first line of the response message.
The Status-Line contains something called a "Status Code": a three-digit number that tells software what happened with the request. Unlike the HTML error page we saw in HTTP/0.9, a program no longer needed to inspect the response content and guess whether something had gone wrong.
Along with the status code, the Status-Line contains a "Reason-Phrase": a short human-readable description of the result.
The RFC explicitly says:
the status code is intended for automata, while the reason phrase is intended for humans.
Now the response in HTTP/1.0 looked similar to:
HTTP/1.0 200 OK
<html>
...
</html>
and,
HTTP/1.0 404 Not Found
<html>
...
</html>
The Status-Line has the following format:
HTTP-Version SP Status-Code SP Reason-Phrase CRLF
Status codes were grouped into classes:
-
2xx: Successful -
3xx: Redirection -
4xx: Client Error -
5xx: Server Error
The first digit already gave software a general idea of what happened, while the complete three-digit code described the specific result.
Backward compatibility
There was still one problem. HTTP/1.0 was already very different from HTTP/0.9, but the Web couldn't simply stop supporting all the clients that were already using the older protocol.
To solve this, the RFC 1945 added the distinction between a Simple-Request and Full-Request, as well as Simple-Response and Full-Response.
A Simple-Request looked like:
GET /index.html
while a Full-Request looked like:
GET /index.html HTTP/1.0
Accept: text/html
Notice something familiar? The Simple-Request is basically the HTTP/0.9 request we implemented in the previous article.
An HTTP/1.0 server receiving a Simple-Request had to answer with a Simple-Response: just the entity body, without a Status-Line or HTTP headers.
So HTTP/1.0 didn't completely replace HTTP/0.9 overnight. The new protocol was designed so old-style requests could still be understood while newer clients could take advantage of the richer message format.
Connection lifecycle
Even though the messages became considerably richer, one fundamental part had not changed yet: HTTP/1.0 still used a new TCP connection for each request/response exchange by default.
Client Server
| |
| ---------------- SYN ---------------------------> |
| <--------------- SYN-ACK ------------------------ |
| ---------------- ACK ---------------------------> |
| |
| TCP connection established |
| |
| -------- GET /index.html HTTP/1.0 --------------> |
| |
| <----------- HTTP/1.0 200 OK -------------------- |
| Content-Type: text/html |
| |
| <html>...</html> |
| |
| <--------------- FIN ---------------------------- |
| ---------------- ACK ---------------------------> |
| ---------------- FIN ---------------------------> |
| <--------------- ACK ---------------------------- |
| |
| TCP connection closed |
Notice the FIN flag in the TCP segment. In HTTP/1.0, when a response didn't include a
Content-Lengthheader, the end of the response body could be determined by the server closing the connection. RFC 1945 explicitly describes this behavior.
This would eventually become another limitation as Web pages started requiring more and more resources. But that is a problem for the next version of HTTP.
Conclusion
The protocol didn't throw away the simplicity that made HTTP useful in the first place. Instead, it added structure around it: headers, methods, status codes, media types, request bodies, and a version identifier. And even with all those changes, it still kept a way to communicate with clients speaking the older protocol.
“The art of progress is to preserve order amid change, and to preserve change amid order.” — Alfred North Whitehead, Process and Reality (1929)
Top comments (0)