How to use cookies with Angular 17 SSR

Angular 17 offers SSR, but there is no cookie support by default. This post will explain how to use the cookies properly without third party packages.

Avatar

Kasperi Pohtinen

2 min read · Jan 16, 2024 · 2 likes

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