diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index da9683f9..757fb6bf 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -94,7 +94,9 @@ jobs:
 
     - name: Format (rules_rust)
       run: |
+        sed -i'' -E 's/^default = \[\]/default = \[\"header-value\"\]/' Cargo.toml
         bazelisk --noworkspace_rc run --noenable_bzlmod //bazel/cargo:crates_vendor
+        git checkout Cargo.toml
         git diff --exit-code
 
   msrv:
@@ -148,6 +150,12 @@ jobs:
     - name: Clippy (wasm32-wasi)
       run: cargo clippy --release --all-targets --target=wasm32-wasi
 
+    - name: Build (header-value)
+      run: cargo build --release --all-targets --target=wasm32-wasi --features header-value
+
+    - name: Clippy (header-value)
+      run: cargo clippy --release --all-targets --target=wasm32-wasi --features header-value
+
     - name: Format (rustfmt)
       run: cargo fmt -- --check
 
@@ -210,6 +218,12 @@ jobs:
     - name: Clippy (wasm32-wasip1)
       run: cargo clippy --release --all-targets --target=wasm32-wasip1
 
+    - name: Build (header-value)
+      run: cargo build --release --all-targets --target=wasm32-wasip1 --features header-value
+
+    - name: Clippy (header-value)
+      run: cargo clippy --release --all-targets --target=wasm32-wasip1 --features header-value
+
     - name: Format (rustfmt)
       run: cargo fmt -- --check
 
@@ -273,6 +287,12 @@ jobs:
     - name: Clippy (wasm32-wasip1)
       run: cargo clippy --release --all-targets --target=wasm32-wasip1
 
+    - name: Build (header-value)
+      run: cargo build --release --all-targets --target=wasm32-wasip1 --features header-value
+
+    - name: Clippy (header-value)
+      run: cargo clippy --release --all-targets --target=wasm32-wasip1 --features header-value
+
     - name: Format (rustfmt)
       run: cargo fmt -- --check
 
diff --git a/BUILD b/BUILD
index 7f611e4f..eb96231e 100644
--- a/BUILD
+++ b/BUILD
@@ -39,6 +39,22 @@ rust_library(
     ],
 )
 
+rust_library(
+    name = "proxy_wasm_header_value",
+    srcs = glob(["src/*.rs"]),
+    crate_features = ["header-value"],
+    crate_name = "proxy_wasm",
+    edition = "2018",
+    visibility = ["//visibility:public"],
+    deps = [
+        ":proxy_wasm_build_script",
+        "//bazel/cargo/remote:bytes",
+        "//bazel/cargo/remote:hashbrown",
+        "//bazel/cargo/remote:http",
+        "//bazel/cargo/remote:log",
+    ],
+)
+
 rust_binary(
     name = "http_auth_random",
     srcs = ["examples/http_auth_random/src/lib.rs"],
@@ -52,3 +68,17 @@ rust_binary(
         "//bazel/cargo/remote:log",
     ],
 )
+
+rust_binary(
+    name = "grpc_auth_random",
+    srcs = ["examples/grpc_auth_random/src/lib.rs"],
+    crate_type = "cdylib",
+    edition = "2018",
+    out_binary = True,
+    rustc_flags = ["-Cstrip=debuginfo"],
+    visibility = ["//visibility:private"],
+    deps = [
+        ":proxy_wasm_header_value",
+        "//bazel/cargo/remote:log",
+    ],
+)
diff --git a/Cargo.toml b/Cargo.toml
index 24193eb8..fd322592 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,9 +11,15 @@ edition = "2018"
 build = "build.rs"
 
 [dependencies]
+bytes = { version = "1", optional = true }
 hashbrown = "0.15"
+http = { version = "1", optional = true }
 log = "0.4"
 
+[features]
+default = []
+header-value = ["dep:bytes", "dep:http"]
+
 [profile.release]
 lto = true
 opt-level = 3
diff --git a/README.md b/README.md
index 7db3cdc2..404499d8 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,13 @@
 [license-badge]: https://img.shields.io/github/license/proxy-wasm/proxy-wasm-rust-sdk
 [license-link]: https://github.com/proxy-wasm/proxy-wasm-rust-sdk/blob/main/LICENSE
 
+## Crate features
+
+This crate supports the following optional features:
+
+- `header-value` - uses RFC-compliant `HeaderValue` instead of UTF-8 `String` for HTTP header and trailer values.
+  This will become the default in future releases.
+
 ## Examples
 
 - [Hello World](./examples/hello_world/)
diff --git a/bazel/cargo/Cargo.Bazel.lock b/bazel/cargo/Cargo.Bazel.lock
index f4d451ea..3a373e6d 100644
--- a/bazel/cargo/Cargo.Bazel.lock
+++ b/bazel/cargo/Cargo.Bazel.lock
@@ -8,12 +8,24 @@ version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
 
+[[package]]
+name = "bytes"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
+
 [[package]]
 name = "equivalent"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
 [[package]]
 name = "foldhash"
 version = "0.1.3"
@@ -31,6 +43,23 @@ dependencies = [
  "foldhash",
 ]
 
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
 [[package]]
 name = "log"
 version = "0.4.22"
@@ -41,6 +70,8 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 name = "proxy-wasm"
 version = "0.2.3-dev"
 dependencies = [
+ "bytes",
  "hashbrown",
+ "http",
  "log",
 ]
diff --git a/bazel/cargo/remote/BUILD.bazel b/bazel/cargo/remote/BUILD.bazel
index f62c1da8..46e22fdb 100644
--- a/bazel/cargo/remote/BUILD.bazel
+++ b/bazel/cargo/remote/BUILD.bazel
@@ -31,12 +31,24 @@ filegroup(
 )
 
 # Workspace Member Dependencies
+alias(
+    name = "bytes",
+    actual = "@crates_vendor__bytes-1.8.0//:bytes",
+    tags = ["manual"],
+)
+
 alias(
     name = "hashbrown",
     actual = "@crates_vendor__hashbrown-0.15.0//:hashbrown",
     tags = ["manual"],
 )
 
