Headless SSR via sitecore-jss-proxy
JSS supports headless server-side rendering using any service that supports hosting Node.js applications. In this technique the JSS app receives an incoming HTTP request, which is then 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, the same way that Integrated Mode would work within Sitecore, and then finally the resultant HTML is returned to the client.
All Sitecore marketing features are supported by this headless mode, including personalization, tracking, multivariate testing, etc.
How to use
Clone the JSS samples repository and copy the
samples/node-headless-ssr-proxy
folder to your disk, for example:c:\jss-headless
.This sample app acts as a HTTP proxy to the Sitecore server, proxying incoming requests to Layout Service, and then rendering the resultant JSON to HTML before returning it. The app is heavily commented.
Create a production build of your app with
jss build
. Ensure that thelayoutServiceHost
in the app'sscjssconfig.json
is set to the hostname of thenode-headless-ssr-proxy
proxy (not directly to Sitecore). For local testing, this would default tohttp://localhost:3000
. In production, it would likely be something likehttps://www.mysite.com
. It is also possible to make requests directly to Sitecore, if you do not wish to run everything through the reverse proxy. Proxying the data APIs has the advantage of allowing Sitecore to live behind a firewall.Copy the production build artifacts from your app to the proxy (i.e.
/node-headless-ssr-proxy/dist/MyApp
). The path copied to should match the relative path that is set in thesitecoreDistPath
config in the app'spackage.json
file (/dist/MyApp
in the previous example).Open
/config.js
file and,- Set
bundlePath
to the path to your built JSS app'sserver.bundle.js
, i.e.'./dist/myappname/server.bundle'
- Set
apiHost
to your Sitecore instance host and theapiKey
to your SSC API Key (see server setup if you don't have an API key yet) - Set
apiKey
to your Sitecore SSC API key - Set the dictionary service path in
createViewBag()
to your app's dictionary service URL. If you are not using dictionaries, you can remove the wholecreateViewBag()
function, which enables dictionary caching.
NOTE: It is possible to configure these settings using environment variables as well, if that is preferable. This is great for containers and some PaaS hosts.
- Set
If proxying to a development Sitecore instance using a privately signed certificate, ensure you've configured Sitecore CA certificates for Node.js. Alternatively, you can disable SSL validation entirely by setting
secure
tofalse
in the proxy options, e.g. in/config.js
proxyOptions: {
// NEVER EVER do this in production. It will make your SSL completely insecure.
secure: false
}
Open a command line in your
node-headless-ssr-proxy
folder, and runnpm install
thennpm start
.The console should show
server listening on port 3000!
. To test, browse tohttp://localhost:3000/
and you should see the same app rendering now in the headless configuration.
Tips & Tricks
Keep-Alive
NOTE: Currently here is a limitation for usage of
proxyOptions.onProxyReq
. UsingonProxyReq
withkeep-alive
can cause server to crash. You can add custom middleware where you can modify request before proxying to contain the values you wish to proxy. Here is an opened GitHub issue.
server.use((req, res, next) => {
// add custom logic here
next();
});
Headers handling
You can explicitly control which headers your app will return. Use setHeaders
function in ./config.js
.
By default:
setHeaders: (req, serverRes, proxyRes) => {
delete proxyRes.headers['content-security-policy'];
}
But you can extend it and remove additional headers.
Media URLs in headless mode
The Layout Service will return URLs to images with the Sitecore server URL included. For example:
- The Sitecore server is
http://siteco.re
- An image in a media field, or a rich text field, would be returned something like
http://sitecor.re/-/media/jss.jpg
- In headless mode if the proxy is
http://proxy
, what we really want ishttp://proxy/-/media/jss.jpg
or even better,/-/media/jss.jpg
In headless mode it is possible to use the headless proxy without exposing the Sitecore server publicly, which makes media URLs that contain the Sitecore server URL problematic. It is easy enough to tell Sitecore to not include the server as part of media requests, however. Place the following contents in a config patch file under App_Config/Include/ADescriptiveName.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>
NOTE: this configuration is appropriate for servers that run headless mode or integrated mode JSS sites - generally Content Delivery servers. Enabling this configuration will cause images to break when JSS sites are run in connected mode, as the images will be served as if local - so do not enable this for servers where development is occurring.