How curl's Header Files Are Organized: Public API vs Internal Implementation

cURL separates its header files into a stable public API layer in include/curl/ and an internal implementation layer in lib/ that are never installed with the library.

The curl/curl repository maintains a strict architectural boundary between application-facing interfaces and internal machinery through its header file organization. Understanding how curl's header files are organized helps developers include the correct headers in their applications while avoiding dependencies on unstable internal structures. The source tree splits these definitions across two main directories that serve fundamentally different purposes.

Public API Headers (include/curl/)

The include/curl/ directory contains the public API headers that ship with libcurl and provide a stable, documented interface for applications. These files are installed to ${prefix}/include/curl/ during the build process and are guarded with #ifdef __cplusplus extern "C" for C++ compatibility.

curl.h: The Umbrella Header

include/curl/curl.h serves as the primary entry point that most applications include. It pulls in version defines from curlver.h, platform abstractions from system.h, and declares the opaque handle types CURL and CURLSH that encapsulate connection state.

easy.h: The Easy Interface

include/curl/easy.h declares the synchronous easy interface functions including curl_easy_init(), curl_easy_setopt(), curl_easy_perform(), and curl_easy_cleanup(). This header provides the simplest way to perform a single blocking transfer.

multi.h: Concurrent Transfers

include/curl/multi.h exposes the multi interface for handling multiple simultaneous transfers through an event-driven API. It declares curl_multi_init(), curl_multi_add_handle(), and curl_multi_perform() for non-blocking I/O operations.

options.h: Configuration Constants

include/curl/options.h enumerates all CURLoption values accepted by curl_easy_setopt(). This file is auto-generated from the master option list, ensuring the public option set remains consistent and versioned.

Specialized Public Headers

Additional public headers provide specific functionality:

  • urlapi.h: URL encoding, decoding, and manipulation functions (curl_url(), curl_url_set())
  • header.h: HTTP header parsing and construction utilities
  • mprintf.h: printf-style formatting functions used internally by libcurl
  • curlver.h: Version macros (LIBCURL_VERSION_MAJOR) and curl_version_info()
  • websockets.h: WebSockets-specific declarations
  • stdcheaders.h and typecheck-gcc.h: Cross-platform compatibility and compile-time type checking

Private Implementation Headers (lib/)

The lib/ directory contains private headers used only by libcurl's own source files. These expose internal structures, protocol-specific code, and helper macros that are not part of the public API and may change without notice between releases.

Platform and Configuration

lib/curl_setup.h and companion config-*.h files handle OS-specific includes, feature detection macros, and build-time configuration. These headers manage the complexity of targeting diverse platforms from Windows to embedded Linux.

Networking Core

Internal networking abstractions reside in headers like:

  • curl_addrinfo.h: DNS resolution wrappers
  • connect.h: Connection establishment and socket handling
  • sockaddr.h: Address structure utilities
  • curl_ssl.h: TLS backend abstractions

Protocol Implementations

Each supported protocol maintains dedicated headers:

  • http.h, http2.h, http1.h: HTTP family implementations
  • ftp.h: File Transfer Protocol
  • smtp.h, imap.h, pop3.h: Email protocols
  • ws.h: WebSockets implementation details

Utilities and State Management

Supporting infrastructure includes cryptographic helpers (curl_md5.h, curl_sha256.h), buffered I/O (bufref.h, bufq.h), and the multi-handle internals (multiif.h, multihandle.h).

Practical Usage Examples

To use curl's public API, include only the headers from include/curl/:

/* simple_get.c */
#include <curl/curl.h>
#include <stdio.h>

int main(void)
{
    CURL *h = curl_easy_init();
    if(h) {
        curl_easy_setopt(h, CURLOPT_URL, "https://example.com");
        curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
        
        CURLcode res = curl_easy_perform(h);
        if(res != CURLE_OK)
            fprintf(stderr, "Error: %s\n", curl_easy_strerror(res));
            
        curl_easy_cleanup(h);
    }
    return 0;
}

For concurrent transfers using the multi-interface:

/* multi_download.c */
#include <curl/curl.h>
#include <stdio.h>

int main(void)
{
    CURLM *multi = curl_multi_init();
    CURL *h1 = curl_easy_init();
    CURL *h2 = curl_easy_init();
    
    curl_easy_setopt(h1, CURLOPT_URL, "https://example.org");
    curl_easy_setopt(h2, CURLOPT_URL, "https://example.net");
    
    curl_multi_add_handle(multi, h1);
    curl_multi_add_handle(multi, h2);
    
    int still_running;
    do {
        curl_multi_perform(multi, &still_running);
        /* Production code should use select()/epoll to wait on sockets */
    } while(still_running);
    
    curl_multi_cleanup(multi);
    curl_easy_cleanup(h1);
    curl_easy_cleanup(h2);
    return 0;
}

Compile these examples using:

gcc simple_get.c -lcurl -o simple_get

Summary

  • Public headers live in include/curl/ and provide the stable API for applications, including curl.h, easy.h, multi.h, and options.h.
  • Private headers reside in lib/ and contain implementation details subject to change, such as curl_setup.h, http.h, and multiif.h.
  • Only headers from include/curl/ are installed with the library and should be included by external code.
  • The public headers use extern "C" guards to ensure C++ compatibility.
  • Internal headers often prefix symbols with curl_ to avoid clashes with system headers.

Frequently Asked Questions

What is the difference between curl.h and easy.h?

curl.h is the umbrella header that includes version info, system abstractions, and core type definitions like the CURL handle. easy.h specifically declares the easy interface functions for synchronous transfers. While curl.h includes many supporting definitions, easy.h focuses strictly on the curl_easy_* function family.

Can I include headers from the lib/ directory in my application?

No. Headers in the lib/ directory are internal implementation details that are not installed with libcurl and change frequently between versions. Including them violates the API boundary and will break your application on future curl updates. Always use the public headers in include/curl/.

Which header provides the curl_multi_* functions?

The multi-interface functions are declared in include/curl/multi.h. This header defines CURLM handles and functions like curl_multi_init(), curl_multi_add_handle(), and curl_multi_perform() for managing concurrent transfers.

How does curl maintain backward compatibility with public headers?

The include/curl/ headers follow strict semantic versioning. Symbols are marked deprecated before removal, and the CURLoption enum in options.h is auto-generated to ensure consistency. Internal changes in lib/ never affect the public struct layouts or function signatures exposed in include/curl/.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →