diff --git a/adev/src/content/guide/ssr.md b/adev/src/content/guide/ssr.md index d5a0cc55580..de2ccb671ca 100644 --- a/adev/src/content/guide/ssr.md +++ b/adev/src/content/guide/ssr.md @@ -362,7 +362,7 @@ To configure this, update your `angular.json` file as follows: You can customize how Angular caches HTTP responses during server‑side rendering (SSR) and reuses them during hydration by configuring `HttpTransferCacheOptions`. This configuration is provided globally using `withHttpTransferCacheOptions` inside `provideClientHydration()`. -By default, `HttpClient` caches all `HEAD` and `GET` requests which don't contain `Authorization`, `Proxy-Authorization`, or `Cookie` headers. You can override those settings by using `withHttpTransferCacheOptions` to the hydration configuration. +By default, `HttpClient` caches all `HEAD` and `GET` requests which don't contain `Authorization`, `Proxy-Authorization`, or `Cookie` headers and are not sent with `withCredentials`. You can override those settings by using `withHttpTransferCacheOptions` to the hydration configuration. ```ts import { bootstrapApplication } from '@angular/platform-browser'; @@ -417,7 +417,7 @@ Use this only when `POST` requests are **idempotent** and safe to reuse between ### `includeRequestsWithAuthHeaders` Determines whether requests containing `Authorization`, `Proxy‑Authorization`, or `Cookie` headers are eligible for caching. -By default, these are excluded to prevent caching user‑specific responses. +By default, these are excluded to prevent caching user‑specific responses. Requests sent with `withCredentials` are also excluded by default. ```ts withHttpTransferCacheOptions({ diff --git a/packages/common/http/src/transfer_cache.ts b/packages/common/http/src/transfer_cache.ts index 95fce76790a..689a4e2ea02 100644 --- a/packages/common/http/src/transfer_cache.ts +++ b/packages/common/http/src/transfer_cache.ts @@ -42,7 +42,7 @@ import {HttpParams} from './params'; * (for example using GraphQL). * @param includeRequestsWithAuthHeaders Enables caching of requests containing `Authorization`, * `Proxy-Authorization`, or `Cookie` headers. By default, these requests are excluded from - * caching. + * caching. Requests sent using `withCredentials` are also excluded by default. * * @see [Configuring the caching options](guide/ssr#configuring-the-caching-options) * @@ -135,6 +135,8 @@ export function transferCacheInterceptorFn( if ( !isCacheActive || requestOptions === false || + // Do not cache requests sent with credentials. + req.withCredentials || // POST requests are allowed either globally or at request level (requestMethod === 'POST' && !globalOptions.includePostRequests && !requestOptions) || (requestMethod !== 'POST' && !ALLOWED_METHODS.includes(requestMethod)) || diff --git a/packages/common/http/test/transfer_cache_spec.ts b/packages/common/http/test/transfer_cache_spec.ts index 4b44c2b7361..0d359087c6c 100644 --- a/packages/common/http/test/transfer_cache_spec.ts +++ b/packages/common/http/test/transfer_cache_spec.ts @@ -40,6 +40,7 @@ interface RequestParams { observe?: 'body' | 'response'; transferCache?: {includeHeaders: string[]} | boolean; headers?: {[key: string]: string}; + withCredentials?: boolean; body?: RequestBody; } @@ -397,6 +398,16 @@ describe('TransferCache', () => { makeRequestAndExpectOne('/test-auth', 'foo'); }); + it('should not cache requests with credentials', async () => { + makeRequestAndExpectOne('/test-auth', 'foo', { + withCredentials: true, + }); + + makeRequestAndExpectOne('/test-auth', 'foo', { + withCredentials: true, + }); + }); + it('should cache POST with the differing body in string form', () => { makeRequestAndExpectOne('/test-1', null, {method: 'POST', transferCache: true, body: 'foo'}); makeRequestAndExpectNone('/test-1', 'POST', {transferCache: true, body: 'foo'}); @@ -549,6 +560,16 @@ describe('TransferCache', () => { makeRequestAndExpectNone('/test-auth'); }); + it(`should not cache requests with credentials when 'includeRequestsWithAuthHeaders' is 'true'`, async () => { + makeRequestAndExpectOne('/test-auth', 'foo', { + withCredentials: true, + }); + + makeRequestAndExpectOne('/test-auth', 'foo', { + withCredentials: true, + }); + }); + it('should cache a POST request', () => { makeRequestAndExpectOne('/include?foo=1', 'post-body', {method: 'POST'});