Index: modules/cache/cache_util.c
===================================================================
--- modules/cache/cache_util.c	(revision 1451191)
+++ modules/cache/cache_util.c	(working copy)
@@ -27,7 +27,7 @@
 
 extern module AP_MODULE_DECLARE_DATA cache_module;
 
-#define CACHE_SEPARATOR ",   "
+#define CACHE_SEPARATOR ", \t"
 
 /* Determine if "url" matches the hostname, scheme and port and path
  * in "filter". All but the path comparisons are case-insensitive.
@@ -542,17 +542,84 @@
     }
 
     /* These come from the cached entity. */
-    if (h->cache_obj->info.control.no_cache
-            || h->cache_obj->info.control.no_cache_header
-            || h->cache_obj->info.control.private_header) {
+    if (h->cache_obj->info.control.no_cache) {
         /*
-         * The cached entity contained Cache-Control: no-cache, or a
-         * no-cache with a header present, or a private with a header
-         * present, so treat as stale causing revalidation.
+         * The cached entity contained Cache-Control: no-cache, so
+         * treat as stale causing revalidation.
          */
         return 0;
     }
+    if (h->cache_obj->info.control.no_cache_header
+            || h->cache_obj->info.control.private_header) {
+        /*
+         * RFC2616 14.9.1: The cached entity contained
+         * Cache-Control: no-cache=, or Cache-Control: private=, with
+         * a header present, hence we are allowed to serve this entity,
+         * but without the specified headers, so let's strip them now,
+         * and fall through the other restrictions.
+         *
+         * Here we assume mixed Cache-Control: no-cache and no-cache=
+         * have been caught above and treated as stale causing revalidation,
+         * leaving here the only no-cache= and/or private= with a header.
+         */
+        char *token;
+        const char *header = apr_table_get(h->resp_hdrs, "Cache-Control");
+        while (header && (token = ap_get_list_item(r->pool, &header))) {
+            /* ap_get_list_item() strips the spurious whitespaces and
+             * lowercases anything (but the quoted-strings) */
+            if (strncmp(token, "no-cache=", 9) == 0) {
+                token += 9;
+            }
+            else if (strncmp(token, "private=", 8) == 0) {
+                token += 8;
+            }
+            else {
+                continue;
+            }
 
+            if (*token == '"') {
+                /* RFC2616 2.2: quoted-string
+                 * found no ap_*() function to unquote those strings,
+                 * so the job is done here... */
+                char *pos, *start, *end;
+                pos = start = end = token + 1;
+                while (*pos && *pos != '"') {
+                    if (*pos == '\\') {
+                        /* RFC2616 2.2: quoted-pair */
+                        if (end == pos) {
+                            /* duplicate to preserve the original token
+                             * should the quoted-string be invalid */
+                            start = apr_pstrdup(r->pool, start);
+                            pos = end = start + (pos - token) - 1;
+                        }
+                        /* skip the quote */
+                        pos++;
+                    }
+                    if (end != pos) {
+                        *end = *pos;
+                    }
+                    end++;
+                    pos++;
+                }
+                if (*pos == '"' && !*(pos + 1)) {
+                    /* valid quoted-string */
+                    token = start;
+                    *end = '\0';
+                }
+                else {
+                    /* invalid quoted-string, continue? fall through?
+                     * like ap_get_mime_headers_core() we do not check
+                     * headers' names validity, and just fall through,
+                     * is there a tiny chance to unset such a header? */
+                    /*continue;*/
+                }
+            }
+
+            /* strip that header from the response */
+            apr_table_unset(h->resp_hdrs, token);
+        }
+    }
+
     if ((agestr = apr_table_get(h->resp_hdrs, "Age"))) {
         age_c = apr_atoi64(agestr);
     }