+alias(
+    name = "http",
+    actual = "@crates_vendor__http-1.1.0//:http",
+    tags = ["manual"],
+)
+
 alias(
     name = "log",
     actual = "@crates_vendor__log-0.4.22//:log",
diff --git a/bazel/cargo/remote/BUILD.bytes-1.8.0.bazel b/bazel/cargo/remote/BUILD.bytes-1.8.0.bazel
new file mode 100644
index 00000000..efac2269
--- /dev/null
+++ b/bazel/cargo/remote/BUILD.bytes-1.8.0.bazel
@@ -0,0 +1,85 @@
+###############################################################################
+# @generated
+# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To
+# regenerate this file, run the following:
+#
+#     bazel run @//bazel/cargo:crates_vendor
+###############################################################################
+
+load("@rules_rust//rust:defs.bzl", "rust_library")
+
+package(default_visibility = ["//visibility:public"])
+
+rust_library(
+    name = "bytes",
+    srcs = glob(
+        include = ["**/*.rs"],
+        allow_empty = True,
+    ),
+    compile_data = glob(
+        include = ["**"],
+        allow_empty = True,
+        exclude = [
+            "**/* *",
+            ".tmp_git_root/**/*",
+            "BUILD",
+            "BUILD.bazel",
+            "WORKSPACE",
+            "WORKSPACE.bazel",
+        ],
+    ),
+    crate_features = [
+        "default",
+        "std",
+    ],
+    crate_root = "src/lib.rs",
+    edition = "2018",
+    rustc_flags = [
+        "--cap-lints=allow",
+    ],
+    tags = [
+        "cargo-bazel",
+        "crate-name=bytes",
+        "manual",
+        "noclippy",
+        "norustfmt",
+    ],
+    target_compatible_with = select({
+        "@rules_rust//rust/platform:aarch64-apple-darwin": [],
+        "@rules_rust//rust/platform:aarch64-apple-ios": [],
+        "@rules_rust//rust/platform:aarch64-apple-ios-sim": [],
+        "@rules_rust//rust/platform:aarch64-fuchsia": [],
+        "@rules_rust//rust/platform:aarch64-linux-android": [],
+        "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [],
+        "@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [],
+        "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [],
+        "@rules_rust//rust/platform:armv7-linux-androideabi": [],
+        "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [],
+        "@rules_rust//rust/platform:i686-apple-darwin": [],
+        "@rules_rust//rust/platform:i686-linux-android": [],
+        "@rules_rust//rust/platform:i686-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:i686-unknown-freebsd": [],
+        "@rules_rust//rust/platform:i686-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [],
+        "@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [],
+        "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:thumbv7em-none-eabi": [],
+        "@rules_rust//rust/platform:thumbv8m.main-none-eabi": [],
+        "@rules_rust//rust/platform:wasm32-unknown-unknown": [],
+        "@rules_rust//rust/platform:wasm32-wasi": [],
+        "@rules_rust//rust/platform:x86_64-apple-darwin": [],
+        "@rules_rust//rust/platform:x86_64-apple-ios": [],
+        "@rules_rust//rust/platform:x86_64-fuchsia": [],
+        "@rules_rust//rust/platform:x86_64-linux-android": [],
+        "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:x86_64-unknown-freebsd": [],
+        "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [],
+        "@rules_rust//rust/platform:x86_64-unknown-none": [],
+        "//conditions:default": ["@platforms//:incompatible"],
+    }),
+    version = "1.8.0",
+)
diff --git a/bazel/cargo/remote/BUILD.fnv-1.0.7.bazel b/bazel/cargo/remote/BUILD.fnv-1.0.7.bazel
new file mode 100644
index 00000000..39a1f179
--- /dev/null
+++ b/bazel/cargo/remote/BUILD.fnv-1.0.7.bazel
@@ -0,0 +1,85 @@
+###############################################################################
+# @generated
+# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To
+# regenerate this file, run the following:
+#
+#     bazel run @//bazel/cargo:crates_vendor
+###############################################################################
+
+load("@rules_rust//rust:defs.bzl", "rust_library")
+
+package(default_visibility = ["//visibility:public"])
+
+rust_library(
+    name = "fnv",
+    srcs = glob(
+        include = ["**/*.rs"],
+        allow_empty = True,
+    ),
+    compile_data = glob(
+        include = ["**"],
+        allow_empty = True,
+        exclude = [
+            "**/* *",
+            ".tmp_git_root/**/*",
+            "BUILD",
+            "BUILD.bazel",
+            "WORKSPACE",
+            "WORKSPACE.bazel",
+        ],
+    ),
+    crate_features = [
+        "default",
+        "std",
+    ],
+    crate_root = "lib.rs",
+    edition = "2015",
+    rustc_flags = [
+        "--cap-lints=allow",
+    ],
+    tags = [
+        "cargo-bazel",
+        "crate-name=fnv",
+        "manual",
+        "noclippy",
+        "norustfmt",
+    ],
+    target_compatible_with = select({
+        "@rules_rust//rust/platform:aarch64-apple-darwin": [],
+        "@rules_rust//rust/platform:aarch64-apple-ios": [],
+        "@rules_rust//rust/platform:aarch64-apple-ios-sim": [],
+        "@rules_rust//rust/platform:aarch64-fuchsia": [],
+        "@rules_rust//rust/platform:aarch64-linux-android": [],
+        "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [],
+        "@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [],
+        "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [],
+        "@rules_rust//rust/platform:armv7-linux-androideabi": [],
+        "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [],
+        "@rules_rust//rust/platform:i686-apple-darwin": [],
+        "@rules_rust//rust/platform:i686-linux-android": [],
+        "@rules_rust//rust/platform:i686-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:i686-unknown-freebsd": [],
+        "@rules_rust//rust/platform:i686-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [],
+        "@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [],
+        "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:thumbv7em-none-eabi": [],
+        "@rules_rust//rust/platform:thumbv8m.main-none-eabi": [],
+        "@rules_rust//rust/platform:wasm32-unknown-unknown": [],
+        "@rules_rust//rust/platform:wasm32-wasi": [],
+        "@rules_rust//rust/platform:x86_64-apple-darwin": [],
+        "@rules_rust//rust/platform:x86_64-apple-ios": [],
+        "@rules_rust//rust/platform:x86_64-fuchsia": [],
+        "@rules_rust//rust/platform:x86_64-linux-android": [],
+        "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:x86_64-unknown-freebsd": [],
+        "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [],
+        "@rules_rust//rust/platform:x86_64-unknown-none": [],
+        "//conditions:default": ["@platforms//:incompatible"],
+    }),
+    version = "1.0.7",
+)
diff --git a/bazel/cargo/remote/BUILD.http-1.1.0.bazel b/bazel/cargo/remote/BUILD.http-1.1.0.bazel
new file mode 100644
index 00000000..51969aca
--- /dev/null
+++ b/bazel/cargo/remote/BUILD.http-1.1.0.bazel
@@ -0,0 +1,90 @@
+###############################################################################
+# @generated
+# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To
+# regenerate this file, run the following:
+#
+#     bazel run @//bazel/cargo:crates_vendor
+###############################################################################
+
+load("@rules_rust//rust:defs.bzl", "rust_library")
+
+package(default_visibility = ["//visibility:public"])
+
+rust_library(
+    name = "http",
+    srcs = glob(
+        include = ["**/*.rs"],
+        allow_empty = True,
+    ),
+    compile_data = glob(
+        include = ["**"],
+        allow_empty = True,
+        exclude = [
+            "**/* *",
+            ".tmp_git_root/**/*",
+            "BUILD",
+            "BUILD.bazel",
+            "WORKSPACE",
+            "WORKSPACE.bazel",
+        ],
+    ),
+    crate_features = [
+        "default",
+        "std",
+    ],
+    crate_root = "src/lib.rs",
+    edition = "2018",
+    rustc_flags = [
+        "--cap-lints=allow",
+    ],
+    tags = [
+        "cargo-bazel",
+        "crate-name=http",
+        "manual",
+        "noclippy",
+        "norustfmt",
+    ],
+    target_compatible_with = select({
+        "@rules_rust//rust/platform:aarch64-apple-darwin": [],
+        "@rules_rust//rust/platform:aarch64-apple-ios": [],
+        "@rules_rust//rust/platform:aarch64-apple-ios-sim": [],
+        "@rules_rust//rust/platform:aarch64-fuchsia": [],
+        "@rules_rust//rust/platform:aarch64-linux-android": [],
+        "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [],
+        "@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [],
+        "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [],
+        "@rules_rust//rust/platform:armv7-linux-androideabi": [],
+        "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [],
+        "@rules_rust//rust/platform:i686-apple-darwin": [],
+        "@rules_rust//rust/platform:i686-linux-android": [],
+        "@rules_rust//rust/platform:i686-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:i686-unknown-freebsd": [],
+        "@rules_rust//rust/platform:i686-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [],
+        "@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [],
+        "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:thumbv7em-none-eabi": [],
+        "@rules_rust//rust/platform:thumbv8m.main-none-eabi": [],
+        "@rules_rust//rust/platform:wasm32-unknown-unknown": [],
+        "@rules_rust//rust/platform:wasm32-wasi": [],
+        "@rules_rust//rust/platform:x86_64-apple-darwin": [],
+        "@rules_rust//rust/platform:x86_64-apple-ios": [],
+        "@rules_rust//rust/platform:x86_64-fuchsia": [],
+        "@rules_rust//rust/platform:x86_64-linux-android": [],
+        "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:x86_64-unknown-freebsd": [],
+        "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [],
+        "@rules_rust//rust/platform:x86_64-unknown-none": [],
+        "//conditions:default": ["@platforms//:incompatible"],
+    }),
+    version = "1.1.0",
+    deps = [
+        "@crates_vendor__bytes-1.8.0//:bytes",
+        "@crates_vendor__fnv-1.0.7//:fnv",
+        "@crates_vendor__itoa-1.0.11//:itoa",
+    ],
+)
diff --git a/bazel/cargo/remote/BUILD.itoa-1.0.11.bazel b/bazel/cargo/remote/BUILD.itoa-1.0.11.bazel
new file mode 100644
index 00000000..d8c549e8
--- /dev/null
+++ b/bazel/cargo/remote/BUILD.itoa-1.0.11.bazel
@@ -0,0 +1,81 @@
+###############################################################################
+# @generated
+# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To
+# regenerate this file, run the following:
+#
+#     bazel run @//bazel/cargo:crates_vendor
+###############################################################################
+
+load("@rules_rust//rust:defs.bzl", "rust_library")
+
+package(default_visibility = ["//visibility:public"])
+
+rust_library(
+    name = "itoa",
+    srcs = glob(
+        include = ["**/*.rs"],
+        allow_empty = True,
+    ),
+    compile_data = glob(
+        include = ["**"],
+        allow_empty = True,
+        exclude = [
+            "**/* *",
+            ".tmp_git_root/**/*",
+            "BUILD",
+            "BUILD.bazel",
+            "WORKSPACE",
+            "WORKSPACE.bazel",
+        ],
+    ),
+    crate_root = "src/lib.rs",
+    edition = "2018",
+    rustc_flags = [
+        "--cap-lints=allow",
+    ],
+    tags = [
+        "cargo-bazel",
+        "crate-name=itoa",
+        "manual",
+        "noclippy",
+        "norustfmt",
+    ],
+    target_compatible_with = select({
+        "@rules_rust//rust/platform:aarch64-apple-darwin": [],
+        "@rules_rust//rust/platform:aarch64-apple-ios": [],
+        "@rules_rust//rust/platform:aarch64-apple-ios-sim": [],
+        "@rules_rust//rust/platform:aarch64-fuchsia": [],
+        "@rules_rust//rust/platform:aarch64-linux-android": [],
+        "@rules_rust//rust/platform:aarch64-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [],
+        "@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [],
+        "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [],
+        "@rules_rust//rust/platform:armv7-linux-androideabi": [],
+        "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [],
+        "@rules_rust//rust/platform:i686-apple-darwin": [],
+        "@rules_rust//rust/platform:i686-linux-android": [],
+        "@rules_rust//rust/platform:i686-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:i686-unknown-freebsd": [],
+        "@rules_rust//rust/platform:i686-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [],
+        "@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [],
+        "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:thumbv7em-none-eabi": [],
+        "@rules_rust//rust/platform:thumbv8m.main-none-eabi": [],
+        "@rules_rust//rust/platform:wasm32-unknown-unknown": [],
+        "@rules_rust//rust/platform:wasm32-wasi": [],
+        "@rules_rust//rust/platform:x86_64-apple-darwin": [],
+        "@rules_rust//rust/platform:x86_64-apple-ios": [],
+        "@rules_rust//rust/platform:x86_64-fuchsia": [],
+        "@rules_rust//rust/platform:x86_64-linux-android": [],
+        "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [],
+        "@rules_rust//rust/platform:x86_64-unknown-freebsd": [],
+        "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [],
+        "@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [],
+        "@rules_rust//rust/platform:x86_64-unknown-none": [],
+        "//conditions:default": ["@platforms//:incompatible"],
+    }),
+    version = "1.0.11",
+)
diff --git a/bazel/cargo/remote/defs.bzl b/bazel/cargo/remote/defs.bzl
index e4051261..97ddadb4 100644
--- a/bazel/cargo/remote/defs.bzl
+++ b/bazel/cargo/remote/defs.bzl
@@ -295,7 +295,9 @@ def aliases(
 _NORMAL_DEPENDENCIES = {
     "": {
         _COMMON_CONDITION: {
+            "bytes": Label("@crates_vendor__bytes-1.8.0//:bytes"),
             "hashbrown": Label("@crates_vendor__hashbrown-0.15.0//:hashbrown"),
+            "http": Label("@crates_vendor__http-1.1.0//:http"),
             "log": Label("@crates_vendor__log-0.4.22//:log"),
         },
     },
@@ -413,6 +415,16 @@ def crate_repositories():
         build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.allocator-api2-0.2.18.bazel"),
     )
 
+    maybe(
+        http_archive,
+        name = "crates_vendor__bytes-1.8.0",
+        sha256 = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da",
+        type = "tar.gz",
+        urls = ["https://static.crates.io/crates/bytes/1.8.0/download"],
+        strip_prefix = "bytes-1.8.0",
+        build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.bytes-1.8.0.bazel"),
+    )
+
     maybe(
         http_archive,
         name = "crates_vendor__equivalent-1.0.1",
@@ -423,6 +435,16 @@ def crate_repositories():
         build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.equivalent-1.0.1.bazel"),
     )
 
+    maybe(
+        http_archive,
+        name = "crates_vendor__fnv-1.0.7",
+        sha256 = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1",
+        type = "tar.gz",
+        urls = ["https://static.crates.io/crates/fnv/1.0.7/download"],
+        strip_prefix = "fnv-1.0.7",
+        build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.fnv-1.0.7.bazel"),
+    )
+
     maybe(
         http_archive,
         name = "crates_vendor__foldhash-0.1.3",
@@ -443,6 +465,26 @@ def crate_repositories():
         build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.hashbrown-0.15.0.bazel"),
     )
 
+    maybe(
+        http_archive,
+        name = "crates_vendor__http-1.1.0",
+        sha256 = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258",
+        type = "tar.gz",
+        urls = ["https://static.crates.io/crates/http/1.1.0/download"],
+        strip_prefix = "http-1.1.0",
+        build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.http-1.1.0.bazel"),
+    )
+
+    maybe(
+        http_archive,
+        name = "crates_vendor__itoa-1.0.11",
+        sha256 = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b",
+        type = "tar.gz",
+        urls = ["https://static.crates.io/crates/itoa/1.0.11/download"],
+        strip_prefix = "itoa-1.0.11",
+        build_file = Label("@proxy_wasm_rust_sdk//bazel/cargo/remote:BUILD.itoa-1.0.11.bazel"),
+    )
+
     maybe(
         http_archive,
         name = "crates_vendor__log-0.4.22",
@@ -454,6 +496,8 @@ def crate_repositories():
     )
 
     return [
+        struct(repo = "crates_vendor__bytes-1.8.0", is_dev_dep = False),
         struct(repo = "crates_vendor__hashbrown-0.15.0", is_dev_dep = False),
+        struct(repo = "crates_vendor__http-1.1.0", is_dev_dep = False),
         struct(repo = "crates_vendor__log-0.4.22", is_dev_dep = False),
     ]
diff --git a/examples/grpc_auth_random/Cargo.toml b/examples/grpc_auth_random/Cargo.toml
index c3e6ec01..3e5b3be6 100644
--- a/examples/grpc_auth_random/Cargo.toml
+++ b/examples/grpc_auth_random/Cargo.toml
@@ -11,7 +11,7 @@ crate-type = ["cdylib"]
 
 [dependencies]
 log = "0.4"
-proxy-wasm = { path = "../../" }
+proxy-wasm = { path = "../../", features = ["header-value"] }
 
 [profile.release]
 lto = true
diff --git a/examples/grpc_auth_random/src/lib.rs b/examples/grpc_auth_random/src/lib.rs
index d1c589e9..11df530e 100644
--- a/examples/grpc_auth_random/src/lib.rs
+++ b/examples/grpc_auth_random/src/lib.rs
@@ -27,12 +27,12 @@ struct GrpcAuthRandom;
 impl HttpContext for GrpcAuthRandom {
     fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
         match self.get_http_request_header("content-type") {
-            Some(value) if value.starts_with("application/grpc") => {}
+            Some(value) if value.as_bytes().starts_with(b"application/grpc") => {}
             _ => {
                 // Reject non-gRPC clients.
                 self.send_http_response(
                     503,
-                    vec![("Powered-By", "proxy-wasm")],
+                    vec![("Powered-By", &HeaderValue::from_static("proxy-wasm"))],
                     Some(b"Service accessible only to gRPC clients.\n"),
                 );
                 return Action::Pause;
@@ -40,7 +40,7 @@ impl HttpContext for GrpcAuthRandom {
         }
 
         match self.get_http_request_header(":path") {
-            Some(value) if value.starts_with("/grpc.reflection") => {
+            Some(value) if value.as_bytes().starts_with(b"/grpc.reflection") => {
                 // Always allow gRPC calls to the reflection API.
                 Action::Continue
             }
@@ -61,7 +61,7 @@ impl HttpContext for GrpcAuthRandom {
     }
 
     fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action {
-        self.set_http_response_header("Powered-By", Some("proxy-wasm"));
+        self.set_http_response_header("Powered-By", Some(&HeaderValue::from_static("proxy-wasm")));
         Action::Continue
     }
 }
diff --git a/examples/http_headers/Cargo.toml b/examples/http_headers/Cargo.toml
index 02afd242..2fb8fa6a 100644
--- a/examples/http_headers/Cargo.toml
+++ b/examples/http_headers/Cargo.toml
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
 
 [dependencies]
 log = "0.4"
-proxy-wasm = { path = "../../" }
+proxy-wasm = { path = "../../", features = ["header-value"] }
 
 [profile.release]
 lto = true
diff --git a/examples/http_headers/src/lib.rs b/examples/http_headers/src/lib.rs
index 315a7b88..a6a4942b 100644
--- a/examples/http_headers/src/lib.rs
+++ b/examples/http_headers/src/lib.rs
@@ -44,14 +44,22 @@ impl Context for HttpHeaders {}
 impl HttpContext for HttpHeaders {
     fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
         for (name, value) in &self.get_http_request_headers() {
-            info!("#{} -> {}: {}", self.context_id, name, value);
+            info!(
+                "#{} -> {}: {}",
+                self.context_id,
+                name,
+                value.to_str().unwrap_or("<non-printable>")
+            );
         }
 
         match self.get_http_request_header(":path") {
             Some(path) if path == "/hello" => {
                 self.send_http_response(
                     200,
-                    vec![("Hello", "World"), ("Powered-By", "proxy-wasm")],
+                    vec![
+                        ("Hello", &HeaderValue::from_static("World")),
+                        ("Powered-By", &HeaderValue::from_static("proxy-wasm")),
+                    ],
                     Some(b"Hello, World!\n"),
                 );
                 Action::Pause
@@ -62,7 +70,12 @@ impl HttpContext for HttpHeaders {
 
     fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action {
         for (name, value) in &self.get_http_response_headers() {
-            info!("#{} <- {}: {}", self.context_id, name, value);
+            info!(
+                "#{} <- {}: {}",
+                self.context_id,
+                name,
+                value.to_str().unwrap_or("<non-printable>")
+            );
         }
         Action::Continue
     }
diff --git a/src/hostcalls.rs b/src/hostcalls.rs
index 15687888..630bc3e4 100644
--- a/src/hostcalls.rs
+++ b/src/hostcalls.rs
@@ -145,6 +145,27 @@ extern "C" {
     ) -> Status;
 }
 
+#[cfg(feature = "header-value")]
+pub fn get_map(map_type: MapType) -> Result<Vec<(String, HeaderValue)>, Status> {
+    unsafe {
+        let mut return_data: *mut u8 = null_mut();
+        let mut return_size: usize = 0;
+        match proxy_get_header_map_pairs(map_type, &mut return_data, &mut return_size) {
+            Status::Ok => {
+                if !return_data.is_null() {
+                    let serialized_map =
+                        bytes::Bytes::from(std::slice::from_raw_parts(return_data, return_size));
+                    Ok(utils::deserialize_map(serialized_map))
+                } else {
+                    Ok(Vec::new())
+                }
+            }
+            status => panic!("unexpected status: {}", status as u32),
+        }
+    }
+}
+
+#[cfg(not(feature = "header-value"))]
 pub fn get_map(map_type: MapType) -> Result<Vec<(String, String)>, Status> {
     unsafe {
         let mut return_data: *mut u8 = null_mut();
@@ -189,7 +210,10 @@ extern "C" {
     ) -> Status;
 }
 
-pub fn set_map(map_type: MapType, map: Vec<(&str, &str)>) -> Result<(), Status> {
+pub fn set_map<V>(map_type: MapType, map: Vec<(&str, V)>) -> Result<(), Status>
+where
+    V: AsRef<[u8]>,
+{
     let serialized_map = utils::serialize_map(map);
     unsafe {
         match proxy_set_header_map_pairs(map_type, serialized_map.as_ptr(), serialized_map.len()) {
@@ -200,13 +224,7 @@ pub fn set_map(map_type: MapType, map: Vec<(&str, &str)>) -> Result<(), Status>
 }
 
 pub fn set_map_bytes(map_type: MapType, map: Vec<(&str, &[u8])>) -> Result<(), Status> {
-    let serialized_map = utils::serialize_map_bytes(map);
-    unsafe {
-        match proxy_set_header_map_pairs(map_type, serialized_map.as_ptr(), serialized_map.len()) {
-            Status::Ok => Ok(()),
-            status => panic!("unexpected status: {}", status as u32),
-        }
-    }
+    set_map(map_type, map)
 }
 
 extern "C" {
@@ -219,6 +237,38 @@ extern "C" {
     ) -> Status;
 }
 
+#[cfg(feature = "header-value")]
+pub fn get_map_value(map_type: MapType, key: &str) -> Result<Option<HeaderValue>, Status> {
+    let mut return_data: *mut u8 = null_mut();
+    let mut return_size: usize = 0;
+    unsafe {
+        match proxy_get_header_map_value(
+            map_type,
+            key.as_ptr(),
+            key.len(),
+            &mut return_data,
+            &mut return_size,
+        ) {
+            Status::Ok => {
+                if !return_data.is_null() {
+                    match HeaderValue::from_bytes(std::slice::from_raw_parts(
+                        return_data,
+                        return_size,
+                    )) {
+                        Ok(value) => Ok(Some(value)),
+                        Err(_) => panic!("illegal field value in: {}", key),
+                    }
+                } else {
+                    Ok(None)
+                }
+            }
+            Status::NotFound => Ok(None),
+            status => panic!("unexpected status: {}", status as u32),
+        }
+    }
+}
+
+#[cfg(not(feature = "header-value"))]
 pub fn get_map_value(map_type: MapType, key: &str) -> Result<Option<String>, Status> {
     let mut return_data: *mut u8 = null_mut();
     let mut return_size: usize = 0;
@@ -296,15 +346,18 @@ extern "C" {
     ) -> Status;
 }
 
-pub fn set_map_value(map_type: MapType, key: &str, value: Option<&str>) -> Result<(), Status> {
+pub fn set_map_value<V>(map_type: MapType, key: &str, value: Option<V>) -> Result<(), Status>
+where
+    V: AsRef<[u8]>,
+{
     unsafe {
         if let Some(value) = value {
             match proxy_replace_header_map_value(
                 map_type,
                 key.as_ptr(),
                 key.len(),
-                value.as_ptr(),
-                value.len(),
+                value.as_ref().as_ptr(),
+                value.as_ref().len(),
             ) {
                 Status::Ok => Ok(()),
                 status => panic!("unexpected status: {}", status as u32),
@@ -323,25 +376,7 @@ pub fn set_map_value_bytes(
     key: &str,
     value: Option<&[u8]>,
 ) -> Result<(), Status> {
-    unsafe {
-        if let Some(value) = value {
-            match proxy_replace_header_map_value(
-                map_type,
-                key.as_ptr(),
-                key.len(),
-                value.as_ptr(),
-                value.len(),
-            ) {
-                Status::Ok => Ok(()),
-                status => panic!("unexpected status: {}", status as u32),
-            }
-        } else {
-            match proxy_remove_header_map_value(map_type, key.as_ptr(), key.len()) {
-                Status::Ok => Ok(()),
-                status => panic!("unexpected status: {}", status as u32),
-            }
-        }
-    }
+    set_map_value(map_type, key, value)
 }
 
 extern "C" {
@@ -354,14 +389,17 @@ extern "C" {
     ) -> Status;
 }
 
-pub fn add_map_value(map_type: MapType, key: &str, value: &str) -> Result<(), Status> {
+pub fn add_map_value<V>(map_type: MapType, key: &str, value: V) -> Result<(), Status>
+where
+    V: AsRef<[u8]>,
+{
     unsafe {
         match proxy_add_header_map_value(
             map_type,
             key.as_ptr(),
             key.len(),
-            value.as_ptr(),
-            value.len(),
+            value.as_ref().as_ptr(),
+            value.as_ref().len(),
         ) {
             Status::Ok => Ok(()),
             status => panic!("unexpected status: {}", status as u32),
@@ -370,18 +408,7 @@ pub fn add_map_value(map_type: MapType, key: &str, value: &str) -> Result<(), St
 }
 
 pub fn add_map_value_bytes(map_type: MapType, key: &str, value: &[u8]) -> Result<(), Status> {
-    unsafe {
-        match proxy_add_header_map_value(
-            map_type,
-            key.as_ptr(),
-            key.len(),
-            value.as_ptr(),
-            value.len(),
-        ) {
-            Status::Ok => Ok(()),
-            status => panic!("unexpected status: {}", status as u32),
-        }
-    }
+    add_map_value(map_type, key, value)
 }
 
 extern "C" {
@@ -705,11 +732,14 @@ extern "C" {
     ) -> Status;
 }
 
-pub fn send_http_response(
+pub fn send_http_response<V>(
     status_code: u32,
-    headers: Vec<(&str, &str)>,
+    headers: Vec<(&str, V)>,
     body: Option<&[u8]>,
-) -> Result<(), Status> {
+) -> Result<(), Status>
+where
+    V: AsRef<[u8]>,
+{
     let serialized_headers = utils::serialize_map(headers);
     unsafe {
         match proxy_send_local_response(
@@ -766,13 +796,16 @@ extern "C" {
     ) -> Status;
 }
 
-pub fn dispatch_http_call(
+pub fn dispatch_http_call<V>(
     upstream: &str,
-    headers: Vec<(&str, &str)>,
+    headers: Vec<(&str, V)>,
     body: Option<&[u8]>,
-    trailers: Vec<(&str, &str)>,
+    trailers: Vec<(&str, V)>,
     timeout: Duration,
-) -> Result<u32, Status> {
+) -> Result<u32, Status>
+where
+    V: AsRef<[u8]>,
+{
     let serialized_headers = utils::serialize_map(headers);
     let serialized_trailers = utils::serialize_map(trailers);
     let mut return_token: u32 = 0;
@@ -1140,6 +1173,10 @@ pub fn increment_metric(metric_id: u32, offset: i64) -> Result<(), Status> {
 
 mod utils {
     use crate::types::Bytes;
+    #[cfg(feature = "header-value")]
+    use crate::types::HeaderValue;
+    #[cfg(feature = "header-value")]
+    use bytes::Buf;
     use std::convert::TryFrom;
 
     pub(super) fn serialize_property_path(path: Vec<&str>) -> Bytes {
@@ -1159,46 +1196,60 @@ mod utils {
         bytes
     }
 
-    pub(super) fn serialize_map(map: Vec<(&str, &str)>) -> Bytes {
+    pub(super) fn serialize_map<V>(map: Vec<(&str, V)>) -> Bytes
+    where
+        V: AsRef<[u8]>,
+    {
         let mut size: usize = 4;
         for (name, value) in &map {
-            size += name.len() + value.len() + 10;
+            size += name.len() + value.as_ref().len() + 10;
         }
         let mut bytes: Bytes = Vec::with_capacity(size);
         bytes.extend_from_slice(&map.len().to_le_bytes());
         for (name, value) in &map {
             bytes.extend_from_slice(&name.len().to_le_bytes());
-            bytes.extend_from_slice(&value.len().to_le_bytes());
+            bytes.extend_from_slice(&value.as_ref().len().to_le_bytes());
         }
         for (name, value) in &map {
             bytes.extend_from_slice(name.as_bytes());
             bytes.push(0);
-            bytes.extend_from_slice(value.as_bytes());
+            bytes.extend_from_slice(value.as_ref());
             bytes.push(0);
         }
         bytes
     }
 
     pub(super) fn serialize_map_bytes(map: Vec<(&str, &[u8])>) -> Bytes {
-        let mut size: usize = 4;
-        for (name, value) in &map {
-            size += name.len() + value.len() + 10;
-        }
-        let mut bytes: Bytes = Vec::with_capacity(size);
-        bytes.extend_from_slice(&map.len().to_le_bytes());
-        for (name, value) in &map {
-            bytes.extend_from_slice(&name.len().to_le_bytes());
-            bytes.extend_from_slice(&value.len().to_le_bytes());
+        serialize_map(map)
+    }
+
+    #[cfg(feature = "header-value")]
+    pub(super) fn deserialize_map(mut bytes: bytes::Bytes) -> Vec<(String, HeaderValue)> {
+        if bytes.is_empty() {
+            return Vec::new();
         }
-        for (name, value) in &map {
-            bytes.extend_from_slice(name.as_bytes());
-            bytes.push(0);
-            bytes.extend_from_slice(value);
-            bytes.push(0);
+        let size = bytes.get_u32_le() as usize;
+        let mut sizes = bytes.split_to(size * 8);
+        let mut map = Vec::with_capacity(size);
+        for _ in 0..size {
+            let size = sizes.get_u32_le() as usize;
+            let key = bytes.split_to(size);
+            bytes.advance(1);
+            let size = sizes.get_u32_le() as usize;
+            let value = bytes.split_to(size);
+            bytes.advance(1);
+            map.push((
+                String::from_utf8(key.to_vec()).unwrap(),
+                // We're intentionally using the unchecked variant in order to retain
+                // values accepted by the hosts and proxies that don't enforce strict
+                // RFC compliance on HTTP field values.
+                unsafe { HeaderValue::from_maybe_shared_unchecked(value) },
+            ));
         }
-        bytes
+        map
     }
 
+    #[cfg(not(feature = "header-value"))]
     pub(super) fn deserialize_map(bytes: &[u8]) -> Vec<(String, String)> {
         let mut map = Vec::new();
         if bytes.is_empty() {
diff --git a/src/traits.rs b/src/traits.rs
index bd54bcbe..63a3db97 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -58,6 +58,19 @@ pub trait Context {
         hostcalls::enqueue_shared_queue(queue_id, value)
     }
 
+    #[cfg(feature = "header-value")]
+    fn dispatch_http_call(
+        &self,
+        upstream: &str,
+        headers: Vec<(&str, &HeaderValue)>,
+        body: Option<&[u8]>,
+        trailers: Vec<(&str, &HeaderValue)>,
+        timeout: Duration,
+    ) -> Result<u32, Status> {
+        hostcalls::dispatch_http_call(upstream, headers, body, trailers, timeout)
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn dispatch_http_call(
         &self,
         upstream: &str,
@@ -78,6 +91,12 @@ pub trait Context {
     ) {
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_call_response_headers(&self) -> Vec<(String, HeaderValue)> {
+        hostcalls::get_map(MapType::HttpCallResponseHeaders).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_call_response_headers(&self) -> Vec<(String, String)> {
         hostcalls::get_map(MapType::HttpCallResponseHeaders).unwrap()
     }
@@ -86,6 +105,12 @@ pub trait Context {
         hostcalls::get_map_bytes(MapType::HttpCallResponseHeaders).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_call_response_header(&self, name: &str) -> Option<HeaderValue> {
+        hostcalls::get_map_value(MapType::HttpCallResponseTrailers, name).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_call_response_header(&self, name: &str) -> Option<String> {
         hostcalls::get_map_value(MapType::HttpCallResponseHeaders, name).unwrap()
     }
@@ -98,6 +123,12 @@ pub trait Context {
         hostcalls::get_buffer(BufferType::HttpCallResponseBody, start, max_size).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_call_response_trailers(&self) -> Vec<(String, HeaderValue)> {
+        hostcalls::get_map(MapType::HttpCallResponseTrailers).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_call_response_trailers(&self) -> Vec<(String, String)> {
         hostcalls::get_map(MapType::HttpCallResponseTrailers).unwrap()
     }
@@ -106,6 +137,12 @@ pub trait Context {
         hostcalls::get_map_bytes(MapType::HttpCallResponseTrailers).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_call_response_trailer(&self, name: &str) -> Option<HeaderValue> {
+        hostcalls::get_map_value(MapType::HttpCallResponseTrailers, name).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_call_response_trailer(&self, name: &str) -> Option<String> {
         hostcalls::get_map_value(MapType::HttpCallResponseTrailers, name).unwrap()
     }
@@ -311,6 +348,12 @@ pub trait HttpContext: Context {
         Action::Continue
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_request_headers(&self) -> Vec<(String, HeaderValue)> {
+        hostcalls::get_map(MapType::HttpRequestHeaders).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_request_headers(&self) -> Vec<(String, String)> {
         hostcalls::get_map(MapType::HttpRequestHeaders).unwrap()
     }
@@ -319,6 +362,12 @@ pub trait HttpContext: Context {
         hostcalls::get_map_bytes(MapType::HttpRequestHeaders).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn set_http_request_headers(&self, headers: Vec<(&str, &HeaderValue)>) {
+        hostcalls::set_map(MapType::HttpRequestHeaders, headers).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn set_http_request_headers(&self, headers: Vec<(&str, &str)>) {
         hostcalls::set_map(MapType::HttpRequestHeaders, headers).unwrap()
     }
@@ -327,6 +376,12 @@ pub trait HttpContext: Context {
         hostcalls::set_map_bytes(MapType::HttpRequestHeaders, headers).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_request_header(&self, name: &str) -> Option<HeaderValue> {
+        hostcalls::get_map_value(MapType::HttpRequestHeaders, name).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_request_header(&self, name: &str) -> Option<String> {
         hostcalls::get_map_value(MapType::HttpRequestHeaders, name).unwrap()
     }
@@ -335,6 +390,12 @@ pub trait HttpContext: Context {
         hostcalls::get_map_value_bytes(MapType::HttpRequestHeaders, name).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn set_http_request_header(&self, name: &str, value: Option<&HeaderValue>) {
+        hostcalls::set_map_value(MapType::HttpRequestHeaders, name, value).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn set_http_request_header(&self, name: &str, value: Option<&str>) {
         hostcalls::set_map_value(MapType::HttpRequestHeaders, name, value).unwrap()
     }
@@ -343,6 +404,12 @@ pub trait HttpContext: Context {
         hostcalls::set_map_value_bytes(MapType::HttpRequestHeaders, name, value).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn add_http_request_header(&self, name: &str, value: &HeaderValue) {
+        hostcalls::add_map_value(MapType::HttpRequestHeaders, name, value).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn add_http_request_header(&self, name: &str, value: &str) {
         hostcalls::add_map_value(MapType::HttpRequestHeaders, name, value).unwrap()
     }
@@ -367,6 +434,12 @@ pub trait HttpContext: Context {
         Action::Continue
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_request_trailers(&self) -> Vec<(String, HeaderValue)> {
+        hostcalls::get_map(MapType::HttpRequestTrailers).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_request_trailers(&self) -> Vec<(String, String)> {
         hostcalls::get_map(MapType::HttpRequestTrailers).unwrap()
     }
@@ -375,6 +448,12 @@ pub trait HttpContext: Context {
         hostcalls::get_map_bytes(MapType::HttpRequestTrailers).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn set_http_request_trailers(&self, trailers: Vec<(&str, &HeaderValue)>) {
+        hostcalls::set_map(MapType::HttpRequestTrailers, trailers).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn set_http_request_trailers(&self, trailers: Vec<(&str, &str)>) {
         hostcalls::set_map(MapType::HttpRequestTrailers, trailers).unwrap()
     }
@@ -383,6 +462,12 @@ pub trait HttpContext: Context {
         hostcalls::set_map_bytes(MapType::HttpRequestTrailers, trailers).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_request_trailer(&self, name: &str) -> Option<HeaderValue> {
+        hostcalls::get_map_value(MapType::HttpRequestTrailers, name).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_request_trailer(&self, name: &str) -> Option<String> {
         hostcalls::get_map_value(MapType::HttpRequestTrailers, name).unwrap()
     }
@@ -391,6 +476,12 @@ pub trait HttpContext: Context {
         hostcalls::get_map_value_bytes(MapType::HttpRequestTrailers, name).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn set_http_request_trailer(&self, name: &str, value: Option<&HeaderValue>) {
+        hostcalls::set_map_value(MapType::HttpRequestTrailers, name, value).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn set_http_request_trailer(&self, name: &str, value: Option<&str>) {
         hostcalls::set_map_value(MapType::HttpRequestTrailers, name, value).unwrap()
     }
@@ -399,6 +490,12 @@ pub trait HttpContext: Context {
         hostcalls::set_map_value_bytes(MapType::HttpRequestTrailers, name, value).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn add_http_request_trailer(&self, name: &str, value: &HeaderValue) {
+        hostcalls::add_map_value(MapType::HttpRequestTrailers, name, value).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn add_http_request_trailer(&self, name: &str, value: &str) {
         hostcalls::add_map_value(MapType::HttpRequestTrailers, name, value).unwrap()
     }
@@ -419,6 +516,12 @@ pub trait HttpContext: Context {
         Action::Continue
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_response_headers(&self) -> Vec<(String, HeaderValue)> {
+        hostcalls::get_map(MapType::HttpResponseHeaders).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_response_headers(&self) -> Vec<(String, String)> {
         hostcalls::get_map(MapType::HttpResponseHeaders).unwrap()
     }
@@ -427,6 +530,12 @@ pub trait HttpContext: Context {
         hostcalls::get_map_bytes(MapType::HttpResponseHeaders).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn set_http_response_headers(&self, headers: Vec<(&str, &HeaderValue)>) {
+        hostcalls::set_map(MapType::HttpResponseHeaders, headers).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn set_http_response_headers(&self, headers: Vec<(&str, &str)>) {
         hostcalls::set_map(MapType::HttpResponseHeaders, headers).unwrap()
     }
@@ -435,6 +544,12 @@ pub trait HttpContext: Context {
         hostcalls::set_map_bytes(MapType::HttpResponseHeaders, headers).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_response_header(&self, name: &str) -> Option<HeaderValue> {
+        hostcalls::get_map_value(MapType::HttpResponseHeaders, name).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_response_header(&self, name: &str) -> Option<String> {
         hostcalls::get_map_value(MapType::HttpResponseHeaders, name).unwrap()
     }
@@ -443,6 +558,12 @@ pub trait HttpContext: Context {
         hostcalls::get_map_value_bytes(MapType::HttpResponseHeaders, name).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn set_http_response_header(&self, name: &str, value: Option<&HeaderValue>) {
+        hostcalls::set_map_value(MapType::HttpResponseHeaders, name, value).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn set_http_response_header(&self, name: &str, value: Option<&str>) {
         hostcalls::set_map_value(MapType::HttpResponseHeaders, name, value).unwrap()
     }
@@ -451,6 +572,12 @@ pub trait HttpContext: Context {
         hostcalls::set_map_value_bytes(MapType::HttpResponseHeaders, name, value).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn add_http_response_header(&self, name: &str, value: &HeaderValue) {
+        hostcalls::add_map_value(MapType::HttpResponseHeaders, name, value).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn add_http_response_header(&self, name: &str, value: &str) {
         hostcalls::add_map_value(MapType::HttpResponseHeaders, name, value).unwrap()
     }
@@ -475,6 +602,12 @@ pub trait HttpContext: Context {
         Action::Continue
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_response_trailers(&self) -> Vec<(String, HeaderValue)> {
+        hostcalls::get_map(MapType::HttpResponseTrailers).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_response_trailers(&self) -> Vec<(String, String)> {
         hostcalls::get_map(MapType::HttpResponseTrailers).unwrap()
     }
@@ -483,6 +616,12 @@ pub trait HttpContext: Context {
         hostcalls::get_map_bytes(MapType::HttpResponseTrailers).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn set_http_response_trailers(&self, trailers: Vec<(&str, &HeaderValue)>) {
+        hostcalls::set_map(MapType::HttpResponseTrailers, trailers).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn set_http_response_trailers(&self, trailers: Vec<(&str, &str)>) {
         hostcalls::set_map(MapType::HttpResponseTrailers, trailers).unwrap()
     }
@@ -491,6 +630,12 @@ pub trait HttpContext: Context {
         hostcalls::set_map_bytes(MapType::HttpResponseTrailers, trailers).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn get_http_response_trailer(&self, name: &str) -> Option<HeaderValue> {
+        hostcalls::get_map_value(MapType::HttpResponseTrailers, name).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn get_http_response_trailer(&self, name: &str) -> Option<String> {
         hostcalls::get_map_value(MapType::HttpResponseTrailers, name).unwrap()
     }
@@ -499,6 +644,12 @@ pub trait HttpContext: Context {
         hostcalls::get_map_value_bytes(MapType::HttpResponseTrailers, name).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn set_http_response_trailer(&self, name: &str, value: Option<&HeaderValue>) {
+        hostcalls::set_map_value(MapType::HttpResponseTrailers, name, value).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn set_http_response_trailer(&self, name: &str, value: Option<&str>) {
         hostcalls::set_map_value(MapType::HttpResponseTrailers, name, value).unwrap()
     }
@@ -507,6 +658,12 @@ pub trait HttpContext: Context {
         hostcalls::set_map_value_bytes(MapType::HttpResponseTrailers, name, value).unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn add_http_response_trailer(&self, name: &str, value: &HeaderValue) {
+        hostcalls::add_map_value(MapType::HttpResponseTrailers, name, value).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn add_http_response_trailer(&self, name: &str, value: &str) {
         hostcalls::add_map_value(MapType::HttpResponseTrailers, name, value).unwrap()
     }
@@ -523,6 +680,17 @@ pub trait HttpContext: Context {
         hostcalls::reset_http_response().unwrap()
     }
 
+    #[cfg(feature = "header-value")]
+    fn send_http_response(
+        &self,
+        status_code: u32,
+        headers: Vec<(&str, &HeaderValue)>,
+        body: Option<&[u8]>,
+    ) {
+        hostcalls::send_http_response(status_code, headers, body).unwrap()
+    }
+
+    #[cfg(not(feature = "header-value"))]
     fn send_http_response(
         &self,
         status_code: u32,
diff --git a/src/types.rs b/src/types.rs
index 7407d3ca..36f387e7 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -139,3 +139,6 @@ pub enum GrpcStatusCode {
 }
 
 pub type Bytes = Vec<u8>;
+
+#[cfg(feature = "header-value")]
+pub use http::HeaderValue;