If this is reproducible, enable debug logging on your Notebook Server (`--log-level=DEBUG`) and reproduce your issue. Once disconnected, you should see log messages indicating that messages are being buffered. Upon re-connecting, you'll see a message that N messages are being discarded for a given value (i.e., key).
The issue is that the key used to determine if the buffered messages should be replayed is a connection-specific value and a new value is produced with each new connection. As a result, the value associated with the now current connection won't be found in the dictionary of buffered messages, so none of the messages relative to the previous (disconnected) connection will ever be replayed.
I think the crux of the issue is locating a value that persists across "tab invocations" (i.e. connections) but isn't kernel-scoped because, apparently, a given kernel instance running from the same notebook server can have multiple connections simultaneously.
Once replay is resolved within the server, there's a good chance that some set of changes in the front end will also be necessary. I know because I've modified the server to use a kernel-scoped key value so messages are replayed to the front-end, but they still don't appear.
See Issue
https://github.com/jupyter/notebook/issues/4105, and PRs
https://github.com/jupyter/notebook/pull/2871 and
https://github.com/jupyter/notebook/pull/4110.