diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.c b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
index 8e2264a60290cb92635200368aab6b65065c160d..7f7dfa5b554b3123b5ff2ed79575aa90865903fa 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
@@ -187,9 +187,23 @@ static void evict_entry(grpc_chttp2_hpack_compressor *c) {
   c->table_elems--;
 }
 
+static bool is_interned(grpc_mdelem elem) {
+  switch (GRPC_MDELEM_STORAGE(elem)) {
+    case GRPC_MDELEM_STORAGE_ALLOCATED:
+    case GRPC_MDELEM_STORAGE_EXTERNAL:
+      return false;
+    case GRPC_MDELEM_STORAGE_INTERNED:
+    case GRPC_MDELEM_STORAGE_STATIC:
+      return true;
+  }
+  GPR_UNREACHABLE_CODE(return false);
+}
+
 /* add an element to the decoder table */
 static void add_elem(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c,
                      grpc_mdelem elem) {
+  GPR_ASSERT(is_interned(elem));
+
   uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem));
   uint32_t value_hash = grpc_slice_hash(GRPC_MDVALUE(elem));
   uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash);
@@ -384,13 +398,6 @@ static uint32_t dynidx(grpc_chttp2_hpack_compressor *c, uint32_t elem_index) {
 /* encode an mdelem */
 static void hpack_enc(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c,
                       grpc_mdelem elem, framer_state *st) {
-  uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem));
-  uint32_t value_hash = grpc_slice_hash(GRPC_MDVALUE(elem));
-  uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash);
-  size_t decoder_space_usage;
-  uint32_t indices_key;
-  int should_add_elem;
-
   GPR_ASSERT(GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)) > 0);
   if (GRPC_SLICE_START_PTR(GRPC_MDKEY(elem))[0] != ':') { /* regular header */
     st->seen_regular_header = 1;
@@ -400,6 +407,22 @@ static void hpack_enc(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c,
         "Reserved header (colon-prefixed) happening after regular ones.");
   }
 
+  if (!is_interned(elem)) {
+    emit_lithdr_noidx_v(c, elem, st);
+    return;
+  }
+
+  uint32_t key_hash;
+  uint32_t value_hash;
+  uint32_t elem_hash;
+  size_t decoder_space_usage;
+  uint32_t indices_key;
+  int should_add_elem;
+
+  key_hash = grpc_slice_hash(GRPC_MDKEY(elem));
+  value_hash = grpc_slice_hash(GRPC_MDVALUE(elem));
+  elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash);
+
   inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum, c->filter_elems);
 
   /* is this elem currently in the decoders table? */
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.c b/src/core/ext/transport/chttp2/transport/hpack_parser.c
index 91bedcf7f0accfdca64c2b44d3c826724620e892..a1ff528d720b2776ca5ba3a5e8a5eb823c1c08af 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.c
@@ -671,6 +671,8 @@ static const uint8_t inverse_base64[256] = {
 static grpc_error *on_hdr(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p,
                           grpc_mdelem md, int add_to_table) {
   if (add_to_table) {
+    GPR_ASSERT(GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_INTERNED ||
+               GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC);
     grpc_error *err = grpc_chttp2_hptbl_add(exec_ctx, &p->table, md);
     if (err != GRPC_ERROR_NONE) return err;
   }
@@ -683,8 +685,14 @@ static grpc_error *on_hdr(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p,
 }
 
 static grpc_slice take_string(grpc_chttp2_hpack_parser *p,
-                              grpc_chttp2_hpack_parser_string *str) {
-  grpc_slice s = grpc_slice_from_copied_buffer(str->str, str->length);
+                              grpc_chttp2_hpack_parser_string *str,
+                              bool intern) {
+  grpc_slice s;
+  if (intern) {
+    s = grpc_slice_intern(grpc_slice_from_static_buffer(str->str, str->length));
+  } else {
+    s = grpc_slice_from_copied_buffer(str->str, str->length);
+  }
   str->length = 0;
   return s;
 }
@@ -819,7 +827,7 @@ static grpc_error *finish_lithdr_incidx(grpc_exec_ctx *exec_ctx,
   grpc_error *err = on_hdr(
       exec_ctx, p,
       grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)),
-                              take_string(p, &p->value)),
+                              take_string(p, &p->value, true)),
       1);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
@@ -830,10 +838,11 @@ static grpc_error *finish_lithdr_incidx_v(grpc_exec_ctx *exec_ctx,
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
-  grpc_error *err = on_hdr(
-      exec_ctx, p, grpc_mdelem_from_slices(exec_ctx, take_string(p, &p->key),
-                                           take_string(p, &p->value)),
-      1);
+  grpc_error *err =
+      on_hdr(exec_ctx, p,
+             grpc_mdelem_from_slices(exec_ctx, take_string(p, &p->key, true),
+                                     take_string(p, &p->value, true)),
+             1);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -888,7 +897,7 @@ static grpc_error *finish_lithdr_notidx(grpc_exec_ctx *exec_ctx,
   grpc_error *err = on_hdr(
       exec_ctx, p,
       grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)),
-                              take_string(p, &p->value)),
+                              take_string(p, &p->value, false)),
       0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
@@ -899,10 +908,11 @@ static grpc_error *finish_lithdr_notidx_v(grpc_exec_ctx *exec_ctx,
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
-  grpc_error *err = on_hdr(
-      exec_ctx, p, grpc_mdelem_from_slices(exec_ctx, take_string(p, &p->key),
-                                           take_string(p, &p->value)),
-      0);
+  grpc_error *err =
+      on_hdr(exec_ctx, p,
+             grpc_mdelem_from_slices(exec_ctx, take_string(p, &p->key, false),
+                                     take_string(p, &p->value, false)),
+             0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -957,7 +967,7 @@ static grpc_error *finish_lithdr_nvridx(grpc_exec_ctx *exec_ctx,
   grpc_error *err = on_hdr(
       exec_ctx, p,
       grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)),
-                              take_string(p, &p->value)),
+                              take_string(p, &p->value, false)),
       0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
@@ -968,10 +978,11 @@ static grpc_error *finish_lithdr_nvridx_v(grpc_exec_ctx *exec_ctx,
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
-  grpc_error *err = on_hdr(
-      exec_ctx, p, grpc_mdelem_from_slices(exec_ctx, take_string(p, &p->key),
-                                           take_string(p, &p->value)),
-      0);
+  grpc_error *err =
+      on_hdr(exec_ctx, p,
+             grpc_mdelem_from_slices(exec_ctx, take_string(p, &p->key, false),
+                                     take_string(p, &p->value, false)),
+             0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }