Walkthrough: Rendering a JSS app server-side using the headless JSS proxy

Current version: 20.x

JSS supports headless server-side rendering (SSR) using any service that supports hosting Node.js applications. The JSS app receives an incoming HTTP request that is rewritten and proxied into a Layout Service request to a Sitecore server. The reply from Layout Service is then provided to the app's SSR infrastructure to render to HTML. Finally, the resulting HTML is returned to the client.

Sitecore provides a sample application to simplify the setup process. The application uses the NPM package sitecore-jss-proxy.

Note

Node.js is a third-party technology that you must configure according to your specific use-case scenarios for Headless mode.

To use this technique, you must have:

This walkthrough describes how to:

  • Serve the JSS application with the headless JSS proxy.

  • Use middleware to work around potential KeepAlive errors.

  • Handle headers returned by the proxy app.

  • Exclude the server host from media URLs.

Serve the JSS application with the headless JSS proxy

Rendering React, Angular, or Vue.js applications server-side requires that you have a server implementation that can serve the production build output of your application. The node-headless-ssr-proxy application sample contains a Node.js server implementation to support this scenario.

To server-side render a JSS React, Angular, or Vue.js app using the sample node-headless-ssr-proxy:

  1. In a terminal, run the following JSS command to create an application based on the node-headless-ssr-proxy sample providing a name for the application, such as jss-proxy-ssr:

    RequestResponse
      npx create-sitecore-jss node-headless-ssr-proxy --appName jss-proxy-ssr
  2. In the JSS app that you want to SSR, in the scjssconfig.json file, set the layoutServiceHost option to the hostname of the jss-proxy-ssr proxy not directly to Sitecore.

    Important

    The scjssconfig.json file might not exist if you have never connected your JSS app to Sitecore.

    For local testing, the hostname defaults to http://localhost:3000. In production, it is the address where you deploy the proxy application, for example, https://www.mysite.com.

    Note

    It is also possible to make requests directly to Sitecore if you do not want to run everything through the reverse proxy. However, proxying the data APIs has the advantage of allowing your Sitecore instance to live behind a firewall.

  3. In a terminal, in the root directory of the JSS app you want to render server-side, build your JSS app for production by running the following script:

    RequestResponse
    jss build
  4. Copy the resulting directory, for example, dist or build, to the root directory of the SSR jss-proxy-ssr app, for example, in a dist/<JSS app name>/ directory.

    Alternatively, in the JSS app, in the scjssconfig.json file, change the instancePath to the SSR sample root path. In the terminal, run the jss deploy files command to deploy the build result to the proxy app.

  5. In the SSR jss-proxy-ssr app, inspect the config.js file and modify it (or use environment variables) to specify values for the following properties:

    • appName - the value for your JSS application, as set in the package.json file in the config.appName configuration.

    • bundlePath - the path where you copied the directory resulting from the build process server.bundle.js. For example: './dist/<JSS app name>/server.bundle'.

    • endpoint - your XM Cloud Experience Edge endpoint.

    • apiKey - the Sitecore SSC API key.

    • In the createViewBag() function, set the dictionary service path to the JSS app's dictionary service URL. The function enables dictionary caching. If you are not using dictionaries, you can remove the entire createViewBag() function.

  6. If proxying to a development Sitecore instance using a privately signed certificate, configure Sitecore CA certificates for Node.js.

    Alternatively, in the SSR application jss-proxy-ssr, in the config.js file, in the proxyOptions object, you can disable SSL validation entirely by setting the secure to false option. For example:

    RequestResponse
      proxyOptions: {
        // NEVER EVER do this in production. It will make your SSL completely insecure.
        secure: false
      }
  7. In a terminal, in the root directory of the SSR jss-proxy-ssr app, run the following script to test the SSR application:

    RequestResponse
    npm run start

Use middleware to work around potential KeepAlive errors

Because of an issue affecting node-http-proxy, if you try to add a middleware with a Keep-Alive agent, you can get an error that causes the server to crash. The issue affects the usage of proxyOptions.onProxyReq with Keep-Alive.

To work around this issue:

  • In the src/index.ts file, implement a custom middleware that modifies the request before proxying to contain the values you want to proxy, then call the next() function. For example:

    RequestResponse
    server.use((req, res, next) => {
      // add custom logic for changing the request
      next();
    });

Handle headers returned by the proxy app

You can explicitly control what headers the proxy app returns.

To modify the returned headers:

  • In the headless proxy application, in the src/config.ts file, modify the body of the setHeaders function to control the headers returned by the proxy application. For example, the default implementation of the function removes the content-security-policy header:

    RequestResponse
    setHeaders: (req, serverRes, proxyRes) => {
        delete proxyRes.headers['content-security-policy'];
    }

    You can modify the body of the function to remove additional headers.

Exclude the server host from media URLs

For a Sitecore server at http://siteco.re, for example, an image in a media field or rich text field has the source URL http://sitecor.re/-/media/jss.jpg, because the Layout Service returns URLs to images with the Sitecore server URL included and exposes the Sitecore server publicly.

When proxying requests in headless mode, however, if the proxy host is http://custom-proxy.host, the desired URL is http://custom-proxy.host/-/media/jss.jpg or better yet /-/media/jss.jpg.

To use the headless proxy without exposing the Sitecore server publicly, you can configure the Layout Service with a Sitecore configuration patch.

To instruct Sitecore not to include the server host as part of media requests:

  1. In the JSS application you want to render server-side with the proxy application, create a configuration patch file, for example, sitecore/config/Include/RemoveMediaURLs.config, with the following content:

    RequestResponse
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
        <layoutService>
          <configurations>
            <config name="jss">
              <rendering>
                <renderingContentsResolver>
                  <IncludeServerUrlInMediaUrls>false</IncludeServerUrlInMediaUrls>
                </renderingContentsResolver>
              </rendering>
            </config>
          </configurations>
        </layoutService>
      </sitecore>
    </configuration>
    Important

    This configuration is appropriate for servers that run headless JSS apps. Enabling this configuration causes images to break when running JSS apps in connected mode because the images are served as if local. Do not use this configuration for servers used in active development.

  2. Deploy the configuration to your Sitecore instance by running the JSS CLI command:

    RequestResponse
    jss deploy config
Note

Depending on how you created the application, this configuration might already be present. Verify the files contained in the following folders:

  • /src/platform/App_Config/Include, for apps contained in a full-stack container-based solution.

  • /sitecore/config, for a standalone JSS app.

Do you have some feedback for us?

If you have suggestions for improving this article,