User Agent Reference
January 20, 2026 ยท View on GitHub
The UserAgent module provides User-Agent string parsing.
UserAgent
parse
pub fn parse(
user_agent: []const u8,
family: []u8,
major: []u8,
minor: []u8,
patch: []u8,
) !ParseResult
Parse a User-Agent string to extract browser information.
const UserAgent = zigly.UserAgent;
var family_buf: [64]u8 = undefined;
var major_buf: [16]u8 = undefined;
var minor_buf: [16]u8 = undefined;
var patch_buf: [16]u8 = undefined;
const result = try UserAgent.parse(
user_agent,
&family_buf,
&major_buf,
&minor_buf,
&patch_buf,
);
std.debug.print("Browser: {s} {s}.{s}.{s}\n", .{
result.family,
result.major,
result.minor,
result.patch,
});
ParseResult
pub const ParseResult = struct {
family: []u8, // Browser family ("Chrome", "Firefox", "Safari")
major: []u8, // Major version
minor: []u8, // Minor version
patch: []u8, // Patch version
};
Example Usage
Browser Detection
const std = @import("std");
const zigly = @import("zigly");
const UserAgent = zigly.UserAgent;
fn start() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var downstream = try zigly.downstream();
const ua = downstream.request.headers.get(allocator, "User-Agent") catch {
try downstream.proxy("origin", null);
return;
};
var family_buf: [64]u8 = undefined;
var major_buf: [16]u8 = undefined;
var minor_buf: [16]u8 = undefined;
var patch_buf: [16]u8 = undefined;
if (UserAgent.parse(ua, &family_buf, &major_buf, &minor_buf, &patch_buf)) |result| {
// Add browser info to request
try downstream.request.headers.set("X-Browser-Family", result.family);
try downstream.request.headers.set("X-Browser-Version", result.major);
} else |_| {}
try downstream.proxy("origin", null);
}
Legacy Browser Detection
fn isLegacyBrowser(ua: []const u8) bool {
var family_buf: [64]u8 = undefined;
var major_buf: [16]u8 = undefined;
var minor_buf: [16]u8 = undefined;
var patch_buf: [16]u8 = undefined;
const result = UserAgent.parse(ua, &family_buf, &major_buf, &minor_buf, &patch_buf) catch return false;
const major = std.fmt.parseInt(u32, result.major, 10) catch return false;
// Check for old browsers
if (std.mem.eql(u8, result.family, "IE")) {
return major < 11;
}
if (std.mem.eql(u8, result.family, "Chrome")) {
return major < 80;
}
if (std.mem.eql(u8, result.family, "Firefox")) {
return major < 75;
}
if (std.mem.eql(u8, result.family, "Safari")) {
return major < 13;
}
return false;
}
fn start() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var downstream = try zigly.downstream();
const ua = downstream.request.headers.get(allocator, "User-Agent") catch "";
if (isLegacyBrowser(ua)) {
try downstream.redirect(302, "/legacy-browser-warning");
return;
}
try downstream.proxy("origin", null);
}
Browser-Specific Caching
fn start() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var downstream = try zigly.downstream();
const ua = downstream.request.headers.get(allocator, "User-Agent") catch "";
var family_buf: [64]u8 = undefined;
var major_buf: [16]u8 = undefined;
var minor_buf: [16]u8 = undefined;
var patch_buf: [16]u8 = undefined;
const browser_key = if (UserAgent.parse(ua, &family_buf, &major_buf, &minor_buf, &patch_buf)) |result|
result.family
else |_|
"unknown";
// Vary cache by browser family
try downstream.request.headers.set("X-Browser-Family", browser_key);
try downstream.proxy("origin", null);
}
Notes
- User-Agent parsing is best-effort; not all UAs will be recognized
- For device type detection (mobile/tablet/desktop), use the device module instead
- Browser families include: Chrome, Firefox, Safari, Edge, IE, Opera, and others
- Version numbers may be empty strings if not detected