Server-side render JSS apps headlessly using the JSS proxy

Abstract

How to use the node-headless-ssr-proxy sample app for headless SSR of JSS React, Angular, or Vue.js apps

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:

To headlessly SSR 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:

      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 SSR, build your JSS app for production by running the following script:

    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 SSR 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 Sitecore 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:

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

    npm run start

Keep-Alive agent

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.

A workaround is to add custom middleware where you can modify the request before proxying to contain the values you want to proxy, then call the next() function. For example:

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

Handling headers returned by the proxy app

You can explicitly control what headers the proxy app returns by using the setHeaders function in the config.js file.

By default the application removes the content-security-policy header from the proxy response:

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

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

Media URLs in headless mode

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 Sitecore 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, place the following contents in a configuration patch file, for example, App_Config/Include/RemoveMediaURLs.config:

<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 or integrated JSS apps, usually Content Delivery servers. 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.