Why does enabling caching break password protected pages? Is there a way around this?

Note that how you would do this, as well as if it’s possible at all, are both unique to each and every single caching system/service/product/plugin. There is no one size fits all generic answer.

Why does this happen?

When you use a page caching mechanism, a version of the page is stored and then served to all users who ask for it.

Passworded pages don’t fit into that because the page shows different things to different people based on wether they entered the password or not, on the same URL.

The same goes for any other dynamic pages.

Is there a way to have caching on a password protected page enabled without it breaking the password functionality?

This depends greatly on the capabilities of your caching system. For some caches it is impossible. For others it is possible but expensive ( either in server performance, or in literal money ).

WP knows if you can see the post or not by using this check:

if ( ! isset( $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] ) ) {

Note that this check is also filtered so plugins may change this, and if that’s the case this answer may not be usable. Consult the plugins documentation/support if this is your situation.

This means you should configure your caching system to not cache requests that contain a cookie starting with wp-postpass_.

This might mean you have to tell your caching system to not cache requests that contain cookies, which would severely reduce the number of requests that can be cached. It may also be impossible to ignore cookies by partial matches, the hash used will be different depending on the site and the password used.

Some caching systems support bucketing, which would allow you to have a separate cache for people with that cookie. Not all caching systems and services support this. You would need to consult the documentation of your chosen solution.

Can I Do This in a Generic Way That Works for All Most Caching?

No. There is no standard that can be used to do this. The answer is unique and individual to each caching system.

WordPress does not provide a full-page caching API that you can use to do this, and even if it did a lot of caching systems involve CDNs that run on different servers from the one running your site, all built in different ways. Some don’t support this at all.

Wether this can be done at all is extremely specific to the caching service software or plugin you used.

For example, some plugins let you use a filter, some systems use HTTP headers, others need you to write a rule in a configuration panel. There is no single solution that works everywhere, and no standard you can use. It’s extremely fragmented.

The most foolproof method of resolving this in the generic non-WP sense would be to lead users who enter the password to a new URL, but that’s not how passworded pages in WP work, and it would mean anybody could share the new URL to bypass the protection trivially