When a browser makes an HTTP request, it looks like this:
GET /search?q=cats HTTP/1.0
Host: www.google.com
Connection: close
… to which the server should send a response that looks like this:
HTTP/1.0 200 Success
Content-Type: text/html; charset=UTF-8
Content-Length: 1337
<!DOCTYPE html>
<html>
<head><title>cats - Google Search</title>
<body>
<h1>About 415,000,000 results</h1>
…
</body>
</html>
Any code running on the server that listens for requests on a TCP socket, reads the request, and replies with the appropriate response will suffice. One dumb way is just to spit out a canned response to anyone who connects to TCP port 80, using a shell script:
$ nc -l 8000 <<'RESPONSE'
HTTP/1.0 200 Success
Content-Type: text/html; charset=UTF-8
Content-Length: 1337
<!DOCTYPE html>
<html>
<head><title>cats - Google Search</title>
<body>
<h1>About 415,000,000 results</h1>
…
</body>
</html>
RESPONSE
Of course, that technique only barely appears to comply with the HTTP protocol.
A step up from that canned response is this simple Python program, which uses the http.server
library in Python 3.
#!/usr/bin/python3
import http.server
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
payload = '<!DOCTYPE html>... insert cats here ...'.encode('UTF-8')
self.send_response(200)
self.send_header('Content-Type', 'text/html; charset=UTF-8')
self.send_header('Content-Length', len(payload))
self.end_headers()
self.wfile.write(payload)
http.server.HTTPServer(('', 80), Handler).serve_forever()
The HTTP server can be written in any language; that is just an example. Obviously, this example is very rudimentary. The payload is hard-coded — the program completely disregards the contents of the request — the URL, query string, Accept-Language header, etc. You could add code to generate meaningful responses based on the request, but then the code would get very complex. Besides, programmers would rather focus on writing the web application, not having to worry about the details of how to handle an HTTP request.
A more appropriate solution would be to use a web server, such as Apache HTTPD, IIS, or nginx. A web server is just a program that listens on the relevant TCP sockets, accepts multiple requests (possibly simultaneously), and decides how to generate a response based on the request URL, headers, and other rules. Ideally, many of the details, such as SSL, access control, and resource limits are taken care of via configuration rather than code. Much of the time, the web server will formulate a response that consists just of content from files in the filesystem.
For dynamic content, though, the web server can be configured to execute some code to generate the response. One mechanism for doing that is with CGI — the server sets some environment variables based on the request, executes a program, and copies its output to the TCP socket. A slightly more sophisticated solution would be to have a module that adds support to the web server for calling code in another programming language (e.g. mod_php for Apache). Yet another option is to write the web server in the same language as the web application, in which case the request dispatch is just a function call. That is the case with node.js and Java servlet engines such as Apache Tomcat.
The choice of technology is really up to you, and depends on the programming language you prefer to use, the hosting environment that is available to you, performance requirements, popular opinion, and passing fads. CGI, for example, has not been favoured lately, since the need to launch external programs limits scalability.