|claim|login
RepoCritics — Review. Share. Archive. Every open-source repo.

karlseguin/http.zig

Wiki: karlseguin/http.zig

Source: https://github.com/karlseguin/http.zig

Last synced 2026-07-18 · 1049 words · Edit wiki on GitHub →

karlseguin/http.zig

> A from-scratch HTTP/1.1 server library for Zig — the de facto base layer of > the Zig web ecosystem, deliberately not built on std.http.Server.

GitHub repo · License: MIT

Overview

http.zig (module name httpz) is an HTTP/1.1 server library by Karl Seguin, started in March 20231. It exists because std.http.Server is slow and assumes well-behaved clients; most other Zig HTTP servers wrap either the std implementation (inheriting its performance) or C libraries2. httpz is pure Zig with its own socket handling and parser; the author reports ~140K requests/second for a basic request on an Apple M22. Higher-level Zig frameworks — JetZig and Tokamak — build on it2, making httpz roughly what net/http is to Go: the substrate others frame over.

The defining tension is Zig itself. Zig is pre-1.0 and breaks its standard library every release, so httpz maintains a branch per Zig version (zig-0.15, dev, etc.); master tracks the latest stable Zig2. As of mid-2026 master targets Zig 0.16 and the author explicitly labels that port experimental and "not well tested"2. Adopting httpz means adopting this churn: upgrade pain comes from the language more than from the library.

At ~1.6k stars with a single primary maintainer, it is small by web-framework standards but among the most-used Zig libraries, and actively developed.

Getting Started

zig fetch --save "git+https://github.com/karlseguin/http.zig#master"
// build.zig
const httpz = b.dependency("httpz", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("httpz", httpz.module("httpz"));
// Zig 0.16 / master API
const std = @import("std");
const httpz = @import("httpz");

pub fn main(init: std.process.Init) !void {
    var server = try httpz.Server(void).init(init.io, init.gpa, .{
        .address = .localhost(5882),
    }, {});

    var router = try server.router(.{});
    router.get("/api/user/:id", getUser, .{});
    try server.listen(); // blocks
}

fn getUser(req: *httpz.Request, res: *httpz.Response) !void {
    try res.json(.{ .id = req.param("id").?, .name = "Teg" }, .{});
}

Architecture / How It Works

Comptime handler generics. The core type is httpz.Server(H) where H is your application handler. With H = void, actions are plain fn(*Request, *Response) !void; with a real handler, the instance is passed to every action (the idiomatic way to share a DB pool or config). Signatures are inferred at compile time — no interface boilerplate, no dispatch cost.

Layered override points. A handler can define, in increasing order of control: dispatch (wrap every action — timing, auth, per-request context structs), notFound, uncaughtError, and finally handle, which bypasses httpz's router and dispatching entirely; JetZig hooks handle to supply its own routing2 — frameworks consume httpz as a raw connection/parsing engine, applications as a router.

Memory model. Each request gets req.arena / res.arena — a configurable thread-local buffer falling back to an std.heap.ArenaAllocator, freed after the response is written2. Response data must stay valid until after the action returns, so the arena (or res.writer()) is the intended path for dynamic bodies. Request bodies land in a static per-connection buffer, a large-buffer pool, or a dynamic allocation depending on size; request.lazy_read_size switches large bodies to streaming via req.reader(timeout_ms) instead of full buffering.

Batteries at the protocol level, not above it. Router with :params, middleware, query/form/multipart/JSON body parsing, server-sent events, internal metrics, a httpz.testing harness for unit-testing actions, and WebSocket upgrades (via the author's companion websocket.zig). No templating, ORM, or sessions — JetZig/Tokamak territory.

Production Notes

  • No TLS. httpz speaks plaintext HTTP/1.1 only — no HTTP/2 or HTTP/3;

terminate TLS at a reverse proxy (nginx, Caddy).

  • Zig version pinning is your main upgrade chore. Pin a zig-X.YY branch

in build.zig.zon. The 0.15 cycle changed the writer API (res.writer() takes a buffer — pass &.{}, httpz buffers internally)2; the 0.16 port rewires I/O through the new std.Io interface and is flagged experimental.

  • Form parsing is off by default. request.max_form_count and

request.max_multiform_count default to 0 — form/multipart parsing yields nothing until raised2. formData() and multiFormData() are mutually exclusive per request; multipart unescapes names in-place, subtly altering a later req.body().

  • Lifetime discipline. Setting res.body to memory that dies when your

action returns is the classic first bug; use res.arena.

  • Single-maintainer risk. Karl Seguin is prolific and responsive (he also

maintains pg.zig and websocket.zig), but the bus factor is 1, with no corporate backing. Issue volume is low (~15 open): little churn, but a small contributor pool.

  • The 140K req/s figure is author-reported on an M2 for a trivial handler2

— an upper bound, not a capacity plan.

When to Use / When Not

Use when:

  • You are writing a Zig service and need an HTTP layer faster and more

defensive than std.http.Server.

  • You want a library, not a framework — routing plus request/response

primitives, with your own architecture on top.

  • You need WebSockets or SSE from the same server.

Avoid when:

  • You need TLS in-process or HTTP/2+ — proxy in front, or use another stack.
  • You want rails-style productivity in Zig — JetZig or Tokamak (both built on

httpz) give you more out of the box.

  • Your team won't track Zig's breaking releases; a Go or Rust HTTP stack is

operationally far more stable.

Alternatives

  • zigzap/zap — wraps the C library facil.io; mature C core, but not pure Zig.

Use it for a batteries-heavier microframework if the C dependency is fine.

  • jetzig-framework/jetzig — full MVC-style framework built on httpz; use it

when you want templating, generators, and conventions rather than a library.

  • cztomsik/tokamak — dependency-injection-oriented server framework on httpz;

use it for structured larger apps with DI ergonomics.

  • ziglang/zig (std.http.Server) — zero dependencies; fine for internal

tools and tests where throughput and hostile clients don't matter.

History

EraDateNotes
Initial2023-03Repo created; HTTP/1.1 server independent of std.http.Server1. Branch-per-Zig-version model: master = latest stable Zig, dev = Zig master, zig-X.YY for older2.
Zig 0.14 support2025-03Tracks Zig 0.14.0 release.
Zig 0.15 support2025-08New std.Io.Writer interface; res.writer() gains a buffer parameter2.
Zig 0.16 port2026main(init: std.process.Init) / std.Io rework; author labels it experimental2.

References

  1. ^ GitHub repository metadata — created 2023-03-13. https://github.com/karlseguin/http.zig
  2. ^ http.zig README (versions, alternatives, performance claim, handler/dispatch, memory & arenas, configuration). https://github.com/karlseguin/http.zig#readme

Tags

zig, http-server, http-1-1, web, networking, server-library, routing, websocket, server-sent-events, low-level