diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f6f8e86..8305e29 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,10 +14,18 @@ jobs:
 
     steps:
       - uses: actions/checkout@v3
-      - uses: ./.github/actions/libextism
+      # - uses: ./.github/actions/libextism
+      - name: Install Extism CLI
+        shell: sh
+        run: sudo curl -s https://get.extism.org/cli | sh -s -- -q -y
+      
+      - name: Check Extism version
+        run: extism --version
 
       - name: Install Zig
         uses: goto-bus-stop/setup-zig@v2
+        with:
+          version: 0.13.0
 
       - name: Check Zig Version
         run: zig version
@@ -69,3 +77,5 @@ jobs:
           COUNT=$(echo $TEST | jq | grep "I'm the inner struct" | wc -l)
           test $COUNT -eq 3
 
+          TEST=$(extism call zig-out/bin/basic-example.wasm http_headers --input '' --allow-host github.com --enable-http-response-headers --log-level debug 2>&1)
+          echo $TEST | grep "text/html"
\ No newline at end of file
diff --git a/examples/basic.zig b/examples/basic.zig
index ec1ab63..325b2d2 100644
--- a/examples/basic.zig
+++ b/examples/basic.zig
@@ -163,6 +163,35 @@ export fn http_get() i32 {
     return 0;
 }
 
+export fn http_headers() i32 {
+    const plugin = Plugin.init(allocator);
+
+    var req = http.HttpRequest.init("GET", "https://github.com");
+    defer req.deinit(allocator);
+
+    const res = plugin.request(req, null) catch unreachable;
+    defer res.deinit();
+
+    if (res.status != 200) {
+        plugin.setError("request failed");
+        return @as(i32, res.status);
+    }
+    var headers = res.headers(plugin.allocator) catch |err| {
+        plugin.setErrorFmt("err: {any}, failed to get headers from response!", .{err}) catch unreachable;
+        return -1;
+    };
+    defer headers.deinit();
+
+    const content_type = headers.get("content-type");
+    if (content_type) |t| {
+        plugin.logFmt(.Debug, "got content-type: {s}", .{t.value}) catch unreachable;
+    } else {
+        return 1;
+    }
+
+    return 0;
+}
+
 export fn greet() i32 {
     const plugin = Plugin.init(allocator);
     const user = plugin.getConfig("user") catch unreachable orelse {
diff --git a/src/ffi.zig b/src/ffi.zig
index 815631a..691fe5d 100644
--- a/src/ffi.zig
+++ b/src/ffi.zig
@@ -17,6 +17,7 @@ pub extern "extism:host/env" fn store_u64(ExtismPointer, u64) void;
 pub extern "extism:host/env" fn load_u64(ExtismPointer) u64;
 pub extern "extism:host/env" fn http_request(ExtismPointer, ExtismPointer) ExtismPointer;
 pub extern "extism:host/env" fn http_status_code() i32;
+pub extern "extism:host/env" fn http_headers() ExtismPointer;
 pub extern "extism:host/env" fn get_log_level() i32;
 pub extern "extism:host/env" fn log_trace(ExtismPointer) void;
 pub extern "extism:host/env" fn log_debug(ExtismPointer) void;
diff --git a/src/http.zig b/src/http.zig
index 9525dd7..ac59a27 100644
--- a/src/http.zig
+++ b/src/http.zig
@@ -1,9 +1,51 @@
 const std = @import("std");
 const Memory = @import("Memory.zig");
+const extism = @import("ffi.zig");
+
+pub const Headers = struct {
+    allocator: std.mem.Allocator,
+    raw: []const u8,
+    internal: std.json.ArrayHashMap([]const u8),
+
+    /// Get a value (if it exists) from the Headers map at the provided name.
+    /// NOTE: this may be a multi-value header, and will be a comma-separated list.
+    pub fn get(self: Headers, name: []const u8) ?std.http.Header {
+        const val = self.internal.map.get(name);
+        if (val) |v| {
+            return std.http.Header{
+                .name = name,
+                .value = v,
+            };
+        } else {
+            return null;
+        }
+    }
+
+    /// Access the internal data to iterate over or mutate as needed.
+    pub fn internal(self: Headers) std.json.ArrayHashMap([]const u8) {
+        return self.internal;
+    }
+
+    /// Check if the Headers is empty.
+    pub fn isEmpty(self: Headers) bool {
+        return self.internal.map.entries.len == 0;
+    }
+
+    /// Check if a header exists in the Headers.
+    pub fn contains(self: Headers, key: []const u8) bool {
+        return self.internal.map.contains(key);
+    }
+
+    pub fn deinit(self: *Headers) void {
+        self.allocator.free(self.raw);
+        self.internal.deinit(self.allocator);
+    }
+};
 
 pub const HttpResponse = struct {
     memory: Memory,
     status: u16,
+    responseHeaders: Memory,
 
     /// IMPORTANT: it's the caller's responsibility to free the returned string
     pub fn body(self: HttpResponse, allocator: std.mem.Allocator) ![]u8 {
@@ -15,11 +57,27 @@ pub const HttpResponse = struct {
 
     pub fn deinit(self: HttpResponse) void {
         self.memory.free();
+        self.responseHeaders.free();
     }
 
     pub fn statusCode(self: HttpResponse) u16 {
         return self.status;
     }
+
+    /// IMPORTANT: it's the caller's responsibility to `deinit` the Headers if returned.
+    pub fn headers(self: HttpResponse, allocator: std.mem.Allocator) !Headers {
+        const data = try self.responseHeaders.loadAlloc(allocator);
+        errdefer allocator.free(data);
+
+        const j = try std.json.parseFromSlice(std.json.ArrayHashMap([]const u8), allocator, data, .{ .allocate = .alloc_always, .ignore_unknown_fields = true });
+        defer j.deinit();
+
+        return Headers{
+            .allocator = allocator,
+            .raw = data,
+            .internal = j.value,
+        };
+    }
 };
 
 pub const HttpRequest = struct {
diff --git a/src/main.zig b/src/main.zig
index e56c2ff..0e7c430 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -143,6 +143,7 @@ pub const Plugin = struct {
 
     pub fn logMemory(self: Plugin, level: LogLevel, memory: Memory) void {
         _ = self; // to make the interface consistent
+
         switch (level) {
             .Trace => extism.log_trace(memory.offset),
             .Debug => extism.log_debug(memory.offset),
@@ -224,10 +225,15 @@ pub const Plugin = struct {
         const length = extism.length_unsafe(offset);
         const status: u16 = @intCast(extism.http_status_code());
 
+        const headersOffset = extism.http_headers();
+        const headersLength = extism.length_unsafe(headersOffset);
+        const headersMem = Memory.init(headersOffset, headersLength);
+
         const mem = Memory.init(offset, length);
         return http.HttpResponse{
             .memory = mem,
             .status = status,
+            .responseHeaders = headersMem,
         };
     }
 };