---
title: Session verification during server-side rendering
description: Enable secure session verification during server-side rendering with cookie-based sessions.
sidebar:
  order: 3
---

## Overview

In Server Side Rendering (SSR) scenarios, the session verification process is slightly different.
Check the following guide to understand how to adjust the flow to work with SSR.

## Before you start

For an ordinary browser navigation, the browser can send cookie-based session tokens to the server performing SSR. It
cannot add SuperTokens' header-transfer token to that navigation. Cookie transfer is the default; the cookie's domain,
path, `SameSite`, and `Secure` attributes must allow it to reach the SSR server.

:::info[Access token guidance]
This guide applies to scenarios involving **SuperTokens Session Access Tokens**.
:::

## Steps

### 1. Enable sharing of cookies across subdomains

If your API layer and website are on different subdomains (like `example.com` and `api.example.com`), then by default, the session tokens attach only to `api.example.com`.
Change this to ensure that the session tokens attach to `.example.com` and the access token cookie goes to your web server on `example.com`.
Enable this by [setting the `cookieDomain` configuration on the backend](/post-authentication/session-management/advanced-workflows/multiple-api-endpoints).

### 2. Verify the session

Prefer the released session verification API for your backend framework. For example, the Node.js Next.js integration
exports `getSSRSession`, which accepts the request's cookies and validates the access-token signature, expiry, and payload
shape. It does not run global claim validators or perform an authoritative database check for session revocation. Treat its
payload as authenticated claims, not as a complete authorization decision: explicitly validate every claim required by the
page, such as tenant, role, permission, email verification, or MFA state.

