Actually, it's a harder workaround than I was suggesting. When the ETag isn't explictly set, the full body of the middleware chain is generated every single time, irrespective of the Conditional Get.
You want the ETag generated without the Length Hiding, which isn't the way Rack::ETag works. It digests the whole content - it's implementation calls upstream first, so it effectively ensures it's always at the end of the middleware chain.
You'd need to extend/replace/otherwise hook into Rack::ETag, which is a bit more surgical.