diff --git a/src/node_http2.cc b/src/node_http2.cc
index 43d1c2ea9f277a..cdf83350bb2a53 100644
--- a/src/node_http2.cc
+++ b/src/node_http2.cc
@@ -746,8 +746,10 @@ void Http2Session::Close(uint32_t code, bool socket_closed) {
   flags_ |= SESSION_STATE_CLOSING;
 
   // Stop reading on the i/o stream
-  if (stream_ != nullptr)
+  if (stream_ != nullptr) {
+    flags_ |= SESSION_STATE_READING_STOPPED;
     stream_->ReadStop();
+  }
 
   // If the socket is not closed, then attempt to send a closing GOAWAY
   // frame. There is no guarantee that this GOAWAY will be received by
@@ -1228,6 +1230,7 @@ int Http2Session::OnDataChunkReceived(nghttp2_session* handle,
   // If we are currently waiting for a write operation to finish, we should
   // tell nghttp2 that we want to wait before we process more input data.
   if (session->flags_ & SESSION_STATE_WRITE_IN_PROGRESS) {
+    CHECK_NE(session->flags_ & SESSION_STATE_READING_STOPPED, 0);
     session->flags_ |= SESSION_STATE_NGHTTP2_RECV_PAUSED;
     return NGHTTP2_ERR_PAUSE;
   }
@@ -1616,6 +1619,7 @@ void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) {
   ClearOutgoing(status);
 
   if ((flags_ & SESSION_STATE_READING_STOPPED) &&
+      !(flags_ & SESSION_STATE_WRITE_IN_PROGRESS) &&
       nghttp2_session_want_read(session_)) {
     flags_ &= ~SESSION_STATE_READING_STOPPED;
     stream_->ReadStart();
diff --git a/test/parallel/test-http2-multistream-destroy-on-read-tls.js b/test/parallel/test-http2-multistream-destroy-on-read-tls.js
new file mode 100644
index 00000000000000..91cbec6b2dc975
--- /dev/null
+++ b/test/parallel/test-http2-multistream-destroy-on-read-tls.js
@@ -0,0 +1,47 @@
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto)
+  common.skip('missing crypto');
+const fixtures = require('../common/fixtures');
+const http2 = require('http2');
+
+// Regression test for https://github.com/nodejs/node/issues/29353.
+// Test that it’s okay for an HTTP2 + TLS server to destroy a stream instance
+// while reading it.
+
+const server = http2.createSecureServer({
+  key: fixtures.readKey('agent2-key.pem'),
+  cert: fixtures.readKey('agent2-cert.pem')
+});
+
+const filenames = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
+
+server.on('stream', common.mustCall((stream) => {
+  function write() {
+    stream.write('a'.repeat(10240));
+    stream.once('drain', write);
+  }
+  write();
+}, filenames.length));
+
+server.listen(0, common.mustCall(() => {
+  const client = http2.connect(`https://localhost:${server.address().port}`, {
+    ca: fixtures.readKey('agent2-cert.pem'),
+    servername: 'agent2'
+  });
+
+  let destroyed = 0;
+  for (const entry of filenames) {
+    const stream = client.request({
+      ':path': `/${entry}`
+    });
+    stream.once('data', common.mustCall(() => {
+      stream.destroy();
+
+      if (++destroyed === filenames.length) {
+        client.destroy();
+        server.close();
+      }
+    }));
+  }
+}));