diff -ru dnsdist-1.9.11.orig/dnsdist-doh-common.hh dnsdist-1.9.11/dnsdist-doh-common.hh
--- dnsdist-1.9.11.orig/dnsdist-doh-common.hh	2025-09-18 09:10:23.000000000 +0200
+++ dnsdist-1.9.11/dnsdist-doh-common.hh	2026-02-23 14:44:36.895401225 +0100
@@ -36,6 +36,7 @@
 namespace dnsdist::doh
 {
 static constexpr uint32_t MAX_INCOMING_CONCURRENT_STREAMS{100U};
+static constexpr size_t MAX_INCOMING_HTTP_HEADERS{256U};

 std::optional<PacketBuffer> getPayloadFromPath(const std::string_view& path);
 }
diff -ru dnsdist-1.9.11.orig/dnsdist-nghttp2-in.cc dnsdist-1.9.11/dnsdist-nghttp2-in.cc
--- dnsdist-1.9.11.orig/dnsdist-nghttp2-in.cc	2025-09-18 09:10:23.000000000 +0200
+++ dnsdist-1.9.11/dnsdist-nghttp2-in.cc	2026-02-23 14:44:36.895638277 +0100
@@ -1114,6 +1114,11 @@
       if (!query.d_headers) {
         query.d_headers = std::make_unique<HeadersMap>();
       }
+      if (query.d_headers->size() >= dnsdist::doh::MAX_INCOMING_HTTP_HEADERS) {
+        /* be nice but not too nice */
+        vinfolog("Too many incoming DoH headers");
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
+      }
       // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): nghttp2 API
       query.d_headers->insert({std::string(reinterpret_cast<const char*>(name), nameLen), std::string(valueView)});
     }
diff -ru dnsdist-1.9.11.orig/doh3.cc dnsdist-1.9.11/doh3.cc
--- dnsdist-1.9.11.orig/doh3.cc	2025-09-18 09:10:23.000000000 +0200
+++ dnsdist-1.9.11/doh3.cc	2026-02-23 14:46:39.286968769 +0100
@@ -712,6 +712,10 @@
       std::string_view content(reinterpret_cast<char*>(value), value_len);
       // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): Quiche API
       auto* headersptr = reinterpret_cast<h3_headers_t*>(argp);
+      if (headersptr->size() >= dnsdist::doh::MAX_INCOMING_HTTP_HEADERS) {
+        /* be nice but not too nice */
+        return 1;
+      }
       headersptr->emplace(key, content);
       return 0;
     },
@@ -798,6 +802,13 @@
       break;
     }

+    if (len > std::numeric_limits<uint16_t>::max() || (std::numeric_limits<uint16_t>::max() - streamBuffer.size()) < static_cast<size_t>(len)) {
+      vinfolog("DOH3 data frame of size %d is too large for a DNS query (we already have %d)", len, streamBuffer.size());
+      conn.d_streamBuffers.erase(streamID);
+      handleImmediateError("DoH3 non-compliant query");
+      return;
+    }
+
     buffer.resize(static_cast<size_t>(len));
     streamBuffer.insert(streamBuffer.end(), buffer.begin(), buffer.end());
   }
@@ -830,15 +841,17 @@
     if (streamID < 0) {
       break;
     }
+    std::unique_ptr<quiche_h3_event, decltype(&quiche_h3_event_free)> eventPtr(event, quiche_h3_event_free);
+    event = nullptr;
     conn.d_headersBuffers.try_emplace(streamID, h3_headers_t{});

-    switch (quiche_h3_event_type(event)) {
+    switch (quiche_h3_event_type(eventPtr.get())) {
     case QUICHE_H3_EVENT_HEADERS: {
-      processH3HeaderEvent(clientState, frontend, conn, client, serverConnID, streamID, event);
+      processH3HeaderEvent(clientState, frontend, conn, client, serverConnID, streamID, eventPtr.get());
       break;
     }
     case QUICHE_H3_EVENT_DATA: {
-      processH3DataEvent(clientState, frontend, conn, client, serverConnID, streamID, event, buffer);
+      processH3DataEvent(clientState, frontend, conn, client, serverConnID, streamID, eventPtr.get(), buffer);
       break;
     }
     case QUICHE_H3_EVENT_FINISHED:
@@ -847,8 +860,6 @@
     case QUICHE_H3_EVENT_GOAWAY:
       break;
     }
-
-    quiche_h3_event_free(event);
   }
 }

diff -ru dnsdist-1.9.11.orig/doh.cc dnsdist-1.9.11/doh.cc
--- dnsdist-1.9.11.orig/doh.cc	2025-09-18 09:10:23.000000000 +0200
+++ dnsdist-1.9.11/doh.cc	2026-02-23 14:44:36.896679318 +0100
@@ -960,6 +960,12 @@

     if (dsc->dohFrontend->d_keepIncomingHeaders) {
       dohUnit->headers = std::make_unique<std::unordered_map<std::string, std::string>>();
+      if (req->headers.size >= dnsdist::doh::MAX_INCOMING_HTTP_HEADERS) {
+        /* be nice but not too nice */
+        vinfolog("Too many incoming DoH headers");
+        h2o_send_error_400(req, "Bad Request", "The DNS query had too many HTTP headers", 0);
+        return;
+      }
       dohUnit->headers->reserve(req->headers.size);
       for (size_t i = 0; i < req->headers.size; ++i) {
         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): h2o API
diff -ru dnsdist-1.9.11.orig/doq.cc dnsdist-1.9.11/doq.cc
--- dnsdist-1.9.11.orig/doq.cc	2025-09-18 09:10:23.000000000 +0200
+++ dnsdist-1.9.11/doq.cc	2026-02-23 14:44:36.898356083 +0100
@@ -633,6 +633,15 @@
       return;
     }

+    if (received > std::numeric_limits<uint16_t>::max() || (std::numeric_limits<uint16_t>::max() - streamBuffer.size()) < static_cast<size_t>(received)) {
+      vinfolog("DoQ data frame of size %d is too large for a DNS query (we already have %d)", received, streamBuffer.size());
+      conn.d_streamBuffers.erase(streamID);
+      ++dnsdist::metrics::g_stats.nonCompliantQueries;
+      ++clientState.nonCompliantQueries;
+      quiche_conn_stream_shutdown(conn.d_conn.get(), streamID, QUICHE_SHUTDOWN_WRITE, static_cast<uint64_t>(DOQ_Error_Codes::DOQ_PROTOCOL_ERROR));
+      return;
+    }
+
     streamBuffer.resize(existingLength + received);
     if (fin) {
       break;
