In Angular 17, SSR (Server-Side Rendering) does not support cookies, which means cookies are not sent along with requests. In my case, I needed to send my HTTP-only, session JWT cookie to the backend so the server could render pages requiring authentication. You can achieve this by following these steps:
Add Cookie Service
Add a cookie service that provides cookies depending on whether it's used on the client or server side.
export const APP_SSR_COOKIES = new InjectionToken<any>('APP_SSR_COOKIES');
@Injectable({
providedIn: 'root',
})
export class CookieService {
private isBrowser: boolean;
constructor(
@Optional() @Inject(DOCUMENT) private doc: Document,
@Inject(PLATFORM_ID) private platformId: string,
@Optional() @Inject(APP_SSR_COOKIES) private ssrCookies: string,
) {
this.isBrowser = isPlatformBrowser(this.platformId);
}
get(): string {
return this.isBrowser ? this.doc.cookie : this.ssrCookies;
}
}
Add Provider for Cookies
You need to provide cookies to your app. Modify server.ts file. Import APP_SSR_COOKIES
from your cookie service.
server.get('*', (req, res, next) => {
const { protocol, originalUrl, baseUrl, headers } = req;
commonEngine
.render({
bootstrap,
documentFilePath: indexHtml,
url: `${protocol}://${headers.host}${originalUrl}`,
publicPath: browserDistFolder,
providers: [
{ provide: APP_BASE_HREF, useValue: baseUrl },
{ provide: APP_SSR_COOKIES, useValue: req.headers.cookie },
],
})
.then((html) => res.send(html))
.catch((err) => next(err));
});
Add HTTP Interceptor
export function credentialsInterceptor(
req: HttpRequest<unknown>,
next: HttpHandlerFn,
): Observable<HttpEvent<unknown>> {
const platformId = inject(PLATFORM_ID);
const cookieService = inject(CookieService);
const isBrowser = isPlatformBrowser(platformId);
let modifiedReq: HttpRequest<unknown>;
if (isBrowser) {
// No need for cookies on client-side requests, as the browser handles them automatically.
modifiedReq = req.clone({
withCredentials: true,
});
} else {
// Server-side code
const cookies = cookieService.get();
// Parse the session cookie and set it on the request
const sessionCookie = cookies
?.split(';')
?.find((cookie) => cookie.startsWith('session='));
// Manually add the cookie header
modifiedReq = req.clone({
withCredentials: true,
setHeaders: {
cookie: sessionCookie ?? '',
},
});
}
return next(modifiedReq);
}
Disclaimer
Remember that Angular does not use server.ts in the development environment! This solution only works in production and not locally.
Result
Now, the session cookie should be sent to your server, enabling the server to properly render authenticated data.
Example Repository
This site uses the described approach. You can check the source code here: https://github.com/KasperiP/til-frontend