HTTP Server in C++
C and C++ are powerful languages for anyone curious about the inner workings of computers. But have you ever wondered how something as common as an HTTP server actually works under the hood? When we use frameworks like Flask in Python or Express in Node.js, most of the complexity is hidden from us. In reality, though, HTTP is just a plain text protocol where your browser sends a request for a resource, and the server replies with a properly formatted response. By peeling back the abstractions and exploring this in C or C++, you can see the web in its rawest form and truly understand how these everyday interactions take place.
Basics of HTTP
HTTP (HyperText Transfer Protocol) is all about exchanging requests and responses.
Everything here is just text, but structured in a fixed format so both the browser and server can understand it.
Where C/C++ Comes In:
When you build a server in Python (Flask/Django) or JavaScript (Node.js), a lot of the low-level work is hidden from you. But in C/C++, you can interact directly with the operating system using system calls. This gives you full control over how your server is created and how it talks to the network.
At the heart of this is socket programming. A socket is like a communication endpoint — think of it as a “plug” your program uses to send and receive data over the network. TCP (Transmission Control Protocol) uses sockets to establish reliable communication between two machines.
Here’s the process of building an HTTP server over TCP in C++ step by step:
1. Creating a socket
We start by calling the socket() function. This tells the OS: “Hey, I want to open a door to the network.”
Example: int server_socket = socket(AF_INET, SOCK_STREAM, 0);
2. Bind it to an IP and port
Next, we attach (bind) this socket to an IP address and port so that clients know where to connect.
3. Listen for incoming connections
After binding, we tell the OS: “I’m ready to accept clients.”
Recommended by LinkedIn
4. Accept a client connection
When a browser or another client connects, we call accept().
5. Receive the request
Now that a client is connected, we read the data it sends using read().
GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not;A=Brand";v="99", "Google Chrome";v="139", "Chromium";v="139"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9,hi;q=0.8,fr;q=0.7
6. Send back a response
We build an HTTP response string (headers + body) and send it using send(). For example:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 44
Connection: close
<h1>Your C++ server is live and running</h1>
This makes the browser render the message.
This is how we make a basic TCP server. Since browsers use TCP under the hood, once we bind our process to localhost:8080, every request made to that port will be handled by our server.
Demo: Minimal HTTP Server in C++
In my basic implementation, I’ve made a C++ server for linux that always returns the same message for any request: "Your C++ server is live and running". Once you compile and run the program, just open your browser and visit http://localhost:8080 — you’ll see the message rendered on the page.
#include <string>
#include <cstring>
#include <vector>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <bits/stdc++.h>
using namespace std;
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// Reuse address and port
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY; // Listen on all interfaces
address.sin_port = htons(8080); // Port 8080
// Bind socket to IP:Port
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// Start listening
if (listen(server_fd, 4) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
cout << "Server running on http://localhost:8080\n";
while (true) {
int client_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
if (client_socket < 0) {
perror("accept");
continue;
}
char buffer[3000] = {0};
int valread = read(client_socket, buffer, sizeof(buffer) - 1);
cout << buffer << endl;
string body = "<h1>Your C++ server is live and running</h1>";
string response =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: " + to_string(body.size()) + "\r\n"
"Connection: close\r\n"
"\r\n" +
body;
send(client_socket, response.c_str(), response.size(), 0);
close(client_socket); // closing connection
}
close(server_fd); //closing server
return 0;
}
This is a very basic implementation, but using the same concept and handling the incoming buffer string, we can send different types of responses. In the next part, I will implement route handling for GET, POST, and DELETE requests, save data in a text file as a simple database, and use threads for concurrency and efficiency — all through building a basic To-Do app.
Of course, this network program is still quite vulnerable, but it serves as a simple and practical way to understand how an HTTP server and socket programming work. Feel free to explore it and see if you can spot potential vulnerabilities. If you’d like to dive even deeper: https://beej.us/guide/bgnet/ https://www.geeksforgeeks.org/html/what-is-http/ https://app.codecrafters.io/courses/http-server/overview