86

I'm trying to issue a GET command to my local server using netcat by doing the following:

echo -e "GET / HTTP/1.1\nHost: localhost" | nc localhost 80

Unfortunately, I get a HTTP/1.1 400 Bad Request response for this. What, at the very minimum, is required for a HTTP request?

Naftuli Kay
  • 87,710
  • 93
  • 269
  • 411
  • 1
    Not sure, it works for me (Bash, Apache, Ubuntu) after I add `\n\n` to the back. But I think HTTP is sensitive to the nature of line endings, so maybe double-check that. – Kerrek SB Jul 13 '11 at 22:07
  • Echo has some issues in handling \r\n characters so same command using printf works but not with echo. http://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo – harry Jun 22 '15 at 10:35
  • related http://serverfault.com/questions/163511/what-is-the-mandatory-information-a-http-request-header-must-contain – Ciro Santilli OurBigBook.com Jul 19 '16 at 16:21

9 Answers9

91

if the request is: "GET / HTTP/1.0\r\n\r\n" then the response contains header as well as body, and the connection closes after the response.

if the request is:"GET / HTTP/1.1\r\nHost: host:port\r\nConnection: close\r\n\r\n" then the response contains header as well as body, and the connection closes after the response.

if the request is:"GET / HTTP/1.1\r\nHost: host:port\r\n\r\n" then the response contains header as well as body, and the connection will not close even after the response.

if your request is: "GET /\r\n\r\n" then the response contains no header and only body, and the connection closes after the response.

if your request is: "HEAD / HTTP/1.0\r\n\r\n" then the response contains only header and no body, and the connection closes after the response.

if the request is: "HEAD / HTTP/1.1\r\nHost: host:port\r\nConnection: close\r\n\r\n" then the response contains only header and no body, and the connection closes after the response.

if the request is: "HEAD / HTTP/1.1\r\nHost: host:port\r\n\r\n" then the response contains only header and no body, and the connection will not close after the response.

Abhishek Oza
  • 3,340
  • 1
  • 27
  • 35
  • 1
    is it specific to a particular http server or is it mandated by http rfcs? – jfs Oct 07 '14 at 18:02
  • 2
    @J.F.Sebastian For HTTP 1.1 connections: http://tools.ietf.org/html/rfc2616#section-8 , For HTTP 1.1 Host: http://tools.ietf.org/html/rfc2616#section-14.23 , For HTTP 1.0 http://tools.ietf.org/html/rfc1945 , For HTTP 0.9 http://www.w3.org/Protocols/HTTP/AsImplemented.html , HTTP Persistent connection: http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Persistent_connections (for persistent connections explanation, i am giving wikipedia link, as it has better explanation) – Abhishek Oza Oct 08 '14 at 07:21
  • 4
    RFC 2616 is obsolete. Please don't cite it anymore except for historical purposes. – Julian Reschke Oct 08 '14 at 11:45
  • In the light of @JulianReschke's comment, here I am giving another link: http://www.w3.org/Protocols/#rfc723x , for HTTP specification. Please find the links under "RFC 723X" on that page to read about latest RFC for HTTP specification. – Abhishek Oza Oct 09 '14 at 07:13
  • Is "GET /\r\n\r\n" http/1.1 compliant? – Kenneth Dec 21 '18 at 06:29
  • @Kenneth "GET /\r\n\r\n" is HTTP/0.9 request. HTTP/1.1 servers conventionally provide backward compatibility. – Abhishek Oza Dec 21 '18 at 13:43
  • it is automatically adding amp; on the URL used by http check request each time when i save the configuration. did anyone know the reason? `/jsonp/FreeForm?&maxrecords=10&format=XML&` – T.Anand Mar 04 '22 at 16:59
  • @T.Anand Your Query String is getting URL-Form-Encoded. This depends on your program. – Abhishek Oza Mar 10 '22 at 20:18
  • @AbhishekOza, i am giving the simple REST request URL on the http health check, so it keeps on adding amp; whenever i am saving the configuration, if i missed to remove the amp; from the URL then the Test getting failed, is there any way to look at pfsense HAProxy, Original URL - `/jsonp/FreeForm?&maxrecords=10&format=XML`, after i saved the configuration it is getting changed to `/jsonp/FreeForm?&maxrecords=10&format=XML&` – T.Anand Mar 11 '22 at 16:08
73

It must use CRLF line endings, and it must end in \r\n\r\n, i.e. a blank line. This is what I use:

printf 'GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n' |
  nc www.example.com 80

Additionally, I prefer printf over echo, and I add an extra header to have the server close the connection, but those aren’t needed.

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
13

See Wiki: HTTP Client Request (Example).

Note the following:

A client request (consisting in this case of the request line and only one header) is followed by a blank line, so that the request ends with a double newline, each in the form of a carriage return followed by a line feed. The "Host" header distinguishes between various DNS names sharing a single IP address, allowing name-based virtual hosting. While optional in HTTP/1.0, it is mandatory in HTTP/1.1.

The absolute minimum (if removing the Host is allowed ;-) is then GET / HTTP/1.0\r\n\r\n.