Before rendering sensitive protected data for which immediate revocation matters, call an authoritative backend/API
endpoint that verifies the session with database checking enabled. Other backend frameworks can use `getSession` or
`verifySession`, provided the SSR response propagates any token updates that the session API attaches. If your platform has
no released SuperTokens SDK, follow the strict requirements in the
[manual verification fallback](/additional-verification/session-verification/protect-api-routes#manual-jwt-verification),
not signature-only JWT verification.

If the access token is missing, invalid, or expired, redirect the user to a
`/refresh-session?redirectBack=<current route>` page. You can use another local path on your website instead.

On the `/refresh-session` page, you want to call the `attemptRefreshingSession` function (from the client side).
This function attempts to refresh the session.
If it succeeds, it returns `true`.
If it fails, it returns `false`.
If it returns `true`, you want to redirect the user back to the page they were on.
If it returns `false`, you want to redirect the user to the login page.

### 3. Implement the refresh-session flow (`/refresh-session` page)

On this path, attempt to refresh the session. This can produce either result:

- **Success:** The frontend gets new access and refresh tokens. Redirect the user to the validated local path in the
  `redirectBack` query parameter.
- **Failure:** The session has expired or the backend has revoked it. Redirect the user to the login page.

Below is the code snippet that you can use on the `/refresh-session` path on the frontend

<UITypeSwitch />

<VariantContent storageKey="ui-type" value="prebuilt">

<CodeGroup group="frontend-prebuilt-ui">
<Tab title="Reactjs" value="reactjs">
```tsx
import React from "react";
import Session from "supertokens-auth-react/recipe/session";
import SuperTokens from "supertokens-auth-react";

export function AttemptRefresh() {
  React.useEffect(() => {
    let cancel = false;
    Session.attemptRefreshingSession().then((success) => {
      if (cancel) {
        // component has unmounted somehow..
        return;
      }
      if (success) {
        // we have new session tokens, so we redirect the user back
        // to where they were.
        const urlParams = new URLSearchParams(window.location.search);
        const redirectBack = new URL(urlParams.get("redirectBack") ?? "/", window.location.origin);
        window.location.href =
          redirectBack.origin === window.location.origin
            ? `${redirectBack.pathname}${redirectBack.search}${redirectBack.hash}`
            : "/";
      } else {
        // we redirect to the login page since the user
        // is now logged out
        SuperTokens.redirectToAuth();
      }
    });
    return () => {
      cancel = true;
    };
  }, []);
  return null;
}
```
</Tab>
<Tab title="Angular" value="angular">
```tsx
import Session from "supertokens-web-js/recipe/session";

function attemptRefresh() {
  Session.attemptRefreshingSession().then((success) => {
    if (success) {
      // we have new session tokens, so we redirect the user back
      // to where they were.
      const urlParams = new URLSearchParams(window.location.search);
      const redirectBack = new URL(urlParams.get("redirectBack") ?? "/", window.location.origin);
      window.location.href =
        redirectBack.origin === window.location.origin
          ? `${redirectBack.pathname}${redirectBack.search}${redirectBack.hash}`
          : "/";
    } else {
      // we redirect to the login page since the user
      // is now logged out
      window.location.href = "/login";
    }
  });
}
```
</Tab>
</CodeGroup>

</VariantContent>

<VariantContent storageKey="ui-type" value="custom">



<DependentContent passive group="frontend-custom-ui">
<ContentOption title="Mobile" value="mobile">
:::warning[Server side rendering is not applicable for mobile apps.]
:::
</ContentOption>
</DependentContent>

<CodeGroup group="frontend-custom-ui">
<Tab title="Web" value="web">
<DependentContent group="install-method" label="Installation method">
<ContentOption title="npm" value="npm">
```tsx
import Session from "supertokens-web-js/recipe/session";

function attemptRefresh() {
  Session.attemptRefreshingSession().then((success) => {
    if (success) {
      // we have new session tokens, so we redirect the user back
      // to where they were.
      const urlParams = new URLSearchParams(window.location.search);
      const redirectBack = new URL(urlParams.get("redirectBack") ?? "/", window.location.origin);
      window.location.href =
        redirectBack.origin === window.location.origin
          ? `${redirectBack.pathname}${redirectBack.search}${redirectBack.hash}`
          : "/";
    } else {
      // we redirect to the login page since the user
      // is now logged out
      window.location.href = "/login";
    }
  });
}
```
</ContentOption>
<ContentOption title="Script tag" value="script-tag">
```tsx check=false reason="script-tag example relies on globals provided by loaded SuperTokens bundles"
function attemptRefresh() {
  supertokensSession.attemptRefreshingSession().then((success) => {
    if (success) {
      // we have new session tokens, so we redirect the user back
      // to where they were.
      const urlParams = new URLSearchParams(window.location.search);
      const redirectBack = new URL(urlParams.get("redirectBack") ?? "/", window.location.origin);
      window.location.href =
        redirectBack.origin === window.location.origin
          ? `${redirectBack.pathname}${redirectBack.search}${redirectBack.hash}`
          : "/";
    } else {
      // we redirect to the login page since the user
      // is now logged out
      window.location.href = "/login";
    }
  });
}
```
</ContentOption>
</DependentContent>
</Tab>
<Tab title="Mobile" value="mobile">

</Tab>
</CodeGroup>



</VariantContent>

#### Why trigger the refresh session flow instead of redirecting the user to the login page directly?

Two reasons why JWT verification can fail are:
- The session tokens were not passed from the frontend: This can happen if the route is accessed when the user has logged out.
- The session tokens pass from the frontend, but the JWT has expired.

If the user goes to the login page directly, then in the second case, the frontend redirects the user back to the current route (since the refresh token is still valid), causing an infinite loop.
To counteract this issue, redirect the user to a refresh page, which creates a new access token.
In the first case, when the user is actually logged out, the refreshing fails, and they go to the login page anyway.

#### Can `verifySession` or `getSession` be used during SSR?

Yes. A released, read-only SSR helper such as Node.js Next.js `getSSRSession` can authenticate the access token, but the
application must still validate the authorization claims required by the page. The helper does not run global claim
validators or authoritatively check database revocation. For sensitive data that requires immediate revocation, verify the
session through an authoritative backend/API endpoint with database checking before rendering.

You can also use `verifySession` or `getSession`, but those APIs may attach rotated or updated tokens to the response. The
SSR server must propagate those updates because frontend SDK network interceptors do not run for the browser's navigation
request. If the SSR process should not receive SuperTokens Core credentials, use the read-only helper for authentication
and an authoritative backend/API check for sensitive authorization, or use a manual verifier that meets every requirement
in the linked fallback guide.

---

## See also

<CardGroup cols={3}>
  <Card title="Protect backend routes" href="/additional-verification/session-verification/protect-api-routes" />
  <Card title="Protect frontend routes" href="/additional-verification/session-verification/protect-frontend-routes" />
  <Card title="Claim validation" href="/additional-verification/session-verification/claim-validation" />
  <Card title="Access session data" href="/post-authentication/session-management/access-session-data" />
</CardGroup>
