There are many ways to perform “invisible face” culling. Most of them use fairly simple ideas one of them is very easy to implement however the other is harder (but if done correctly a lot faster), So the first method is to calculate each faces normal then compare it to the cameras normal. Upside of this is it is fairly easy to implement and can be extremely effective however the downside is that if you have two blocks infront of each other and dont account for the visibilty of both one can be drawn ontop of the other when its not supposed to. The second method is to use raycasting, this can be much more complex due to implementation, you have to account for the cameras rotation , position , the block rotation and wether the block is visible or not. Upside is that GPUS are designed to perform these tasks with ease and the fact that the ray can be canceled after you have done one block is useful and can drastically improve performance. These methods can be implemented together which each raycast block only showing the faces required dependent on the first method and not having to do this to every block.
Of course you can choose the method and I would recommend asking the community if there are any better methods that they know of.