Happy coding

  • this is what I was looking for. Thank you. (also the one that actually answers what's wrong with the HTTP/1.1 request above) – Wyatt Ward Aug 03 '16 at 21:08
10

I was able to get a response from my Apache server with only the requested document, no response header, with just

GET /\r\n

If you want response headers, including the status code, you need one of the other answers here though.

jfklein
  • 877
  • 1
  • 11
  • 13
  • 7
    Interesting. This uses the original [HTTP/0.9](http://www.w3.org/Protocols/HTTP/AsImplemented.html) protocol from the early 90s. I'm very impressed Apache still responds to it. – Ben Russell May 10 '13 at 13:37
  • Nginx also still supports this. From what I've seen, whenever someone develops a new HTTP server, they just copy all the kludges of other existing implementations, and this is why these things persist over decades. – Tronic Oct 14 '19 at 07:43
7

The fact of the 400 Bad Request error itself does not imply that your request violates HTTP. The server very well could be giving this response for another reason.

As far as I know the absolute minimum valid HTTP request is:

GET / HTTP/1.0\r\n\r\n
wberry
  • 18,519
  • 8
  • 53
  • 85
  • 7
    Actually, the absolute minimum is "GET /\r\n". If no version is specified, the server is supposed to assume HTTP/0.9. In HTTP/0.9, request headers aren't allowed, so you don't need the blank line to terminate them. I wouldn't expect this to be supported everywhere, however, as HTTP/0.9 clients are *very* rare in reality, so servers may well not be tested with them. – Jules May 20 '12 at 10:41
  • 3
    HTTP/1.0 accepts no headers, HTTP/1.1 has a mandatory header: Host. – Pedro Nov 11 '13 at 00:59
6

Please, please, please, do not implement your own HTTP client without first reading the relevant specs. Please read and make sure that you've fully understood at least RFC 2616. (And if you're ambitious, RFC 7230 through 7235).

While HTTP looks like an easy protocol, there are actually a number of subtle points about it. Anyone who has written an HTTP server will tell you about the workarounds he had to implement in order to deal with incorrect but widely deployed clients. Unless you're into reading specifications, please use a well-established client library; Curl is a good choice, but I'm sure there are others.

If you're going to implement your own:

  • do not use HTTP/0.9;
  • HTTP/1.0 requires the query line and the empty line;
  • in HTTP/1.1, the Host: header is compulsory in addition to the above.

Omitting the Host: header in HTTP/1.1 is the most common cause of 400 errors.

Community
  • 1
  • 1
jch
  • 5,382
  • 22
  • 41
  • In 2019 it seems that HTTP/1.0 has finally vanished and that you only need to support HTTP/1.1 (plus HTTP/2 and HTTP/3 if you wish). – Tronic Oct 14 '19 at 07:48
0

The really REALLY BARE minimum, is not using netcat, but using bash itself:

user@localhost:~$ exec 3<>/dev/tcp/127.0.0.1/80
user@localhost:~$ echo -e "GET / HTTP/1.1\n" >&3
user@localhost:~$ cat <&3
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.7.6
Date: Mon, 13 Oct 2014 17:55:55 GMT
Content-type: text/html; charset=UTF-8
Content-Length: 514

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for /</title>
<body>
<h2>Directory listing for /</h2>
<hr>
<ul>
</ul>
<hr>
</body>
</html>
user@localhost:~$ 
Marcel
  • 1,266
  • 7
  • 18
  • 1
    Actually this is an invalid HTTP/1.1 request. 1. It should be using CRLF for line termination. 2. It should include `Host` header. – Pijusn Jan 22 '15 at 13:56
  • Webserver responded with 200 OK not with 400 Bad request. – Marcel Jan 22 '15 at 15:39
  • 4
    It does not mean request was compliant with HTTP/1.1. Web servers in production are known to accept requests that are not 100% compliant with the HTTP/1.1. Reasons might vary but you should never rely on server understanding what you want if you send an invalid request. For instance, missing `Host` header is a very common reason for 400 because server simply cannot know what you want without it (Apache uses it for routing, for instance). – Pijusn Jan 22 '15 at 16:09
  • Yes, apache's vhost routing does that, but my example was requesting simplehttpserver localhost. – Marcel Jan 22 '15 at 16:35
  • 1
    Some good soul zeroing me on this answer. I really don't get why I got negative score here. I've answered strictly. – Marcel Jun 29 '15 at 14:22
  • FWIW, by minimum I meant the minimum actual request, not the code to produce such a request. – jfklein Jan 16 '17 at 23:09
  • does not work with tomcat, but works if you use GET / HTTP/1.0\r\n\r\n – shikida Nov 12 '21 at 15:42
0

You should add an empty line: \r\n\r\n

http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Client_request

Dor
  • 7,344
  • 4
  • 32
  • 45
0

For HTTP 1.1, the minimum seems to be

const std::string host = "example.com";
std::stringstream http;
http << "GET /index.html HTTP/1.1\r\n";
http << "Host: " << host << "\r\n";
http << "Connection: close\r\n";
//end
http << "\r\n";

Without the 'Host' header a HTTP 400 Bad Request is returned (testing on a Apache server) Without 'Connection: close' , the connection will not close (so, no response)

Pedro Vicente
  • 681
  • 2
  • 9
  • 21