HTTP/0.9: The Minimal Protocol
Tim Berners-Lee published HTTP/0.9 in 1991 while working at CERN. His goal was modest: let physicists share research documents across the lab network. The entire protocol was one operation - GET a document by its URL. No headers, no status codes, no content types. Send 'GET /document HTTP/0.9', get back HTML. Done.
The simplicity was the feature. HTTP/0.9 was so minimal that anyone could implement it in an afternoon. Within two years there were dozens of web servers and browsers, all interoperable. This is the first API design lesson: a protocol simple enough for everyone to implement correctly beats a protocol theoretically capable of everything.
CGI: The Web Becomes Dynamic
Rob McCool at NCSA built the Common Gateway Interface in 1993 to solve a specific problem: web servers could only serve static files. CGI defined a standard way for a web server to hand a request to any executable program and return its output as the HTTP response. A Perl script, a shell script, a C program - if it could read environment variables and write to stdout, it was a CGI handler.
CGI scripts were functionally identical to modern API endpoints. The URL was the endpoint path. Query parameters were the request parameters. Stdout was the response body. A CGI script that accepted a product ID and returned a JSON-formatted product record was an API. We just did not call it that yet.
CGI's spawn-a-process-per-request model is the direct ancestor of serverless functions. AWS Lambda, Cloudflare Workers, Vercel Functions all follow the same contract: receive an HTTP request, run some code, return a response. The execution model evolved from forking processes to containers to isolates. The API shape is unchanged.
HTTP/1.0: A Real Application Protocol
HTTP/1.0 (RFC 1945, 1996) added three things that made real APIs possible. First: headers. Content-Type, Authorization, Accept - request and response metadata for negotiating how to communicate. Second: status codes. The 2xx/3xx/4xx/5xx structure gave both sides a shared vocabulary for what happened. Third: methods beyond GET. POST let clients send data to the server.
When you return a 201 Created after inserting a record, or set Content-Type: application/json on your response, you are using the language HTTP/1.0 invented in 1996. Getting status codes right is one of the most impactful and most commonly botched things in API design. HTTP/1.0 gave us the vocabulary. Using it correctly is still the job.
The Status Code Decision That Shaped API Design
HTTP/1.0's status code system was a deliberate design decision with specific intentions. The 2xx range (200-299) means success. The 3xx range (300-399) means redirect - the resource has moved and the client should follow the new URL. The 4xx range (400-499) means client error - the request was wrong and resending it unchanged will produce the same result. The 5xx range (500-599) means server error - something on the server failed and resending the request might work if the server recovers.
This structure is a decision tree for API clients. A 4xx means do not retry (fix the request). A 5xx means retry with backoff (wait for the server). A 429 (Too Many Requests) means slow down and retry. A 503 (Service Unavailable) means the server is temporarily overwhelmed - retry after the Retry-After header. Getting this structure right in your API is not just good practice; it determines whether your callers implement correct retry and error handling. An API that returns 200 with an error body in JSON (the anti-pattern called 'RPC over HTTP') breaks every HTTP client, proxy, and monitoring tool that expects the status code to mean something.
CGI's Direct Line to Modern Serverless
CGI's execution model was: one request arrives, spin up a process, execute the script, capture stdout, tear down the process. This is expensive (process creation is slow) and stateless (each request starts fresh with no shared memory). The expensive part was optimized away by FastCGI (persistent processes), mod_perl and mod_php (embedded interpreters), and eventually application servers (long-lived processes that handle many requests). The stateless part was never eliminated because statelessness is valuable, not a limitation.
AWS Lambda (2014) returned to the CGI model's essential shape. One event arrives, a container is spun up (or reused from a warm pool), the function executes, the response is returned, the container may be destroyed. Lambda's cold start problem is the CGI process-creation overhead in modern guise. Lambda's execution context reuse is the FastCGI persistent process optimization in modern guise. Understanding CGI helps you understand why serverless functions behave the way they do: they are CGI scripts with better tooling and infrastructure.
HTTP Methods and Idempotency in Interviews
- Safe methods (GET, HEAD, OPTIONS) must not modify server state. Caches, proxies, and clients can retry them freely.
- Idempotent methods (GET, HEAD, PUT, DELETE, OPTIONS) can be called multiple times with the same result. Crucial for automatic retries.
- POST is neither safe nor idempotent. Submitting a form twice creates two records. This is why checkout flows need idempotency keys.
- The 201 Created response should include a Location header with the URL of the new resource. Most APIs skip this and should not.
- 205 Reset Content (rarely used) tells the client to reset its UI after a successful form submission - the correct response for 'your POST succeeded, now clear the form'.
The Content-Type Header: Still Getting It Wrong in 2024
HTTP/1.0's Content-Type header is one of the most commonly misused parts of the HTTP specification. The header should describe what the body actually contains - not what the sender intended to send, not what the endpoint expects, but what the bytes in the body represent. Returning text/html when the body is actually application/json is a bug. Setting Content-Type: application/json when the body is empty (for a 204 No Content response) is a bug. Accepting any Content-Type without validating that the body matches is a security vulnerability.
MIME sniffing - where browsers attempt to guess the content type when the Content-Type is missing or wrong - has caused security vulnerabilities where attackers upload files with innocent content types that browsers interpret as executable scripts. The X-Content-Type-Options: nosniff header disables MIME sniffing in browsers. This header, introduced to fix a problem caused by lazy Content-Type usage, is now a required security header for any API response that might be rendered by a browser. Content-Type correctness is not a cosmetic concern - it is a security concern.
CGI also taught the web a hard lesson about process isolation. Each CGI request spawned a new process, and that process had full access to the server filesystem. The first web application security vulnerabilities - path traversal, command injection - were discovered in CGI scripts within a year of widespread CGI adoption. The lesson was that any interface between a network and code execution is a security boundary. FastCGI, mod_perl, and eventually application servers with internal routing exist because CGI showed that executing arbitrary code in response to HTTP requests requires more isolation than a Unix process with filesystem access.
The Status Code Decision That Shaped API Design
HTTP/1.0's status code system was a deliberate design decision with specific intentions. The 2xx range (200-299) means success. The 3xx range (300-399) means redirect - the resource has moved and the client should follow the new URL. The 4xx range (400-499) means client error - the request was wrong and resending it unchanged will produce the same result. The 5xx range (500-599) means server error - something on the server failed and resending the request might work if the server recovers.
This structure is a decision tree for API clients. A 4xx means do not retry (fix the request). A 5xx means retry with backoff (wait for the server). A 429 (Too Many Requests) means slow down and retry. A 503 (Service Unavailable) means the server is temporarily overwhelmed - retry after the Retry-After header. Getting this structure right in your API is not just good practice; it determines whether your callers implement correct retry and error handling. An API that returns 200 with an error body in JSON (the anti-pattern called 'RPC over HTTP') breaks every HTTP client, proxy, and monitoring tool that expects the status code to mean something.
CGI's Direct Line to Modern Serverless
CGI's execution model was: one request arrives, spin up a process, execute the script, capture stdout, tear down the process. This is expensive (process creation is slow) and stateless (each request starts fresh with no shared memory). The expensive part was optimized away by FastCGI (persistent processes), mod_perl and mod_php (embedded interpreters), and eventually application servers (long-lived processes that handle many requests). The stateless part was never eliminated because statelessness is valuable, not a limitation.
AWS Lambda (2014) returned to the CGI model's essential shape. One event arrives, a container is spun up (or reused from a warm pool), the function executes, the response is returned, the container may be destroyed. Lambda's cold start problem is the CGI process-creation overhead in modern guise. Lambda's execution context reuse is the FastCGI persistent process optimization in modern guise. Understanding CGI helps you understand why serverless functions behave the way they do: they are CGI scripts with better tooling and infrastructure.
HTTP Methods and Idempotency in Interviews
- Safe methods (GET, HEAD, OPTIONS) must not modify server state. Caches, proxies, and clients can retry them freely.
- Idempotent methods (GET, HEAD, PUT, DELETE, OPTIONS) can be called multiple times with the same result. Crucial for automatic retries.
- POST is neither safe nor idempotent. Submitting a form twice creates two records. This is why checkout flows need idempotency keys.
- The 201 Created response should include a Location header with the URL of the new resource. Most APIs skip this and should not.
- 205 Reset Content (rarely used) tells the client to reset its UI after a successful form submission - the correct response for 'your POST succeeded, now clear the form'.
The Content-Type Header: Still Getting It Wrong in 2024
HTTP/1.0's Content-Type header is one of the most commonly misused parts of the HTTP specification. The header should describe what the body actually contains - not what the sender intended to send, not what the endpoint expects, but what the bytes in the body represent. Returning text/html when the body is actually application/json is a bug. Setting Content-Type: application/json when the body is empty (for a 204 No Content response) is a bug. Accepting any Content-Type without validating that the body matches is a security vulnerability.
MIME sniffing - where browsers attempt to guess the content type when the Content-Type is missing or wrong - has caused security vulnerabilities where attackers upload files with innocent content types that browsers interpret as executable scripts. The X-Content-Type-Options: nosniff header disables MIME sniffing in browsers. This header, introduced to fix a problem caused by lazy Content-Type usage, is now a required security header for any API response that might be rendered by a browser. Content-Type correctness is not a cosmetic concern - it is a security concern.
CGI also taught the web a hard lesson about process isolation. Each CGI request spawned a new process, and that process had full access to the server filesystem. The first web application security vulnerabilities - path traversal, command injection - were discovered in CGI scripts within a year of widespread CGI adoption. The lesson was that any interface between a network and code execution is a security boundary. FastCGI, mod_perl, and eventually application servers with internal routing exist because CGI showed that executing arbitrary code in response to HTTP requests requires more isolation than a Unix process with filesystem access.