cpp-httplib
Header-only HTTP client and server library for C++11. Easy integration with single header file. Compactly provides HTTP/HTTPS support, SSL/TLS, multipart, range requests, basic authentication, and JSON processing capabilities.
GitHub Overview
yhirose/cpp-httplib
A C++ header-only HTTP/HTTPS server and client library
Topics
Star History
Library
cpp-httplib
Overview
cpp-httplib is "a C++11 compliant single-file header-only HTTP/HTTPS library" developed as one of the most easily integrated C++ HTTP libraries. With the concept of "simple and user-friendly," it provides HTTP server and client functionality by simply including a single header file (httplib.h). Offering intuitive programming model with blocking socket I/O, multi-platform support, SSL/TLS encryption support, and comprehensive features necessary for HTTP communication in C++ applications, it serves as an ideal choice for developers who prioritize lightweight design and ease of use.
Details
cpp-httplib 2025 edition has established itself as a mature HTTP library providing the best integration experience in C++11+ environments. With over 15 years of development experience, it boasts refined APIs and high stability, being widely adopted across Windows, Linux, and macOS environments. The single-header design minimizes external dependencies, making project integration extremely simple. With a simple programming model using blocking I/O, both HTTP server and client functionality can be implemented intuitively. It meets enterprise-level HTTP communication requirements including HTTPS support through OpenSSL, multipart form data processing, authentication systems, and proxy support.
Key Features
- Header-only Design: Complete functionality with just the httplib.h file
- Server & Client Integration: Bidirectional HTTP communication in a single library
- C++11 Compliance: Full compliance with modern C++ standards
- Multi-platform Support: Complete support for Windows, Linux, and macOS
- SSL/TLS Encryption: Complete HTTPS support through OpenSSL 3.0+
- Blocking I/O: Easy-to-understand synchronous programming model
Pros and Cons
Pros
- Extremely simple integration process with minimal dependency management
- Simplified distribution and deployment through single-header design
- High learning efficiency and code readability through intuitive API
- Unified library providing both server and client functionality
- Predictable execution flow through blocking I/O model
- Rich HTTP feature support (authentication, proxy, multipart, etc.)
Cons
- Concurrent connection limitations due to blocking I/O (unsuitable for high throughput)
- Resource consumption in high-concurrency processing due to thread pool dependency
- Performance disadvantage compared to asynchronous I/O libraries
- External library requirement due to OpenSSL dependency for HTTPS usage
- Scalability limitations for I/O-intensive applications
- Feature/performance trade-offs for applications requiring maximum performance
Reference Pages
Code Examples
Installation and Basic Setup
// Using cpp-httplib (header-only)
#include "httplib.h"
// Enable OpenSSL support for HTTPS
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
// Compilation example (Linux/macOS)
// g++ -std=c++11 your_app.cpp -pthread
// Link options for HTTPS
// g++ -std=c++11 your_app.cpp -pthread -lssl -lcrypto
// CMake dependency configuration
/*
find_package(httplib REQUIRED)
target_link_libraries(your_target httplib::httplib)
*/
Basic HTTP Client (GET/POST/PUT/DELETE)
#include "httplib.h"
#include <iostream>
int main() {
// Create HTTP client
httplib::Client client("https://api.example.com");
// Basic GET request
auto res = client.Get("/users");
if (res) {
if (res->status == 200) {
std::cout << "Status: " << res->status << std::endl;
std::cout << "Content-Type: " << res->get_header_value("content-type") << std::endl;
std::cout << "Body: " << res->body << std::endl;
}
} else {
auto err = res.error();
std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
}
// GET request with query parameters
httplib::Params params = {
{"page", "1"},
{"limit", "10"},
{"sort", "created_at"}
};
res = client.Get("/users", params, httplib::Headers{});
// POST request (sending JSON)
std::string json_data = R"({
"name": "John Doe",
"email": "[email protected]",
"age": 30
})";
httplib::Headers headers = {
{"Content-Type", "application/json"},
{"Authorization", "Bearer your-token"}
};
res = client.Post("/users", headers, json_data, "application/json");
if (res && res->status == 201) {
std::cout << "User created successfully: " << res->body << std::endl;
} else {
std::cout << "Error: " << (res ? std::to_string(res->status) : "Connection failed") << std::endl;
}
// POST request (sending form data)
httplib::Params form_data = {
{"username", "testuser"},
{"password", "secret123"}
};
res = client.Post("/login", form_data);
// PUT request (data update)
std::string update_json = R"({"name": "Jane Doe", "email": "[email protected]"})";
res = client.Put("/users/123", headers, update_json, "application/json");
// DELETE request
res = client.Delete("/users/123", headers);
if (res && res->status == 204) {
std::cout << "User deleted successfully" << std::endl;
}
// Detailed response information inspection
if (res) {
std::cout << "Status: " << res->status << std::endl;
std::cout << "Reason: " << res->reason << std::endl;
std::cout << "Headers:" << std::endl;
for (const auto& [key, value] : res->headers) {
std::cout << " " << key << ": " << value << std::endl;
}
}
return 0;
}
Advanced Configuration and Customization (Headers, Authentication, Timeout, etc.)
#include "httplib.h"
#include <chrono>
void advanced_client_configuration() {
// Create HTTPS client
httplib::Client client("https://secure-api.example.com");
// Custom header configuration
httplib::Headers custom_headers = {
{"User-Agent", "MyApp/1.0 (cpp-httplib)"},
{"Accept", "application/json"},
{"Accept-Language", "en-US,ja-JP"},
{"X-API-Version", "v2"},
{"X-Request-ID", "req-12345"}
};
// Set default headers
client.set_default_headers(custom_headers);
// Basic authentication setup
client.set_basic_auth("username", "password");
// Bearer Token authentication
client.set_bearer_token_auth("your-jwt-token");
// Digest authentication (requires OpenSSL)
client.set_digest_auth("username", "password");
// Timeout configuration
client.set_connection_timeout(0, 300000); // Connection timeout 300ms
client.set_read_timeout(5, 0); // Read timeout 5 seconds
client.set_write_timeout(5, 0); // Write timeout 5 seconds
// SSL certificate configuration
client.set_ca_cert_path("./ca-bundle.crt"); // CA certificate bundle
client.enable_server_certificate_verification(true); // Enable certificate verification
client.enable_server_hostname_verification(true); // Enable hostname verification
// Disable SSL verification for development (not recommended)
// client.enable_server_certificate_verification(false);
// Proxy configuration
client.set_proxy("proxy.example.com", 8080);
client.set_proxy_basic_auth("proxy_user", "proxy_pass");
// Keep-Alive configuration
client.set_keep_alive(true);
// Redirect following configuration
client.set_follow_location(true);
// Compression configuration
client.set_compress(true); // Request compression
client.set_decompress(true); // Response auto-decompression
// Network interface specification (Linux/macOS)
client.set_interface("eth0");
// Request execution example
auto res = client.Get("/secure-data");
if (res) {
std::cout << "Secure request successful: " << res->status << std::endl;
}
}
// Custom authentication header implementation
class APIKeyAuth {
private:
std::string api_key;
std::string header_name;
public:
APIKeyAuth(const std::string& key, const std::string& header = "X-API-Key")
: api_key(key), header_name(header) {}
httplib::Headers get_headers() const {
return {{header_name, api_key}};
}
};
void custom_authentication_example() {
httplib::Client client("https://api.example.com");
// Using custom authentication
APIKeyAuth auth("your-api-key-here");
auto headers = auth.get_headers();
auto res = client.Get("/protected-data", headers);
if (res) {
std::cout << "Authenticated request: " << res->status << std::endl;
}
}
Error Handling and Retry Functionality
#include "httplib.h"
#include <chrono>
#include <thread>
#include <stdexcept>
// Comprehensive error handling
class HTTPClient {
private:
httplib::Client client;
int max_retries;
std::chrono::milliseconds base_delay;
public:
HTTPClient(const std::string& host, int port = -1, int retries = 3)
: client(host, port), max_retries(retries), base_delay(1000) {
// Basic configuration
client.set_connection_timeout(0, 300000); // 300ms
client.set_read_timeout(10, 0); // 10 seconds
client.set_write_timeout(10, 0); // 10 seconds
}
httplib::Result safe_get(const std::string& path,
const httplib::Headers& headers = {},
const httplib::Params& params = {}) {
for (int attempt = 0; attempt <= max_retries; ++attempt) {
auto res = client.Get(path, params, headers);
if (res) {
// Successful response case
if (res->status >= 200 && res->status < 300) {
return res;
}
// Determine retry-eligible status codes
if (res->status == 429 || res->status == 500 ||
res->status == 502 || res->status == 503 || res->status == 504) {
if (attempt < max_retries) {
auto delay = base_delay * (1 << attempt); // Exponential backoff
std::cout << "Retry " << (attempt + 1) << "/" << max_retries
<< " (Status: " << res->status << ") "
<< "after " << delay.count() << "ms..." << std::endl;
std::this_thread::sleep_for(delay);
continue;
}
}
// Non-retryable error or max attempts reached
return res;
} else {
// Connection error case
auto error = res.error();
std::cout << "Connection error: " << httplib::to_string(error) << std::endl;
if (attempt < max_retries) {
auto delay = base_delay * (1 << attempt);
std::cout << "Connection retry " << (attempt + 1) << "/" << max_retries
<< " after " << delay.count() << "ms..." << std::endl;
std::this_thread::sleep_for(delay);
continue;
}
}
}
// All attempts failed
return httplib::Result{nullptr, httplib::Error::Unknown};
}
void handle_response(const httplib::Result& res) {
if (!res) {
auto error = res.error();
switch (error) {
case httplib::Error::Connection:
std::cerr << "Connection error: Cannot connect to server" << std::endl;
break;
case httplib::Error::BindIPAddress:
std::cerr << "IP address bind error" << std::endl;
break;
case httplib::Error::Read:
std::cerr << "Read error: Failed to receive data" << std::endl;
break;
case httplib::Error::Write:
std::cerr << "Write error: Failed to send data" << std::endl;
break;
case httplib::Error::ExceedRedirectCount:
std::cerr << "Redirect error: Exceeded redirect limit" << std::endl;
break;
case httplib::Error::Canceled:
std::cerr << "Request canceled" << std::endl;
break;
case httplib::Error::SSLConnection:
std::cerr << "SSL connection error: HTTPS connection failed" << std::endl;
break;
case httplib::Error::SSLLoadingCerts:
std::cerr << "SSL certificate loading error" << std::endl;
break;
case httplib::Error::SSLServerVerification:
std::cerr << "SSL certificate verification error" << std::endl;
break;
case httplib::Error::UnsupportedMultipartBoundaryChars:
std::cerr << "Multipart boundary character error" << std::endl;
break;
default:
std::cerr << "Unknown error: " << httplib::to_string(error) << std::endl;
break;
}
return;
}
// Status code-specific handling
switch (res->status) {
case 200:
std::cout << "Success: " << res->body << std::endl;
break;
case 201:
std::cout << "Created successfully: " << res->body << std::endl;
break;
case 204:
std::cout << "Processing completed (no response)" << std::endl;
break;
case 400:
std::cerr << "Bad request: " << res->body << std::endl;
break;
case 401:
std::cerr << "Authentication error: Please check your token" << std::endl;
break;
case 403:
std::cerr << "Permission error: Access denied" << std::endl;
break;
case 404:
std::cerr << "Not found: Resource does not exist" << std::endl;
break;
case 429:
std::cerr << "Rate limit: Please wait before retrying" << std::endl;
break;
case 500:
std::cerr << "Server error: Problem on server side" << std::endl;
break;
default:
if (res->status >= 400) {
std::cerr << "HTTP error " << res->status << ": " << res->body << std::endl;
} else {
std::cout << "Response " << res->status << ": " << res->body << std::endl;
}
break;
}
}
};
void error_handling_example() {
try {
HTTPClient client("https://api.example.com");
// Normal request
auto res = client.safe_get("/users");
client.handle_response(res);
// Potentially failing request
res = client.safe_get("/unstable-endpoint");
client.handle_response(res);
} catch (const std::exception& e) {
std::cerr << "Exception occurred: " << e.what() << std::endl;
}
}
Basic HTTP Server Implementation
#include "httplib.h"
#include <iostream>
#include <fstream>
#include <json/json.h> // When using JsonCpp
class SimpleHTTPServer {
private:
httplib::Server server;
public:
SimpleHTTPServer() {
setup_routes();
setup_middleware();
}
void setup_routes() {
// Basic GET endpoint
server.Get("/hello", [](const httplib::Request& req, httplib::Response& res) {
res.set_content("Hello, World!", "text/plain");
});
// JSON response
server.Get("/api/users", [](const httplib::Request& req, httplib::Response& res) {
std::string json_response = R"({
"users": [
{"id": 1, "name": "John Doe", "email": "[email protected]"},
{"id": 2, "name": "Jane Smith", "email": "[email protected]"}
]
})";
res.set_content(json_response, "application/json");
});
// Route with path parameters
server.Get(R"(/api/users/(\d+))", [](const httplib::Request& req, httplib::Response& res) {
auto user_id = req.matches[1];
std::string response = "User ID: " + std::string(user_id);
res.set_content(response, "text/plain");
});
// Named path parameters
server.Get("/users/:id", [](const httplib::Request& req, httplib::Response& res) {
auto user_id = req.path_params.at("id");
std::string response = "User ID: " + user_id;
res.set_content(response, "text/plain");
});
// POST endpoint (JSON reception)
server.Post("/api/users", [](const httplib::Request& req, httplib::Response& res) {
// Content-Type check
if (req.get_header_value("Content-Type") != "application/json") {
res.status = 400;
res.set_content("Content-Type must be application/json", "text/plain");
return;
}
// Process request body
std::cout << "Received JSON: " << req.body << std::endl;
// Create response
std::string response_json = R"({
"status": "success",
"message": "User created successfully",
"id": 123
})";
res.status = 201;
res.set_content(response_json, "application/json");
});
// Multipart form data processing
server.Post("/upload", [](const httplib::Request& req, httplib::Response& res) {
if (req.is_multipart_form_data()) {
for (const auto& file : req.files) {
std::cout << "File: " << file.first << std::endl;
std::cout << " Filename: " << file.second.filename << std::endl;
std::cout << " Content-Type: " << file.second.content_type << std::endl;
std::cout << " Size: " << file.second.content.size() << " bytes" << std::endl;
// Save file
std::ofstream ofs("./uploads/" + file.second.filename, std::ios::binary);
ofs << file.second.content;
}
res.set_content("Files uploaded successfully", "text/plain");
} else {
res.status = 400;
res.set_content("Expected multipart/form-data", "text/plain");
}
});
// Streaming response
server.Get("/stream", [](const httplib::Request& req, httplib::Response& res) {
res.set_chunked_content_provider(
"text/plain",
[](size_t offset, httplib::DataSink& sink) {
static int count = 0;
if (count < 10) {
std::string chunk = "Chunk " + std::to_string(count++) + "\n";
sink.write(chunk.c_str(), chunk.size());
return true; // Continue
} else {
sink.done();
return false; // Complete
}
}
);
});
// Static file server
server.set_mount_point("/static", "./public");
// Server stop endpoint
server.Get("/stop", [&](const httplib::Request& req, httplib::Response& res) {
res.set_content("Server stopping...", "text/plain");
server.stop();
});
}
void setup_middleware() {
// Request logger
server.set_logger([](const httplib::Request& req, const httplib::Response& res) {
std::cout << "[" << std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count()
<< "] " << req.method << " " << req.path
<< " -> " << res.status << std::endl;
});
// CORS configuration
server.set_pre_routing_handler([](const httplib::Request& req, httplib::Response& res) {
res.set_header("Access-Control-Allow-Origin", "*");
res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization");
if (req.method == "OPTIONS") {
res.status = 200;
return httplib::Server::HandlerResponse::Handled;
}
return httplib::Server::HandlerResponse::Unhandled;
});
// Error handler
server.set_error_handler([](const httplib::Request& req, httplib::Response& res) {
std::string error_html = "<html><body><h1>Error " +
std::to_string(res.status) + "</h1><p>Something went wrong</p></body></html>";
res.set_content(error_html, "text/html");
});
// Exception handler
server.set_exception_handler([](const httplib::Request& req, httplib::Response& res, std::exception_ptr ep) {
try {
std::rethrow_exception(ep);
} catch (const std::exception& e) {
res.status = 500;
std::string error_msg = "Internal Server Error: " + std::string(e.what());
res.set_content(error_msg, "text/plain");
} catch (...) {
res.status = 500;
res.set_content("Unknown Internal Server Error", "text/plain");
}
});
}
void run(const std::string& host = "localhost", int port = 8080) {
std::cout << "Server starting: http://" << host << ":" << port << std::endl;
if (!server.listen(host, port)) {
std::cerr << "Server start failed" << std::endl;
}
}
};
int main() {
SimpleHTTPServer server;
server.run("0.0.0.0", 8080);
return 0;
}
Advanced Features (File Upload, Streaming, Authentication)
#include "httplib.h"
#include <fstream>
#include <filesystem>
#include <jwt-cpp/jwt.h> // When using JWT authentication library
class AdvancedHTTPServer {
private:
httplib::Server server;
std::string jwt_secret = "your-secret-key";
public:
AdvancedHTTPServer() {
setup_advanced_routes();
configure_server();
}
bool verify_jwt_token(const std::string& token) {
try {
auto decoded = jwt::decode(token);
auto verifier = jwt::verify()
.allow_algorithm(jwt::algorithm::hs256{jwt_secret})
.with_issuer("your-app");
verifier.verify(decoded);
return true;
} catch (const std::exception& e) {
std::cerr << "JWT verification failed: " << e.what() << std::endl;
return false;
}
}
void setup_advanced_routes() {
// Endpoint requiring JWT authentication
server.Get("/api/protected", [this](const httplib::Request& req, httplib::Response& res) {
std::string auth_header = req.get_header_value("Authorization");
if (auth_header.empty() || auth_header.substr(0, 7) != "Bearer ") {
res.status = 401;
res.set_content("Missing or invalid Authorization header", "text/plain");
return;
}
std::string token = auth_header.substr(7);
if (!verify_jwt_token(token)) {
res.status = 401;
res.set_content("Invalid JWT token", "text/plain");
return;
}
res.set_content("Protected data: Secret information", "text/plain");
});
// Large file upload
server.Post("/api/upload/large", [](const httplib::Request& req, httplib::Response& res) {
std::string upload_dir = "./uploads";
std::filesystem::create_directories(upload_dir);
if (req.is_multipart_form_data()) {
for (const auto& file : req.files) {
std::string filepath = upload_dir + "/" + file.second.filename;
std::ofstream ofs(filepath, std::ios::binary);
if (ofs) {
ofs.write(file.second.content.data(), file.second.content.size());
std::cout << "File saved: " << filepath
<< " (" << file.second.content.size() << " bytes)" << std::endl;
} else {
res.status = 500;
res.set_content("File save failed", "text/plain");
return;
}
}
std::string response = "Upload completed: " + std::to_string(req.files.size()) + " files";
res.set_content(response, "text/plain");
} else {
res.status = 400;
res.set_content("Expected multipart/form-data", "text/plain");
}
});
// Streaming reception with content receiver
server.Post("/api/upload/stream", [](const httplib::Request& req, httplib::Response& res,
const httplib::ContentReader& content_reader) {
std::string filename = "streamed_file.dat";
std::ofstream ofs(filename, std::ios::binary);
if (!ofs) {
res.status = 500;
res.set_content("Cannot create file", "text/plain");
return;
}
size_t total_bytes = 0;
content_reader([&](const char* data, size_t data_length) {
ofs.write(data, data_length);
total_bytes += data_length;
// Progress display (every 1MB)
if (total_bytes % (1024 * 1024) == 0) {
std::cout << "Received: " << total_bytes / (1024 * 1024) << " MB" << std::endl;
}
return true; // Continue
});
ofs.close();
std::string response = "File received: " + std::to_string(total_bytes) + " bytes";
res.set_content(response, "text/plain");
});
// File download (Range Request support)
server.Get(R"(/api/download/(.+))", [](const httplib::Request& req, httplib::Response& res) {
std::string filename = req.matches[1];
std::string filepath = "./downloads/" + filename;
if (!std::filesystem::exists(filepath)) {
res.status = 404;
res.set_content("File not found", "text/plain");
return;
}
// Get file size
auto file_size = std::filesystem::file_size(filepath);
// Handle Range Request
std::string range_header = req.get_header_value("Range");
if (!range_header.empty()) {
// Parse "bytes=start-end" format
size_t start = 0, end = file_size - 1;
if (range_header.substr(0, 6) == "bytes=") {
std::string range_spec = range_header.substr(6);
auto dash_pos = range_spec.find('-');
if (dash_pos != std::string::npos) {
if (dash_pos > 0) {
start = std::stoull(range_spec.substr(0, dash_pos));
}
if (dash_pos + 1 < range_spec.length()) {
end = std::stoull(range_spec.substr(dash_pos + 1));
}
}
}
// Validate range
if (start >= file_size || end >= file_size || start > end) {
res.status = 416; // Range Not Satisfiable
res.set_header("Content-Range", "bytes */" + std::to_string(file_size));
return;
}
// Send partial content
std::ifstream ifs(filepath, std::ios::binary);
ifs.seekg(start);
size_t content_length = end - start + 1;
std::vector<char> buffer(content_length);
ifs.read(buffer.data(), content_length);
res.status = 206; // Partial Content
res.set_header("Content-Range", "bytes " + std::to_string(start) + "-" +
std::to_string(end) + "/" + std::to_string(file_size));
res.set_header("Accept-Ranges", "bytes");
res.set_content(std::string(buffer.data(), content_length), "application/octet-stream");
} else {
// Send entire file
res.set_file_content(filepath);
res.set_header("Accept-Ranges", "bytes");
}
});
// Real-time data streaming
server.Get("/api/events", [](const httplib::Request& req, httplib::Response& res) {
res.set_header("Content-Type", "text/event-stream");
res.set_header("Cache-Control", "no-cache");
res.set_header("Connection", "keep-alive");
res.set_chunked_content_provider(
"text/event-stream",
[](size_t offset, httplib::DataSink& sink) {
static int event_count = 0;
if (event_count < 10) {
auto now = std::chrono::system_clock::now();
auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(
now.time_since_epoch()).count();
std::string event_data = "data: {\"event\": " + std::to_string(event_count) +
", \"timestamp\": " + std::to_string(timestamp) + "}\n\n";
sink.write(event_data.c_str(), event_data.size());
event_count++;
std::this_thread::sleep_for(std::chrono::seconds(1));
return true;
} else {
sink.done();
return false;
}
}
);
});
}
void configure_server() {
// Server configuration
server.set_read_timeout(60, 0); // 60 seconds
server.set_write_timeout(60, 0); // 60 seconds
server.set_payload_max_length(100 * 1024 * 1024); // 100MB
server.set_keep_alive_max_count(10);
server.set_keep_alive_timeout(30);
// Thread pool configuration
server.new_task_queue = []() {
return new httplib::ThreadPool(12); // 12 threads
};
}
void run_https(const std::string& cert_path, const std::string& key_path,
const std::string& host = "0.0.0.0", int port = 8443) {
httplib::SSLServer ssl_server(cert_path, key_path);
// Copy configuration to HTTPS server
ssl_server = std::move(server);
std::cout << "HTTPS server starting: https://" << host << ":" << port << std::endl;
if (!ssl_server.listen(host, port)) {
std::cerr << "HTTPS server start failed" << std::endl;
}
}
};
int main() {
AdvancedHTTPServer server;
// Run as HTTPS server (requires certificate files)
// server.run_https("./cert.pem", "./key.pem");
return 0;
}