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.
Important
- HTTP/1.0 to HTTP/3: A unified library for multiple HTTP protocol versions.
- Complete HTTP stack: Client, server, TLS, DNS, proxies, connection management, and networking.
- User-friendly API: A clear
httpx.Clientworkflow without unnecessary convenience functions or duplicate APIs. - Explicit resource ownership: Clear allocator, I/O, memory-management, and resource-lifecycle responsibilities.
- Built-in web framework: Routing, middleware, authentication, static files, WebSockets, multipart processing, and more.
- Downloads and streaming: Progress reporting, resumable downloads, checksum verification, and batch operations.
- Additional capabilities: Compression, HTML/XML parsing, templates, GraphQL, REST, OpenAPI, and other web application features.
- Modular architecture: Organized components, direct dependency integration, and consistent error handling.
httpx.zig brings HTTP clients, servers, networking, and web application functionality together in one native Zig library, with a focus on usability, explicit ownership, and maintainable APIs.
Refer to the documentation for installation instructions, examples, API references, supported features, and migration guidance.
Note
httpx.zig is actively evolving, and breaking API or behavioral changes may occur between releases. If you're using an older version, consider upgrading and review the release notes and documentation before migrating.
Contributions are welcome! Bug reports, feature suggestions, documentation improvements, and pull requests are appreciated.
Similar Zig Projects (click to expand)
- For environment variables and
.envparsing, check out env.zig. - For TUI (Terminal User Interface) support, check out tui.zig.
- For ZON file format support, check out zon.zig.
- For spinners, loading indicators, and progress bars, check out loaders.zig.
- For terminal colors and text styling, check out tint.zig.
- For Model Context Protocol (MCP) support, check out mcp.zig.
- For API framework support, check out api.zig.
- For web framework support, check out zix.
- For archive handling and compression, check out archive.zig.
- For compression file formats, check out zigx.
- For CUDA support, check out cuda.zig.
- For simplified
build.zigconfiguration, check out buildx.zig. - For SQLite with a Zig-native implementation, check out sqlite.zig.
- For file downloading, check out downloader.zig.
- For update checking and automatic updates, check out updater.zig.
- For numerical computing, check out num.zig.
- For logging, check out logly.zig.
- For data validation and serialization, check out zigantic.
- For UUID support, check out uuid.zig.
- For key-value database support, check out zkv.zig.
- For terminal colors and text styling, check out hint.zig.
- For Brotli compression, check out brotli.zig.
- For Zstandard (Zstd) compression, check out zstd.zig.
- For Tree-sitter integration, check out tree-sitter.zig.
- For regular expressions, check out regex.zig.
Features (click to expand)
| Feature | Description |
|---|---|
| Protocol Support | HTTP/1.0, HTTP/1.1, and HTTP/2 client/server runtimes; HTTP/3 client/server runtime over QUIC/TLS 1.3. QUIC includes ACK tracking, PTO/loss recovery, NewReno congestion control, flow control, transport parameters, 0-RTT resumption, Retry/token validation, v1/v2 version negotiation, and 1-RTT key updates. HTTP/3 still has no HelloRetryRequest handling or connection migration; the high-level client pools HTTP/3 connections for sequential reuse and does not multiplex concurrent requests over one connection. |
| Header Compression | HPACK (RFC 7541) for HTTP/2; QPACK (RFC 9204) for HTTP/3 with static and dynamic table management. |
| Response Compression | Automatic Accept-Encoding negotiation and on-the-fly compression for zstd, Brotli, gzip, and deflate across HTTP/1.1, HTTP/2, and HTTP/3, with configurable codec levels and input bounds. |
| 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. The HTTP/2 server keeps one state per concurrent stream; the high-level client serializes per pooled connection. |
| Connection Pooling | TCP keep-alive reuse, HTTP/2 and HTTP/3 connection pooling, transport-level stream multiplexing (the high-level client serializes per pooled connection), and TLS session resumption caching. |
| Unified DOM & Web Parsing | DOM parsing and inspection for HTML, XML, RSS/Atom/JSON feeds, robots.txt, sitemaps, and a documented CSS-selector subset 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), automated CSRF protection, and recovery; rate limiting, server event callbacks, health endpoints, and CSRF token helpers. |
| 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), X25519MLKEM768/X25519 key exchange, ChaCha20-Poly1305/AES-128-GCM server record 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 opt-in 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 | Standalone RFC 6265 cookie jar plus per-request cookie and header helpers for client and server contexts. |
| Security & Hardening | Security headers (Helmet), automated CSRF middleware and token helpers, and CRLF injection defenses. |
| Multipart Form Data | RFC 2046 multipart encoder and parser for text fields and file parts with streaming disk-sink and bounded buffered support. |
| FTP & FTPS | FTP client and server with PASV/EPSV, directory listing, streaming uploads/downloads, and resumption; explicit FTPS (AUTH TLS/PBSZ/PROT) is supported. |
| Concurrency & Workers | Thread-safe bounded httpx.concurrency.Pool 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.3)
zig fetch --save https://github.com/muhammad-fiaz/httpx.zig/archive/refs/tags/0.2.3.tar.gzPrevious Release (v0.2.2)
zig fetch --save https://github.com/muhammad-fiaz/httpx.zig/archive/refs/tags/0.2.2.tar.gzWarning
Zig 0.17 is required by v0.2.3. 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.3.
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.3.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.
Initialize an httpx.Client with your allocator and I/O context. The client provides built-in connection pooling, TLS session resumption, and DNS caching:
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();
// 1. Initialize client
var client = httpx.Client.init(allocator, io, .{
.timeoutMs = 10_000,
.followRedirects = true,
.maxRedirects = 5,
.dnsCache = .{ .enable = true, .ttlMs = 60_000 },
});
defer client.deinit();
// 2. GET request
var res = try client.get("https://httpbun.com/get", .{});
defer res.deinit();
std.debug.print("Status: {d}, Body: {s}\n", .{ res.status, res.text() });
// 3. POST with strongly-typed JSON payload
const User = struct { name: []const u8, role: []const u8 };
var postRes = try client.postJson("https://httpbun.com/post", User{
.name = "Alice",
.role = "engineer",
}, .{});
defer postRes.deinit();
}Execute standard HTTP methods and advanced requests across shared connections:
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();
// Initialize stateful client
var client = httpx.Client.init(allocator, io, .{
.timeoutMs = 10_000,
.followRedirects = true,
.maxRedirects = 5,
.dnsCache = .{ .enable = true, .ttlMs = 60_000 },
});
defer client.deinit();
// Standard HTTP methods
var getRes = try client.get("https://httpbun.com/get", .{});
defer getRes.deinit();
var postRes = try client.post("https://httpbun.com/post", .{
.body = "hello=world",
.headers = &.{.{ .name = "content-type", .value = "application/x-www-form-urlencoded" }},
});
defer postRes.deinit();
// Strongly-typed JSON serialization & deserialization
const CreateUser = struct { name: []const u8, email: []const u8 };
var userRes = try client.postJson("https://httpbun.com/post", CreateUser{
.name = "Alice",
.email = "alice@example.com",
}, .{});
defer userRes.deinit();
const Echo = struct { json: ?CreateUser = null };
const echo = try userRes.json(Echo);
if (echo.json) |user| std.debug.print("Created: {s} <{s}>\n", .{ user.name, user.email });
// Additional HTTP verbs
var putRes = try client.put("https://httpbun.com/put", .{ .body = "updated" });
defer putRes.deinit();
var patchRes = try client.patch("https://httpbun.com/patch", .{ .body = "partial" });
defer patchRes.deinit();
var delRes = try client.delete("https://httpbun.com/delete", .{});
defer delRes.deinit();
}For advanced request assembly with headers, query parameters, authorization, and timeouts:
var builder = client.requestBuilder(.POST, "https://httpbun.com/post");
defer builder.deinit();
_ = try builder.header("X-API-Key", "secret-token-123");
_ = try builder.query("filter", "active");
_ = builder.timeoutMs(5000);
_ = builder.bearerAuth("jwt-token-xyz");
_ = try builder.json(.{ .query = "search term", .page = @as(u32, 1) });
var res = try builder.send();
defer res.deinit();
std.debug.print("Response: {d} {s}\n", .{ res.status, res.text() });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; free the slice with the client's allocator.
const urls = [_][]const u8{
"https://httpbun.com/get",
"https://httpbun.com/headers",
};
var results = try client.getAll(urls);
defer {
for (results) |*r| r.deinit();
allocator.free(results);
}
// Parallel requests - requestAll
const reqs = .{
.{ .method = .GET, .url = "https://httpbun.com/get" },
.{ .method = .GET, .url = "https://httpbun.com/headers" },
};
var batch = try client.requestAll(reqs);
defer {
for (batch) |*r| r.deinit();
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.client.download.ftpDownload(io, 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.parsing.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.parsing.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.router.Context) anyerror!httpx.router.Response {
return ctx.renderJson(.{ .message = "Hello!" });
}
fn page(ctx: *httpx.router.Context) anyerror!httpx.router.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.router.Context, req: CreateItemRequest) !httpx.ApiResult(ItemResponse) {
_ = ctx;
return .created(.{
.id = 101,
.name = req.name,
.price = req.price,
});
}
fn getItem(ctx: *httpx.router.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.net.resolve.Resolver.init(allocator, io);
const addrs = try resolver.lookup("example.com", .{ .port = 443 });
defer allocator.free(addrs);fn handler(ctx: *httpx.router.Context) anyerror!httpx.router.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- Client retries with linear 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- Buffered response reader (plus SSE examples)auth_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- Cookie-backed session flowmetrics_server- Prometheus metricsinterceptor_example- Client request wrappers around a server routecookie_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 reloadliveReload- 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/liveReload- 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-macosBenchmarks (click to expand)
Run the benchmarks with:
zig build benchBenchmark target: x86_64-windows, ReleaseFast. Measurements recorded on October 2, 2026.
| Benchmark | Category | Average Latency | Throughput | Target |
|---|---|---|---|---|
headers_parse |
Core Operations | 257.49 ns/op | 3,883,591 ops/sec | x86_64-windows |
uri_parse |
Core Operations | 35.65 ns/op | 28,046,635 ops/sec | x86_64-windows |
status_lookup |
Core Operations | 2.23 ns/op | 449,147,518 ops/sec | x86_64-windows |
method_lookup |
Core Operations | 19.74 ns/op | 50,650,119 ops/sec | x86_64-windows |
http1_request_head |
Core Operations | 24.58 ns/op | 40,687,786 ops/sec | x86_64-windows |
http1_header_block |
Core Operations | 263.30 ns/op | 3,797,967 ops/sec | x86_64-windows |
router_static_match |
Routing | 1.02 µs/op | 977,952 ops/sec | x86_64-windows |
router_param_match |
Routing | 1.06 µs/op | 944,143 ops/sec | x86_64-windows |
router_dispatch |
Routing | 1.03 µs/op | 970,782 ops/sec | x86_64-windows |
router_typed_match |
Routing | 1.14 µs/op | 880,393 ops/sec | x86_64-windows |
router_miss_404 |
Routing | 1.81 µs/op | 550,989 ops/sec | x86_64-windows |
router_reverse |
Routing | 68.65 ns/op | 14,565,687 ops/sec | x86_64-windows |
json_stringify |
Serialization | 245.93 ns/op | 4,066,174 ops/sec | x86_64-windows |
json_parse |
Serialization | 312.18 ns/op | 3,203,298 ops/sec | x86_64-windows |
basic_auth_encode |
Security | 24.20 ns/op | 41,328,974 ops/sec | x86_64-windows |
basic_auth_decode |
Security | 23.31 ns/op | 42,897,466 ops/sec | x86_64-windows |
bearer_token_parse |
Security | 7.82 ns/op | 127,813,494 ops/sec | x86_64-windows |
gzip_compress |
Compression | 53.11 µs/op | 18,830 ops/sec | x86_64-windows |
gzip_decompress |
Compression | 6.64 µs/op | 150,626 ops/sec | x86_64-windows |
deflate_compress |
Compression | 63.54 µs/op | 15,737 ops/sec | x86_64-windows |
deflate_decompress |
Compression | 6.48 µs/op | 154,405 ops/sec | x86_64-windows |
html_parse |
Parsing | 20.13 µs/op | 49,668 ops/sec | x86_64-windows |
template_parse |
Parsing | 2.90 µs/op | 345,360 ops/sec | x86_64-windows |
template_render |
Parsing | 1.46 µs/op | 683,819 ops/sec | x86_64-windows |
template_incremental |
Parsing | 22.94 µs/op | 43,597 ops/sec | x86_64-windows |
json_feed_parse |
Parsing | 3.78 µs/op | 264,404 ops/sec | x86_64-windows |
live_reload_inject |
Parsing | 230.33 ns/op | 4,341,596 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 | 237,375 ops/sec | x86_64-windows |
worker_pool_submit |
Concurrency | 228.54 ns/op | 4,375,553 ops/sec | x86_64-windows |
concurrency_queue |
Concurrency | 37.93 ns/op | 26,365,676 ops/sec | x86_64-windows |
dns_cache_hit |
DNS | 48.49 ns/op | 20,623,531 ops/sec | x86_64-windows |
h2_frame_header |
Protocols | 1.09 ns/op | 921,523,093 ops/sec | x86_64-windows |
hpack_int_encode |
Protocols | 1.04 ns/op | 963,131,332 ops/sec | x86_64-windows |
hpack_int_decode |
Protocols | 1.42 ns/op | 704,508,147 ops/sec | x86_64-windows |
h3_varint_encode |
Protocols | 1.58 ns/op | 632,739,191 ops/sec | x86_64-windows |
h3_varint_decode |
Protocols | 1.59 ns/op | 627,817,330 ops/sec | x86_64-windows |
tls_record_seal |
TLS | 1.62 µs/op | 619,051 ops/sec | x86_64-windows |
tls_cert_parse |
TLS | 1.07 µs/op | 938,620 ops/sec | x86_64-windows |
client_server_get |
Network | 387.84 µs/op | 2,578 req/sec | x86_64-windows |
h2_pooled_get |
Network | 55.89 µs/op | 17,891 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 |
For the full benchmark methodology, measurement details, and analysis, see Benchmark Documentation.
Note: These are recorded measurements for the specified platform and build mode, not universal performance guarantees. Results may vary with hardware, compiler version, workload, and system configuration.
The examples/ directory contains runnable examples demonstrating all features of httpx.zig.
To run any example:
zig build run-<example-name>
# Examples:
# zig build run-simple-get
# zig build run-spa-fallback
# zig build run-download
# zig build run-rest-apisimple_get— Basic GET requestspost_json— POST with JSON bodycustom_headers— Custom header managementconnection_pool— Connection pooling and statsredirect— Redirect handlinghttp10_client— HTTP/1.0 clienthttp11_client— HTTP/1.1 clienttls_get— HTTPS client with TLShttps_client— HTTPS client with TLSretry_demo— Client retries with linear backoffconcurrent_demo— Parallel request patterns
Server & Web API Examples (Click to expand)
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— Buffered response reader (plus SSE examples)auth_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— Cookie-backed session flowmetrics_server— Prometheus metricsinterceptor_example— Client request wrappers around a server routecookie_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 servingbrowser_demo_server— Browser demo serverfull_integration— Full client/server integration
Download & File Inspection Examples (Click to expand)
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 (DOM Engine) Examples (Click to expand)
html_client— Client fetch and automatic response.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 Examples (Click to expand)
file_watcher— OS-native file monitoring (Windows ReadDirectoryChangesW, Linux inotify, macOS kqueue) with rename pairing and browser live reloadliveReload— 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 parsingstatic/index.html— Main HTML page for SPA examplestatic/about.html— About pagestatic/contact.html— Contact page with formstatic/styles.css— CSS stylesstatic/app.js— Client-side JavaScript
Protocols, Security & Network Transport Examples (Click to expand)
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 QUIChttp3_0rtt— HTTP/3 Zero-RTT connection resumptiontls12_client— TLS 1.2 with self-signed certtls13_client— TLS 1.3 with self-signed certtls_mtls— Mutual TLS (mTLS)resolve— DNS resolutiondns_demo— DNS resolution and IP checksdns_cache— DNS cachingproxy_demo— HTTP forward proxyconnectivity— Online checks and connectivity probescompression_demo— gzip/deflate/brotli compressionftp_client— FTP clienttcp_local— Local TCP socket server and client communicationudp_local— Local UDP datagram transmission and reception
Templates & Website Examples (Click to expand)
web/templates/basic— Basic template renderingweb/templates/inheritance— Template inheritanceweb/templates/loops— Template loopsweb/templates/includes— Template includesweb/templates/liveReload— Live-reload templatesweb/templates/jinja— Flask/Jinja2-style templates (extends, blocks, includes, macros, filters)website— Full website with embedded assets
Advanced Features (Click to expand)
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
Project Structure (click to expand)
httpx.zig/
├── src/
│ ├── httpx.zig # Public API entry point and exports
│ ├── client/ # HTTP client engine
│ │ ├── httpClient.zig # Client, connection pooling, retries
│ │ ├── requestBuilder.zig # Request construction and options
│ │ ├── connectionPool.zig # Connection pool management and stats
│ │ ├── cookieJar.zig # RFC 6265 client cookie jar
│ │ └── downloadManager.zig # Downloads, resume, verification, batches
│ ├── server/
│ │ └── httpServer.zig # Server, routing, TLS, lifecycle
│ ├── web/
│ │ ├── app.zig # Unified application facade
│ │ ├── router/ # Router, context, response, patterns
│ │ ├── middleware/ # CORS, security, rate limiting, auth, CSRF
│ │ ├── rest/ # REST API framework and OpenAPI reflection
│ │ ├── staticFiles/ # Static files, ETags, MIME detection
│ │ ├── spa/ # SPA fallback serving
│ │ ├── openApi/ # OpenAPI 3.1 specification generation
│ │ ├── docs/ # Swagger UI, ReDoc, Scalar, GraphiQL
│ │ ├── graphql/ # GraphQL schema, resolvers, mounting
│ │ ├── sse/ # Server-Sent Events
│ │ ├── websocket/ # WebSocket handshake and frames
│ │ ├── multipart/ # Multipart encoding and parsing
│ │ ├── health/ # Health-check endpoints
│ │ ├── metrics/ # Metrics registry and Prometheus output
│ │ ├── auth/ # Basic and Bearer authentication
│ │ ├── templates/ # Native template engine
│ │ ├── site/ # File-based website routing
│ │ └── watcher/ # File watching and browser live reload
│ │ ├── watcherBackend.zig # Watcher facade, debounce, indexing
│ │ ├── watcherEvents.zig # Event model and classification
│ │ ├── fileWatcher.zig # Browser-side live-reload script
│ │ ├── watcherDependency.zig# Dependency graph for invalidation
│ │ ├── windowsWatcher.zig # Windows directory-change backend
│ │ ├── linuxWatcher.zig # Linux inotify backend
│ │ └── macosWatcher.zig # macOS watcher backend
│ ├── protocols/
│ │ ├── http1/ # HTTP/1.x parsing, writing, semantics
│ │ ├── http2/ # HTTP/2 frames, HPACK, transport
│ │ ├── http3/ # HTTP/3 frames, QPACK, transport
│ │ ├── quic/ # QUIC packets, crypto, frames, connections
│ │ ├── tls/ # TLS, ALPN, mTLS, certificates
│ │ ├── ftp/ # FTP client and server
│ │ └── common/ # Shared protocol utilities
│ ├── net/
│ │ ├── address.zig # IPv4 and IPv6 address abstraction
│ │ ├── connectivity.zig # Connectivity probing
│ │ ├── dns/ # DNS resolution and caching
│ │ │ ├── dnsResolver.zig # DNS resolution
│ │ │ └── dnsCache.zig # TTL-aware DNS cache
│ │ ├── proxy.zig # HTTP proxy support
│ │ ├── resolve.zig # Host resolution abstraction
│ │ ├── socks4.zig # SOCKS4/4a tunneling
│ │ └── socks5.zig # SOCKS5/5h tunneling
│ ├── sockets/
│ │ ├── tcp.zig # Cross-platform TCP sockets
│ │ ├── udp.zig # UDP socket wrapper
│ │ └── sys.zig # System calls and error mapping
│ ├── compression/ # gzip, Brotli, Zstandard, deflate
│ ├── concurrency/ # Pools, bounded queues, parallel requests
│ ├── parsing/ # HTML/XML DOM, CSS selectors, feeds
│ │ ├── html.zig # HTML parser
│ │ ├── xml.zig # XML parser
│ │ ├── selector.zig # CSS selector engine
│ │ ├── dom.zig # DOM traversal
│ │ ├── document.zig # Document abstraction
│ │ ├── extract.zig # Content extraction
│ │ ├── feed.zig # RSS, Atom, JSON feeds
│ │ ├── robots.zig # robots.txt parser
│ │ └── sitemap.zig # Sitemap parser
│ ├── common/ # Methods, status, headers, URI, I/O
│ ├── utils/ # MIME detection and filesystem helpers
│ └── assets/ # Embedded UI assets
├── examples/ # Runnable usage examples
├── bench/
│ └── main.zig # Benchmarks
├── docs/ # VitePress documentation
├── build.zig # Build configuration
├── build.zig.zon # Package metadata
├── README.md
├── SECURITY.md
├── LICENSE
└── CONTRIBUTING.md
MIT License - see LICENSE for details.
