Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Fundamentals › Networking

HTTP requests

What does an HTTP request look like as text?

An HTTP request that a browser sends to a server is a few lines of text in a fixed shape. The first line (the request line) holds the method, the path, and the version. Below it come lines called headers, in the form "name: value", and an empty line, a line with no text at all, tells the server that the headers are over (a request with no body ends here). The server reads this text to find out what the request asks for. In this lab you change or remove the first line, the Host line, and the last empty line one at a time, and send the text exactly as it is to the same server (a server that has only the /hello address), to see whether the server reads the text as a request, what response it sends back, and whether the request reaches the server code we wrote. The (empty line) in the code box is a way to show a line with no text at all, so that you can see it.

Checked against real execution · Sent the text of all 12 combinations exactly as it is to an http server on Node.js 24.18.0 and checked against the results

Concepts in this lab

Request line
The request line is the very first line of an HTTP request, and it holds the method, the request target (usually a path), and the HTTP version in order, separated by spaces. For example, GET /hello HTTP/1.1 means "send GET (a request to get something) for /hello, by the rules of HTTP/1.1". The server uses this line to find out what the request asks for. If the three parts do not have the right shape, the server cannot read the text as a request, and RFC 9112 recommends answering a wrong request line with a 400 response (section 3.2).
Host header
Host is a header that says which site (host name and port) a request came to, and its value is written like localhost:8080. One server can serve several sites, so the server uses it to tell which site a request is for. RFC 9112 says that a client must send a Host header in every HTTP/1.1 request, and that a server must respond with 400 (Bad Request) to an HTTP/1.1 request that has no Host (section 3.2). The http module of Node.js also answers with 400, without passing the request to the application code, when Host is missing.
End of an HTTP request (the empty line)
An HTTP request is written in the order of the request line, the header lines, and an empty line with no text at all, and the empty line is the mark that the headers have ended (RFC 9112 section 2.1). Line breaks are written as CRLF (the two characters carriage return and line feed). The server starts to handle the request only after this empty line arrives and it knows the request has come in full, so if the empty line does not come, the server waits, thinking more text will follow. In a request with a body, the body starts after the empty line.
HTTP status codes
A three-digit number that shows the result of the server's handling of a request. 2xx means success, 3xx means a move to another location (redirection), 4xx means a problem with the client's request, and 5xx means an error on the server side. Common ones are 200 (success), 201 (created), 400 (bad request), 401 (authentication required), 403 (no permission), 404 (not found), and 500 (server error).

Try these

Running with the default settings

  1. When you type localhost:8080/hello in the address bar of the browser, the browser connects to the server and sends the text below. The server is one that has only the /hello address.
  2. Node.js, which makes up the server, reads the first line. The text it read is "GET /hello HTTP/1.1". The first line (the request line) has three things written in order. GET is the method, which says what the request wants to do (it asks to get something), /hello is the path, which says what it is about, and HTTP/1.1 is the version, which says what rules the text was written by. All three parts are there, so it is read as a request line.
  3. Node.js reads the next line. The text it read is "Host: localhost:8080". A line in the form "name: value" is called a header. The Host header tells which site (address) this request came to.
  4. Node.js reads the next line and finds an empty line with no text at all (the (empty line) on the screen). An empty line marks the end of the headers, so Node.js decides that the request has arrived in full.
  5. In the request Node.js read, the method is GET and the path is /hello. The shape is right and Host is there, so Node.js hands this request to the server code we wrote.
  6. The server code we wrote looks at the path. /hello is an address we made, so it builds a 200 response with the body "Hello". This code is not shown on the screen, but it is made to answer this way only to a GET request whose path is /hello.
  7. The browser receives the 200 response. The body is "Hello".

Result of this run

Code

GET /hello HTTP/1.1
Host: localhost:8080
(empty line)

Assumptions of this model

The server is a single one made with the http module of Node.js 24.18.0. It returns 200 with the body "Hello" for GET /hello and 404 for any other path. All 12 combinations were sent character for character, with the net module, to this server on 127.0.0.1 (lines end with CRLF), and checked against the model: the status line and body of the response received, whether our request handler function was called, and the method and url that function received. The (empty line) on the screen is a way to show a line with no text at all, so that you can see it, and for the combinations with no empty line, the text was sent only as far as the line break of the last line. "No response (waiting)" means that no byte of a response came for 1.5 seconds after the text was sent. How long Node.js waits before giving up was not examined. For the first line with a wrong shape (/hello), Node.js cannot read it as a request, raises a clientError (HPE_INVALID_METHOD), and sends back 400 by itself, and 400 also came when only the first line was sent, whatever the later lines were. For a request with no Host, Node.js sends back 400 by itself after the empty line arrives and the headers end, before it calls our code (the request handler function was not called and no clientError was raised). When the same text was sent to a server with the Host check turned off (requireHostHeader: false), the handler function received a GET request, so the text had been read correctly up to the request line. These are behaviors observed on this Node.js version. What was checked against the documentation is RFC 9112 section 2.1 (message format: the empty line after the request line and headers), section 3 (the request line: three parts, method, request target, and version), section 3.2 (a server must respond with 400 to an HTTP/1.1 request that has no Host), and RFC 9110 section 7.2 (Host), section 15.5.1 (400), and section 15.5.5 (404). The request on the screen has the minimum shape, with only Host left as a header, and only GET requests without a body were covered. How a 400 is returned can differ from server to server, and this screen shows the behavior of a Node.js server. The server code is not shown on the screen. The in-depth details on the same subject (the shape of responses, all the status codes) are covered in the in-depth lab "HTTP messages and status codes".

Previous lab: DNS · Next lab: HTTP methods

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these