A minimal, secure HTTPS client for Zig.
Single static binary that fetches URLs over HTTPS with strict security defaults. Replaces curl in restricted environments where you need a small, auditable tool with no runtime dependencies.
Also usable as a Zig library for embedding HTTP(S) fetches in your own programs.
- Single static binary, no runtime dependencies
- HTTP(S) only -- small attack surface by design
- Strict security defaults: TLS verification on, redirect limits enforced, body size capped
- Streaming I/O -- responses written directly to a sink, never buffered in memory
- Deterministic exit codes for scripting
- Embeddable Zig library with allocator-explicit API
- Zig 0.15.2+
- CMake
- Ninja (for building vendored BoringSSL)
git clone https://github.com/nichochar/zfetch.git
cd zfetch
zig build -Doptimize=ReleaseSmallThe binary is at zig-out/bin/zfetch.
# Simple GET request
zfetch https://example.com
# POST with JSON body
zfetch -d '{"key":"val"}' -H 'Content-Type: application/json' https://api.example.com
# PUT with body from stdin
zfetch -X PUT -d @- https://api.example.com/resource < data.json
# Write response to file
zfetch -o output.html https://example.com
# Fail on HTTP errors (exit 22 on 4xx/5xx)
zfetch --fail https://api.example.com/resource| Flag | Description |
|---|---|
-X <METHOD> |
HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD) |
-H <Header: Value> |
Add custom header (repeatable) |
-d <data> |
Send request body (implies POST unless -X given) |
-d @- |
Read request body from stdin |
-o <file> |
Write response body to file instead of stdout |
-s |
Silent mode (suppress status output on stderr) |
-v, --verbose |
Verbose output (TLS diagnostics) |
-k, --insecure |
Disable TLS certificate verification |
--fail |
Exit 22 on HTTP 4xx/5xx errors |
--connect-timeout <s> |
Connection timeout in seconds |
--max-time <s> |
Maximum time for entire request in seconds |
--help |
Show help and exit |
--version |
Show version and exit |
Add zfetch as a dependency in your build.zig.zon:
.dependencies = .{
.zfetch = .{
.url = "https://github.com/nichochar/zfetch/archive/<commit>.tar.gz",
.hash = "...",
},
},Wire it into your build.zig:
const zfetch_dep = b.dependency("zfetch", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("zfetch", zfetch_dep.module("zfetch"));Then use it in your code:
const std = @import("std");
const zfetch = @import("zfetch");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var client = zfetch.Client.init(allocator, .{});
defer client.deinit();
var response = client.fetch("https://example.com", std.fs.File.stdout()) catch |err| {
std.debug.print("error: {s}\n", .{zfetch.errorMessage(err)});
return;
};
defer response.deinit(allocator);
}BoringSSL is vendored and links transparently through the zfetch module -- consumers do not need to configure TLS separately.
| Profile | Use Case | Binary Size |
|---|---|---|
| ReleaseSmall | Containers, minimal environments, CI images | 2.5 MB |
| ReleaseSafe | Servers, development, debugging with safety checks | 26.4 MB |
Build with a specific profile:
zig build -Doptimize=ReleaseSmall # Smallest binary
zig build -Doptimize=ReleaseSafe # Safety checks enabled