httpx.zig is a modern, high-performance HTTP library for Zig, providing everything needed to build fast and reliable networked applications, including HTTP clients, servers, APIs, web services, reverse proxies, and full-featured websites.
Tip
If you build with httpx.zig, make sure to give it a star.
Note
Project maturity: This project is under active development. It provides a comprehensive HTTP client and server implementation with modern protocol, networking, security, and performance features, and contributions are welcome.
Custom HTTP/2, HTTP/3, TLS, Streaming, and Parsing implementation: Zig's standard library does not provide HTTP/2, HTTP/3, QUIC, ALPN negotiation, OpenAPI documentation UI, full HTML/XML DOM parsing, or built-in progress download engines. httpx.zig implements these subsystems natively in Zig, including:
- TLS 1.3 server engine + TLS 1.2/1.3 client (RFC 5246 / RFC 8446) — a custom server-side TLS 1.3 engine (handshake, X25519 key exchange, ChaCha20-Poly1305 / AES-128-GCM / AES-256-GCM record encryption, ALPN negotiation per RFC 7301 with HTTP/1.1 fallback, X.509 parsing/verification, P-256 ECDSA certificates); the HTTPS client transport builds on
std.crypto.tls(TLS 1.2/1.3) with httpx verification policy (SNI, SAN/hostname checks, system + custom trust stores) - HPACK header compression (RFC 7541) with
Without Indexing/Never Indexedsecurity for HTTP/2 - HTTP/2 stream multiplexing, flow control (WINDOW_UPDATE), SETTINGS enforcement, GOAWAY/RST_STREAM, PRIORITY, CONTINUATION frames, PING, and HTTP/1.x connection pooling (RFC 7540; HTTP/2 connections are per-request for now)
- QPACK header compression (RFC 9204) with static/dynamic tables and decoder/encoder stream instructions for HTTP/3
- QUIC transport frame encoding/decoding (RFC 9000) with RESET_STREAM/STOP_SENDING cancellation, version negotiation, and transport parameters
- HTTP/3 frame types, SETTINGS, GOAWAY, and CONNECTION_CLOSE handling
- Streaming Downloader & File Manager with zero-config
loaders.zigprogress bars, dynamic speed & ETA estimation, range-based resumption, atomic updates, and cryptographic verification (SHA-256, SHA-384, SHA-512, MD5, SHA-1) - Unified DOM Engine & Web Resource Inspector for HTML5, XML, RSS/Atom/JSON feeds, robots.txt, sitemaps, and CSS selector queries with zero manual per-element memory freeing
- Self-Hosted Interactive Documentation supporting Swagger UI, ReDoc, Scalar, and GraphiQL with embedded offline assets
Related Zig projects:
- For Env.zig (.env parsing), check out env.zig.
- For TUI support, check out tui.zig.
- For ZON file format support, check out zon.zig.
- For Spinners/loading/progress bar support, check out loaders.zig.
- For Terminal colors & text styles support, check out tint.zig.
- For MCP support, check out mcp.zig.
- For API framework support, check out api.zig.
- For Web framework support, check out zix.
- For archive/compression support, check out archive.zig.
- For compression file format support, check out zigx.
- For CUDA support, check out cuda.zig.
- For Simplified build.zig config support, check out buildx.zig.
- For SQLite (zig-native implementation) support, check out sqlite.zig.
- For File downloading support, check out downloader.zig.
- For update checker/auto-updater support, check out updater.zig.
- For Numerical computing support, check out num.zig.
- For Logging support, check out logly.zig.
- For Data validation and serialization support, check out zigantic.
- For UUID support, check out uuid.zig.
- For Key-Value database support, check out zkv.zig.
- For Terminal color & text styles support, check out hint.zig.
- For Brotli compression support, check out brotli.zig.
- For Zstd compression support, check out zstd.zig.
- For Tree-Sitter support, check out tree-sitter.zig
Features (click to expand)
| Feature | Description |
|---|---|
| Protocol Support | Full client + server runtime across HTTP/1.0, HTTP/1.1, HTTP/2, and HTTP/3: HTTP/2 over TCP/TLS with ALPN h2, stream multiplexing, flow control, SETTINGS, and trailers; HTTP/3 over QUIC/UDP with native TLS 1.3, ALPN h3, QPACK encoder/decoder streams, dynamic table management, request/response streaming, and TLS 1.3 0-RTT early-data resumption. |
| Header Compression | HPACK (RFC 7541) for HTTP/2; QPACK (RFC 9204) for HTTP/3 with static and dynamic table management. |
| HTTP/2 & HTTP/3 ALPN | Server-side ALPN negotiation during the TLS handshake with graceful HTTP/1.1 fallback; the native client offers ALPN too, so explicit .http2 over TLS negotiates h2 end to end, and .http3 over QUIC negotiates h3 end to end. |
| Stream Multiplexing | HTTP/2 and HTTP/3 stream state machines with flow control (WINDOW_UPDATE / MAX_STREAM_DATA), SETTINGS enforcement, GOAWAY/RST_STREAM, and trailers. |
| Connection Pooling | Automatic reuse of TCP keep-alive connections, multiplexed HTTP/2 and HTTP/3 connections, and TLS session resumption caching. |
| Unified DOM & Web Parsing | Native parser for HTML5, XML, RSS/Atom/JSON feeds, robots.txt, and sitemaps with zero-leak arena architecture. |
| Streaming Downloader | Resumable chunked file downloader powered by loaders.zig progress bars, ETA calculation, and hash verification. |
| Pattern-based Routing | Intuitive server routing with typed parameters (/users/{id:int}), slugs, catch-alls, groups, mounting, named routes + reversing, 404/405 handling, and OpenAPI integration. |
| Middleware Stack | Built-in middleware for CORS, security headers (Helmet), recovery, logging, rate limiting, and CSRF, plus health endpoints. |
| TLS/SSL | Native TLS 1.3 server engine + native TLS 1.3 client (RFC 8446) plus TLS 1.2/1.3 via the std-based HTTPS/1.1 transport: server ALPN (RFC 7301) with HTTP/1.1 fallback, native client ALPN (h2 for HTTP/2 over TLS, h3 for HTTP/3 over QUIC), X25519 ECDHE, AEAD ciphers, X.509 parsing/verification (P-256 ECDSA server certs), system/custom trust stores, hostname checks, mutual TLS enforcement (required/optional client certificates, presented via high-level .tls = .{ .clientCertPem, .clientKeyPem }), TLS 1.3 PSK session resumption (psk_dhe_ke NST tickets) + HelloRetryRequest, and complete TLS 1.3 0-RTT early data with bounded server-side anti-replay cache (ReplayCache) and safe HTTP method policy. |
| Static Files & SPA | High-performance static file serving with ETag, cache control, conditional GET, MIME detection, and SPA HTML5 fallback. |
| Interactive API Docs | Auto-generated OpenAPI 3.1 specifications with embedded Swagger UI, ReDoc, Scalar, and GraphiQL interfaces. |
| Streaming & Realtime | Chunked transfer responses with optional trailers, Server-Sent Events (SSE), and WebSocket frame support. |
| Conditional Requests | ETag and Last-Modified static file serving with If-None-Match revalidation. |
| DNS Resolution | Resolution with caching and concurrent resolver coalescing. |
| Cookie APIs | First-class request/response cookie jar and header helpers for both client and server contexts. |
| Security & Hardening | Security headers (Helmet), CSRF token helpers, and CRLF injection defenses. |
| Multipart Form Data | RFC 2046 streaming multipart body builder and parser for text fields and large file uploads. |
| FTP & FTPS | FTP client and server with PASV/EPSV, directory listing, streaming uploads/downloads, and resumption (explicit FTPS returns a typed error until TLS wiring lands). |
| Concurrency & Workers | Thread-safe bounded WorkerPool and parallel client requests (getAll, requestAll). |
| Proxy Support | Client-side HTTP forward proxy (CONNECT with Proxy-Authorization auth, 407 → error.ProxyAuthRequired) and SOCKS4/4a plus SOCKS5/SOCKS5h tunneling with remote-DNS delegation. |
| Structured Logging | Zero-allocation level-filtered structured logger supporting custom sinks and terminal formatting. |
| Cross-Platform Sockets | Robust non-blocking Windows socket handling with WSAEWOULDBLOCK retry, plus MSG_NOSIGNAL on POSIX. |
| Observability & Metrics | Prometheus text exposition (/metrics), live request/duration histograms, status counters, and zero-alloc snapshots. |
| File Watcher & Live Reload | Event-driven directory watching (next() ?WatchEvent, changeCount()) with bounded event queues, cross-platform notifications, and hot/warm reload. |
Prerequisites and Supported Platforms (click to expand)
| Requirement | Version | Notes |
|---|---|---|
| Zig | 0.17.0 (required) | Download from ziglang.org |
| Operating System | Windows 10+, Linux, macOS | Cross-platform networking support |
[!IMPORTANT] Zig 0.17.0 is required. This project targets the stable Zig 0.17.0 release. Please use Zig 0.17.0 for all builds. Zig 0.16.0 is not supported by this release.
| Platform | x86_64 (64-bit) | aarch64 (ARM64) | x86 (32-bit) |
|---|---|---|---|
| Linux | Yes | Yes | Yes |
| Windows | Yes | Yes | Yes |
| macOS | Yes | Yes (Apple Silicon) | No |
# Build for Linux ARM64 from Windows
zig build -Dtarget=aarch64-linux
# Build for Windows from Linux
zig build -Dtarget=x86_64-windows
# Build for macOS Apple Silicon from Linux
zig build -Dtarget=aarch64-macos
# Build for 32-bit Windows
zig build -Dtarget=x86-windowsLatest Release (v0.2.2)
zig fetch --save https://github.com/muhammad-fiaz/httpx.zig/archive/refs/tags/0.2.2.tar.gzPrevious Release (v0.2.1)
zig fetch --save https://github.com/muhammad-fiaz/httpx.zig/archive/refs/tags/0.2.1.tar.gzWarning
Zig 0.17 is required by v0.2.2. Zig 0.16 is deprecated and supported only by v0.2.1, and Zig 0.15 is deprecated and supported only by v0.0.7. New projects should use Zig 0.17.0+ with httpx.zig v0.2.2.
Use this for the latest development build from the main branch:
zig fetch --save git+https://github.com/muhammad-fiaz/httpx.zig.git.dependencies = .{
.httpx = .{
.url = "https://github.com/muhammad-fiaz/httpx.zig/archive/refs/tags/0.2.2.tar.gz",
.hash = "...", // Run `zig fetch --save <url>` to generate the hash.
},
},git clone https://github.com/muhammad-fiaz/httpx.zig.git
cd httpx.zig
zig buildTo use a local checkout from another project:
.dependencies = .{
.httpx = .{
.path = "../httpx.zig",
},
},const httpx_dep = b.dependency("httpx", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("httpx", httpx_dep.module("httpx"));Note
If you encounter file exists in modules 'X' and 'X0', try removing .target = target and .optimize = optimize from your dependency options in the root build.zig and use .{} instead. This can help prevent duplicate module instances when multiple dependencies resolve the same module. See Zig issue #36694.
const std = @import("std");
const httpx = @import("httpx");
pub fn main() !void {
// 1. Primary unified fetch API (supports GET, POST, headers, typed JSON)
var resp = try httpx.fetch("https://httpbun.com/get", .{});
defer resp.deinit();
std.debug.print("GET Status: {d}, Body: {s}\n", .{ resp.status, resp.bytes() });
// 2. POST with strongly typed Zig struct (serialized via std.json)
const CreateUser = struct { name: []const u8, email: []const u8 };
var post = try httpx.fetch("https://httpbun.com/post", .{
.method = .POST,
.json = CreateUser{ .name = "Alice", .email = "alice@example.com" },
});
defer post.deinit();
// 3. Strongly typed response decoding (httpbun echoes our JSON under "json")
const Echo = struct { json: ?CreateUser = null };
const echo = try post.json(Echo);
if (echo.json) |user| std.debug.print("User: {s} <{s}>\n", .{ user.name, user.email });
// 4. Convenience verb shortcuts
var del = try httpx.delete("https://httpbun.com/delete", .{});
defer del.deinit();
}HTTPX uses one consistent options-struct rule: fundamental inputs
(URL, host, source data) and long-lived resources (allocator, io)
stay positional; all configuration, behavior, limits, and settings go
inside the final .{} argument:
client.get(url, .{ .timeoutMs = 10_000 });
client.resolve("httpbun.com", .{ .port = 443 });const std = @import("std");
const httpx = @import("httpx");
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const io = std.Io.Threaded.global_single_threaded.io();
// Create client with full config (all options use camelCase)
var client = httpx.Client.init(allocator, io, .{
.timeoutMs = 10_000,
.followRedirects = true,
.maxRedirects = 5,
.maxRetries = 3,
.retryDelayMs = 500,
.retryStatusCodes = &.{ 502, 503, 504 },
.dnsCache = .{ .enable = true, .ttlMs = 60_000 },
});
defer client.deinit();
// Unified fetch request (GET)
var response = try client.fetch("https://httpbun.com/get", .{});
defer response.deinit();
// Unified fetch request (POST with JSON)
var post = try client.fetch("https://httpbun.com/post", .{
.method = .POST,
.json = .{ .name = "John", .role = "engineer" },
});
defer post.deinit();
// HTTPS with TLS options
var tls_resp = try client.fetch("https://httpbun.com/get", .{
.tls = .{ .verify = .none }, // dev only
});
defer tls_resp.deinit();
// Graceful close (purge connection pool)
client.close();
// Full reset (close + clear DNS cache)
client.reset();
}// Parallel requests - getAll (arrays and slices accepted directly).
// Each Response must be deinited; the slice itself is freed with the
// caller's allocator (page_allocator for these global helpers).
const urls = [_][]const u8{
"https://httpbun.com/get",
"https://httpbun.com/headers",
};
var results = try httpx.getAll(urls);
defer {
for (results) |*r| r.deinit();
std.heap.page_allocator.free(results);
}
// Parallel requests - requestAll
const reqs = [_]httpx.RequestOptions{
.{ .method = .GET, .url = "https://httpbun.com/get" },
.{ .method = .GET, .url = "https://httpbun.com/headers" },
};
var batch = try httpx.requestAll(reqs);
defer {
for (batch) |*r| r.deinit();
std.heap.page_allocator.free(batch);
}httpx.zig includes a streaming download, resume, and file verification subsystem powered by loaders.zig for terminal progress bars:
const std = @import("std");
const httpx = @import("httpx");
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const io = std.Io.Threaded.global_single_threaded.io();
var client = httpx.Client.init(allocator, io, .{});
defer client.deinit();
const sampleUrl = "https://ontheline.trincoll.edu/images/bookdown/sample-local-pdf.pdf";
// 1. Zero-config download with automatic filename & loaders.zig progress bar
const res = try client.download(sampleUrl, .{
.path = "downloads/",
.progress = .auto,
.existing = .overwrite,
.createDirs = true,
});
std.debug.print("Downloaded: {s} ({d} bytes)\n", .{ res.destinationPath(), res.downloadedBytes });
// 2. Download with in-flight cryptographic SHA-256 verification
const verifiedRes = try client.download(sampleUrl, .{
.path = "downloads/sample.pdf",
.verify = .{
.sha256 = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
.minSize = 100,
.maxSize = 50 * 1024 * 1024,
},
.atomic = true, // downloads to temp file first, renames on valid hash
});
// 3. Inspect remote file metadata without downloading (size, filename, ranges)
const fileInfo = try client.lookupFileInfo(sampleUrl, .{});
var sizeStrBuf: [32]u8 = undefined;
std.debug.print("Remote file: {s}, size: {s}\n", .{ fileInfo.fileName(), fileInfo.formatSize(&sizeStrBuf) });
// 4. Resume partial download via HTTP Range: bytes=X- (clean non-reserved keyword name)
const resumedRes = try client.download(sampleUrl, .{
.path = "downloads/sample.pdf",
.existing = .resumePartial,
.maxRetries = 3,
});
// 5. Safe file updater with rollback backup
const updateRes = try client.updateFile(sampleUrl, .{
.path = "bin/app.bin",
.backupExisting = true,
.backupSuffix = ".bak",
});
// 6. Native FTP Download with progress
const ftpRes = try httpx.ftp.download(allocator, .{
.host = "ftp.example.com",
.remotePath = "/pub/archive.tar.gz",
.destinationPath = "downloads/",
.progress = .auto,
});
}httpx.zig includes a comprehensive document parsing, DOM manipulation, and CSS selector inspection engine.
Note
HTTPX parses HTML, XML, and templates with Tree-sitter grammars defined in their owning modules (parsing/html.zig, parsing/xml.zig, web/templates/parser.zig); DOM/AST semantics are built directly from those syntax trees. Applications interact exclusively with HTTPX's public APIs (httpx.Parser, response.html(), doc.select()). Tree-sitter is strictly an internal implementation detail and never needs to be imported by application code.
const std = @import("std");
const httpx = @import("httpx");
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// 1. Initialize unified parser with reusable allocator & configuration
var p = httpx.Parser.init(allocator, .{});
// 2. Parse HTML directly
var doc = try p.parseHtml("<html><head><title>My Page</title></head><body><h1 class='title'>Hello</h1><a href='/link'>Click</a></body></html>");
defer doc.deinit();
// Fluent zero-allocator navigation
const title = try doc.title();
const links = try doc.links();
var h1_nodes = try doc.select("h1.title");
defer h1_nodes.deinit();
// 3. Parse RSS / Atom / JSON Feed
var feed = try p.parseFeed(xml_feed_str, null);
defer feed.deinit();
// 4. Parse robots.txt
var robots = try p.parseRobots("User-agent: *\nDisallow: /admin/\n");
defer robots.deinit();
const allowed = robots.isAllowed("MyBot", "/public");
// 5. Parse Sitemap XML
var sitemap = try p.parseSitemap(sitemap_xml_str);
defer sitemap.deinit();
}const std = @import("std");
const httpx = @import("httpx");
fn hello(ctx: *httpx.Context) anyerror!httpx.Response {
return ctx.renderJson(.{ .message = "Hello!" });
}
fn page(ctx: *httpx.Context) anyerror!httpx.Response {
return ctx.html("<h1>Welcome</h1>");
}
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const io = std.Io.Threaded.global_single_threaded.io();
var server = try httpx.Server.init(allocator, io, .{
.host = "127.0.0.1",
.port = 8080,
.portStrategy = .incremental, // auto-increments port (8081, 8082, ...) if 8080 is busy
});
defer server.deinit();
try server.get("/hello", hello);
try server.get("/page", page);
server.run();
}httpx.zig provides a high-level App facade and a type-driven Rest subsystem. REST routes automatically bind request payloads, execute typed handlers, serialize response structs to JSON, and derive an OpenAPI 3.1 specification via compile-time reflection.
const std = @import("std");
const httpx = @import("httpx");
const CreateItemRequest = struct {
name: []const u8,
price: u64,
};
const ItemResponse = struct {
id: u64,
name: []const u8,
price: u64,
};
fn createItem(ctx: *httpx.Context, req: CreateItemRequest) !httpx.ApiResult(ItemResponse) {
_ = ctx;
return .created(.{
.id = 101,
.name = req.name,
.price = req.price,
});
}
fn getItem(ctx: *httpx.Context, req: struct { id: u64 }) !httpx.ApiResult(ItemResponse) {
_ = ctx;
return .ok(.{
.id = req.id,
.name = "Widget",
.price = 499,
});
}
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const io = std.Io.Threaded.global_single_threaded.io();
var app = try httpx.App.init(allocator, io, .{
.host = "127.0.0.1",
.port = 8080,
});
defer app.deinit();
// Register typed REST endpoints (drives router + OpenAPI 3.1 schema)
try app.restPost("/api/items", createItem);
try app.restGet("/api/items/{id}", getItem);
// Mount interactive documentation UIs with local embedded vendor assets
try app.docs(.{
.openapi = .{ .enabled = true, .path = "/openapi.json", .title = "Items API", .version = "1.0.0" },
.swaggerUi = .{ .enabled = true, .path = "/docs" },
.redoc = .{ .enabled = true, .path = "/redoc" },
.scalar = .{ .enabled = true, .path = "/scalar" },
});
try app.run();
}const std = @import("std");
const httpx = @import("httpx");
fn handler(_: httpx.tls.Request) anyerror!httpx.tls.Response {
return .{ .body = "Hello over TLS!" };
}
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const io = std.Io.Threaded.global_single_threaded.io();
var tls_listener = try httpx.tls.Listener.init(allocator, io, .{
.port = 8443,
.defaultIdentity = .{
.certChainPem = @embedFile("cert.pem"),
.privateKeyPem = @embedFile("key.pem"),
},
});
defer tls_listener.deinit();
// Blocking accept loop — use requestShutdown() to break out
try tls_listener.run(handler);
}// Blocking — runs until requestShutdown() or stop() is called
server.run();
// Non-blocking — spawns a thread, returns handle for join()
const thread = try server.start();
// Pause accepting new connections (existing connections continue)
server.pause();
// Resume accepting new connections
server.resumeAccepting();
// Graceful shutdown — finishes in-flight requests, then stops
server.requestShutdown();
// Immediate shutdown — closes listener and all connections now
server.stop();HTTPX servers feature dynamic Prometheus v0.0.4 text exposition and thread-safe snapshots:
// Mount dynamic Prometheus endpoint
try server.metrics("/metrics");
// Query point-in-time server snapshot (zero-allocation)
const snap = server.snapshot();
std.debug.print("Uptime: {d}ms, Requests: {d}, Errors: {d}, Error Rate: {d:.2}%\n", .{
snap.uptimeMs,
snap.requestsTotal,
snap.errorsTotal,
snap.errorRate() * 100.0,
});
// Query point-in-time metrics snapshot
const m_snap = server.metricsSnapshot();
std.debug.print("Avg Latency: {d:.3}ms\n", .{m_snap.averageLatencyMs()});Visiting /metrics provides standard Prometheus metrics:
http_requests_total,http_responses_total,http_errors_total,http_timeouts_totalhttp_bytesIn_total,http_bytesOut_totalhttp_active_connections,http_active_requestshttp_requests_by_method_total{method="..."},http_responses_by_status_total{status="..."}http_request_duration_seconds_bucket{le="..."},_sum,_count
Native OS events (ReadDirectoryChangesW / inotify / kqueue) with recursive watching and rename pairing. The walk prunes generated trees (node_modules, .git, .zig-cache, zig-out, zig-pkg, .cache, dist) at any depth, so watching a repository root does not drown in dependency files.
Each changed file is classified into a reload strategy, and the browser client acts on it over SSE:
| Change | Strategy | Browser behaviour |
|---|---|---|
.css |
hotReload |
Stylesheet <link>s are cache-busted and refetched in place; no document reload, no lost script state |
.html, .htm, templates, assets |
warmReload |
Document reload |
.json, .env, .toml, .yaml, .conf |
coldReload |
Document reload |
.zig |
restart |
Document reload |
Setting liveReload = true on the server wires it up for you: the SSE endpoint is mounted at liveReloadPath, and the client script is injected into every HTML response — handler-written pages, static files, and the site generator alike.
var server = try httpx.Server.init(allocator, io, .{
.host = "127.0.0.1",
.port = 8080,
.liveReload = true, // SSE endpoint + client injection
.watch = true, // watch watchDir
.watchDir = "./public",
});onChange is invoked on the watcher thread with no internal lock held, so a callback may safely call next(), changeCount(), or stop():
var watcher = try httpx.static.Watcher.init(allocator, io, .{
.dirPath = "./public",
.pollIntervalMs = 50,
.onChange = onFileChanged,
});
defer watcher.deinit();
try watcher.start();If you build the page yourself, the same script is available directly:
const html = try httpx.static.reload.inject(allocator, body, "/__httpx_liveReload");// Blocking accept loop
try tls_listener.run(handler);
// Graceful shutdown (via the wrapped server)
tls_listener.server.requestShutdown();
// Immediate shutdown
tls_listener.stop();// Graceful close — purges the connection pool
client.close();
// Full reset — close + clear DNS cache
client.reset();Configure automatic retries for failed or retryable requests:
var client = httpx.Client.init(allocator, io, .{
.maxRetries = 3, // retry up to 3 times (4 total attempts)
.retryDelayMs = 500, // base delay between retries
.retryStatusCodes = &.{ 502, 503, 504 }, // status codes that trigger retry
});The delay between retries increases linearly: retryDelayMs * (attempt + 1).
var resolver = httpx.resolve.Resolver.init(allocator, io);
const addrs = try resolver.lookup("example.com", .{ .port = 443 });
defer allocator.free(addrs);fn handler(ctx: *httpx.Context) anyerror!httpx.Response {
// 1. Query parameters & Cookies
const page = ctx.queryParam("page") orelse "1";
const token = ctx.cookie("session");
// 2. Remote address
const addr = ctx.remoteAddress() orelse "unknown";
// 3. Rich Responses
if (std.mem.eql(u8, page, "html")) return ctx.html("<h1>Welcome</h1>");
if (std.mem.eql(u8, page, "text")) return ctx.text("Plain text response");
if (std.mem.eql(u8, page, "xml")) return ctx.xml("<data>sample</data>");
if (std.mem.eql(u8, page, "rss")) return ctx.rss("<rss version=\"2.0\"><channel></channel></rss>");
if (std.mem.eql(u8, page, "atom")) return ctx.atom("<feed xmlns=\"http://www.w3.org/2005/Atom\"></feed>");
if (std.mem.eql(u8, page, "robots")) return ctx.robots("User-agent: *\nAllow: /");
if (std.mem.eql(u8, page, "sitemap")) return ctx.sitemap("<urlset></urlset>");
if (std.mem.eql(u8, page, "binary")) return ctx.binary(&[_]u8{ 0x01, 0x02, 0x03 }, "application/octet-stream");
return ctx.renderJson(.{ .page = page, .addr = addr, .token = token });
}The examples/ directory contains runnable examples demonstrating all features of httpx.zig:
Client:
simple_get- Basic GET requestspost_json- POST with JSON bodycustom_headers- Custom header managementconnection_pool- Connection pooling and statsredirect- Redirect handlinghttp10_client- HTTP/1.0 clienttls_get- HTTPS client with TLShttps_client- HTTPS client with TLStls12_client- TLS 1.2 with self-signed certtls13_client- TLS 1.3 with self-signed certtls_mtls- Mutual TLS (mTLS)resolve- DNS resolutionconcurrent_demo- Parallel request patternsproxy_demo- HTTP forward proxydns_demo- DNS resolution and IP checksdns_cache- DNS cachingcompression_demo- gzip/deflate/brotli compressionretry_demo- Retry with exponential backoffhttp11_client- HTTP/1.1 clientconnectivity- Online checks and connectivity probesbrowser_demo_server- Browser demo serverfull_integration- Full client/server integration
Server:
simple_server- Minimal HTTP serverrest_api- Typed REST endpoints with automatic OpenAPI 3.1 & Docscustom_responses- Rich response generation (HTML, JSON, XML, RSS, Atom, robots.txt, sitemap.xml, binary)static_files- Static file serving with ETaghealth_check- Liveness/readiness probesstreaming- Chunked transfer and SSEauth_and_errors- Authentication and error handlinglive_static_watcher- Live file watcher and auto-reloaddocs_server- Swagger UI, ReDoc, Scalar, GraphiQLgraphql_server- GraphQL serverspa_fallback- SPA with HTML/JS/CSS and client-side routingwebsocket_server- WebSocket serversse_server- Server-Sent Eventssession_server- TTL-based session managementmetrics_server- Prometheus metricsinterceptor_example- Request/response interceptorscookie_server- Cookie managementcors_server- CORS configurationhelmet_server- Security headers (Helmet)rate_limit_server- Rate limitingbody_parser_server- Request body parsingcustom_server- Request ID and body parsingtls_server- HTTPS/TLS server with self-signed certftp_server- FTP-like serverhttp11_server- HTTP/1.1 serverrouting_demo- Typed routing, groups, mounts, reversingstatic_embedded- Embedded-asset serving
Download & File Inspection:
download- Download with built-in progress bar and destination inferencedownload_batch- Concurrent worker pool batch downloadsdownload_resume- Range-based resumptiondownload_verify- Cryptographic verification (SHA-256, SHA-384, SHA-512, MD5, SHA-1)download_checksum_file- Remote checksum file lookup and verificationdownload_existing- Existing file policies (fail, overwrite, skip, resume, replace_if_changed)download_update- Atomic self-updates with rollback safetydownload_info- Metadata HEAD inspection without full body downloaddownload_custom_progress- Custom progress tracking and observersftp_download- Direct FTP file download
Parsing & Inspection (Internal Tree-sitter & DOM Engine):
html_client- Client fetch and automaticresponse.html()parsinghtml_select- CSS selector engine queries (tag,.class,#id,[attr], combinators)html_extract- High-level extraction helpers (title, text, links, forms, images)html_stream- Streaming reader input parsinghtml_file- HTML file parsing and node inspectionhtml_transform- Structural mutation, attribute updating, and XSS-safe serializationparse_html- HTML DOM, CSS Selectors, RSS feeds, robots.txt, and sitemaps
File Watching, Static Assets & Live Reload:
file_watcher- OS-native file monitoring (Windows ReadDirectoryChangesW, Linux inotify, macOS kqueue) with rename pairing and browser live reloadlive_reload- Live reload dev server with CSS hot reload vs HTML page reloadstatic_site- Static site directory mounting with ETag caching and conditional GETspa_server- Single Page Application server with client-side route fallbackdevelopment_server- Unified dev server combining watcher, live reload, and incremental parsing
Protocol:
http2_client- HTTP/2 clienthttp2_multiplex- HTTP/2 stream multiplexinghttp2_tls- HTTP/2 over TLS with ALPN + chain verificationhttp3_client- HTTP/3 clienthttp3_quic- HTTP/3 over QUIC
Templates & Website:
web/templates/basic- Basic template renderingweb/templates/inheritance- Template inheritanceweb/templates/loops- Template loopsweb/templates/includes- Template includesweb/templates/live_reload- Live-reload templatesweb/templates/jinja- Flask-style templates (extends, blocks, includes, macros, filters)website- Full website with embedded assets
Advanced:
multipart- Multipart form dataopenapi- OpenAPI spec generationftp_client- FTP client
Static Assets (for SPA example):
static/index.html- Main HTML pagestatic/about.html- About pagestatic/contact.html- Contact page with formstatic/styles.css- CSS stylesstatic/app.js- Client-side JavaScript
To run any example:
zig build run-<example-name>
# e.g., zig build run-simple-get
# e.g., zig build run-spa-fallback# Host runtime validation
zig build test
zig build run-all-examples # Runs sequentially to prevent parallel compiler OOM
# Cross-target library compile validation
zig build build-all-examples -Dtarget=x86_64-linux-gnuTo validate Linux runtime behavior, run the cross-compiled artifacts on Linux/WSL (a foreign-target zig build test only compiles; it does not execute):
zig build test -Dtarget=x86_64-linux-gnu
zig build run-simple-get -Dtarget=x86_64-linux-gnuFor explicit cross-target compilation:
# Compile tests for 32-bit Windows
zig build test -Dtarget=x86-windows-gnu
# Compile an example for macOS ARM64
zig build run-simple-get -Dtarget=aarch64-macosRun benchmarks:
zig build benchBenchmark target: x86_64-windows, ReleaseFast (measured 2026-10-02).
| Benchmark | Category | Avg Latency | Throughput | Target |
|---|---|---|---|---|
headers_parse |
Core Operations | 257.49 ns/op | 3883591 ops/sec | x86_64-windows |
uri_parse |
Core Operations | 35.65 ns/op | 28046635 ops/sec | x86_64-windows |
status_lookup |
Core Operations | 2.23 ns/op | 449147518 ops/sec | x86_64-windows |
method_lookup |
Core Operations | 19.74 ns/op | 50650119 ops/sec | x86_64-windows |
http1_request_head |
Core Operations | 24.58 ns/op | 40687786 ops/sec | x86_64-windows |
http1_header_block |
Core Operations | 263.30 ns/op | 3797967 ops/sec | x86_64-windows |
router_static_match |
Routing | 1.02 µs/op | 977952 ops/sec | x86_64-windows |
router_param_match |
Routing | 1.06 µs/op | 944143 ops/sec | x86_64-windows |
router_dispatch |
Routing | 1.03 µs/op | 970782 ops/sec | x86_64-windows |
router_typed_match |
Routing | 1.14 µs/op | 880393 ops/sec | x86_64-windows |
router_miss_404 |
Routing | 1.81 µs/op | 550989 ops/sec | x86_64-windows |
router_reverse |
Routing | 68.65 ns/op | 14565687 ops/sec | x86_64-windows |
json_stringify |
Serialization | 245.93 ns/op | 4066174 ops/sec | x86_64-windows |
json_parse |
Serialization | 312.18 ns/op | 3203298 ops/sec | x86_64-windows |
basic_auth_encode |
Security | 24.20 ns/op | 41328974 ops/sec | x86_64-windows |
basic_auth_decode |
Security | 23.31 ns/op | 42897466 ops/sec | x86_64-windows |
bearer_token_parse |
Security | 7.82 ns/op | 127813494 ops/sec | x86_64-windows |
gzip_compress |
Compression | 53.11 µs/op | 18830 ops/sec | x86_64-windows |
gzip_decompress |
Compression | 6.64 µs/op | 150626 ops/sec | x86_64-windows |
deflate_compress |
Compression | 63.54 µs/op | 15737 ops/sec | x86_64-windows |
deflate_decompress |
Compression | 6.48 µs/op | 154405 ops/sec | x86_64-windows |
html_parse |
Parsing | 20.13 µs/op | 49668 ops/sec | x86_64-windows |
template_parse |
Parsing | 2.90 µs/op | 345360 ops/sec | x86_64-windows |
template_render |
Parsing | 1.46 µs/op | 683819 ops/sec | x86_64-windows |
template_incremental |
Parsing | 22.94 µs/op | 43597 ops/sec | x86_64-windows |
json_feed_parse |
Parsing | 3.78 µs/op | 264404 ops/sec | x86_64-windows |
live_reload_inject |
Parsing | 230.33 ns/op | 4341596 ops/sec | x86_64-windows |
watcher_scan |
Watcher | 1.14 ms/op | 879 ops/sec | x86_64-windows |
watcher_deps |
Watcher | 4.21 µs/op | 237375 ops/sec | x86_64-windows |
worker_pool_submit |
Concurrency | 228.54 ns/op | 4375553 ops/sec | x86_64-windows |
concurrency_queue |
Concurrency | 37.93 ns/op | 26365676 ops/sec | x86_64-windows |
dns_cache_hit |
DNS | 48.49 ns/op | 20623531 ops/sec | x86_64-windows |
h2_frame_header |
Protocols | 1.09 ns/op | 921523093 ops/sec | x86_64-windows |
hpack_int_encode |
Protocols | 1.04 ns/op | 963131332 ops/sec | x86_64-windows |
hpack_int_decode |
Protocols | 1.42 ns/op | 704508147 ops/sec | x86_64-windows |
h3_varint_encode |
Protocols | 1.58 ns/op | 632739191 ops/sec | x86_64-windows |
h3_varint_decode |
Protocols | 1.59 ns/op | 627817330 ops/sec | x86_64-windows |
tls_record_seal |
TLS | 1.62 µs/op | 619051 ops/sec | x86_64-windows |
tls_cert_parse |
TLS | 1.07 µs/op | 938620 ops/sec | x86_64-windows |
client_server_get |
Network | 387.84 µs/op | 2578 req/sec | x86_64-windows |
h2_pooled_get |
Network | 55.89 µs/op | 17891 req/sec | x86_64-windows |
h3_get |
Network | 205.89 ms/op | 4 req/sec | x86_64-windows |
tls_full_handshake |
TLS | 4.35 ms/op | 230 ops/sec | x86_64-windows |
tls_resumed_handshake |
TLS | 2.94 ms/op | 339 ops/sec | x86_64-windows |
See docs/reference/benchmarks.md for full methodology and detailed analysis.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
zig build test - Submit a pull request
httpx.zig/
├── src/
│ ├── httpx.zig # Public API entry point & re-exports
│ ├── client/ # HTTP client
│ │ ├── client.zig # Client struct, connection pooling, retry
│ │ ├── request.zig # Raw request API, TLS options, auto-TLS
│ │ ├── cookies.zig # Client cookie jar
│ │ └── download.zig # File download, resume, verify, batch
│ ├── server/
│ │ └── lifecycle.zig # Server struct, Config, run/stop/start, Ctrl+C
│ ├── web/
│ │ ├── router/ # Router, Context, Response, pattern matching
│ │ ├── middleware/ # CORS, Helmet, rate-limit, auth, CSRF, proxy
│ │ ├── static_files/ # Static file serving, ETag, MIME detection
│ │ ├── spa/ # SPA HTML5 fallback serving
│ │ ├── openapi/ # OpenAPI 3.1 spec generation
│ │ ├── docs/ # Swagger UI, ReDoc, Scalar, GraphiQL
│ │ ├── graphql/ # GraphQL schema, resolvers, mount
│ │ ├── sse/ # Server-Sent Events writer/parser
│ │ ├── websocket/ # WebSocket handshake & frames
│ │ ├── multipart/ # Multipart form encoder/parser
│ │ ├── health/ # Health check endpoints
│ │ ├── metrics/ # Metrics registry
│ │ ├── auth/ # Basic & Bearer auth helpers
│ │ ├── templates/ # Flask-style template engine (Tree-sitter syntax, cached AST)
│ │ ├── site/ # File-based website routing
│ │ ├── watcher/ # File watcher + browser live-reload client
│ │ ─ ─ ─ ─ backend.zig # Watcher facade, index, queue, debounce
│ │ ─ ─ ─ ─ events.zig # Event model, classification, coalescing
│ │ ─ ─ ─ ─ client.zig # Browser-side SSE live-reload script
│ │ ─ ─ ─ ─ dependency.zig # Dependency graph for reload invalidation
│ │ ─ ─ ─ ─ windows/linux/macos.zig # Native backends (RDC/inotify/kqueue)
│ ├── protocols/
│ │ ├── http1/ # HTTP/1.x parser & writer
│ │ ├── http2/ # HTTP/2 frame, HPACK, transport
│ │ ├── http3/ # HTTP/3 frame, connection
│ │ ├── quic/ # QUIC varint, packet, crypto
│ │ ├── tls/ # TLS 1.3 server engine + native client, QUIC-TLS, ALPN, mTLS
│ │ ├── ftp/ # FTP client & server
│ │ └── common/ # Shared protocol utilities
│ ├── net/
│ │ ├── resolve.zig # DNS resolver with caching
│ │ ├── address.zig # Network address abstraction
│ │ ├── socks5.zig # SOCKS5/5h proxy tunneling
│ │ ├── socks4.zig # SOCKS4/4a proxy tunneling
│ │ ├── proxy.zig # HTTP proxy support
│ │ ├── connectivity.zig # Connectivity probing
│ │ └── dns/ # DNS protocol + cache
│ ├── sockets/
│ │ ├── tcp.zig # Cross-platform TCP socket (IOCP/epoll)
│ │ ├── udp.zig # UDP socket
│ │ └── sys.zig # Raw syscall layer + error mapping
│ ├── compression/ # gzip, brotli, zstd, deflate
│ ├── concurrency/ # WorkerPool, parallel requests
│ ├── parsing/ # HTML/XML DOM, CSS selectors, feeds
│ │ ├── html.zig # HTML5 parser
│ │ ├── xml.zig # XML parser
│ │ ├── selector.zig # CSS selector engine
│ │ ├── dom.zig # DOM tree traversal
│ │ ├── document.zig # Document abstraction
│ │ ├── extract.zig # Content extraction
│ │ ├── feed.zig # RSS/Atom/JSON feed parser
│ │ ├── robots.zig # robots.txt parser
│ │ └── sitemap.zig # Sitemap parser
│ ├── common/ # Method, Status, Headers, URI, Logger, clock
│ ├── utils/ # MIME detection (mime.zig), filesystem helpers (fs.zig)
│ └── assets/ # Embedded UI assets (Swagger, ReDoc, GraphiQL)
├── examples/ # 80+ runnable examples
│ ├── simpleGet.zig # Basic HTTP GET
│ ├── postJson.zig # POST with JSON body
│ ├── simpleServer.zig # Minimal HTTP server
│ ├── graphqlServer.zig # GraphQL + REST + OpenAPI
│ ├── tlsGet.zig # HTTPS with TLS
│ ├── download.zig # File download with progress
│ └── ... # 70+ more (see examples/ dir)
├── bench/
│ └── main.zig # Microbenchmarks
├── docs/ # VitePress documentation site
├── build.zig # Build system
├── build.zig.zon # Package metadata
├── README.md
├── SECURITY.md
├── LICENSE
└── CONTRIBUTING.md
MIT License - see LICENSE for details.
