Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I developed the JS architecture of https://www.productreview.com.au and we have faced tons of issues getting SSR right, but it was worth it. We're getting more than 10M pageviews a month and I'd like to share our experience:

* Upgrading NodeJS indeed gives us massive performance boosts, but apply with caution. Ideally have a set of visual regression tests just to be safe

* Profile your NodeJS code just like you do with browser code. Sometimes the bottleneck could be in an Express middleware or in reading a massive Webpack manifest file

* If a component doesn't need to rendered on the server, don't do it. Don't waste CPU cycles (for ex, out-of-view content). Just make sure you got your SEO meta tags right

* Don't load more data than you need. It takes time to parse, it takes time to loop through and it takes time to stringify for rehydration

* Enable BabelJS's debug mode and remove unnecessary plugins

* Don't import more stuff than you need. Tree-shaking is important on the server-side too

* If you're using CSS modules, use the Webpack loader `css-loader/locals` on the server so that it doesn't emit CSS files (useless). The client compiler should do so

* Monitor your server-to-server requests. They're usually what take the longest, so cache the most important ones

* As with the majority of websites, cache is king

* Properly serialize your JSON strings. That's what we use: https://gist.github.com/eliseumds/6192135660267e2c64180a8a9c...

* It can be worth it to return a dangerous HTML string from a component instead of a tree of React nodes. We do that when we render SVGs and microdata tags

Again, it's a pain-in-the-butt. You'll have checksum errors, need to synchronize clock, polyfill Intl APIs because they're inconsistent and so on.



> * Properly serialize your JSON strings. That's what we use: https://gist.github.com/eliseumds/6192135660267e2c64180a8a9c....

That doesn’t look like “properly”. The double escaping is overcomplicated and no safer compared to a direct

  window.__productreview_data = ${escapedReduxStateJsonString};
(and forgets about \v, maybe others), the transformation doesn’t preserve “</_escaped_script”, and it doesn’t address a vulnerability involving <!-- that’s contrivable.

Closer to correct:

  JSON.stringify(data)
    .replace(/\u2028/g, '\\u2028')
    .replace(/\u2029/g, '\\u2029')
    .replace(/</g, '\\x3c')
Better, if you put the JSON in an inert <script> (type="application/json"), it’s only necessary to escape < (or /<[/!]/g). This is a good idea so you can use restrictive CSPs.


Thanks for pointing out the `<!--` vulnerability. In regards to rendering the string inside a JSON.parse, we do that because of performance: https://v8.dev/blog/cost-of-javascript-2019. From what I remember, we had some issues with IE11, thus the replacement for the other characters.

We'll consider "application/json", makes sense.


Given a correct function that converts a JSON-representable value to embed-safe JSON, you can use it on the JSON to get your JSON.parse performance:

  const inlineJSON = data =>
    JSON.stringify(data)
      .replace(/\u2028/g, '\\u2028')
      .replace(/\u2029/g, '\\u2029')
      .replace(/</g, '\\x3c');
with:

  const escapedReduxStateJsonString = inlineJSON(JSON.stringify(data));
But yeah, the isolated <script> thing is usually even better (more compact in addition to the security benefit).


I've been recommended this library rather than figuring it for myself:

https://github.com/yahoo/serialize-javascript

You can use it in JSON mode like this: `serialize(obj, {isJSON: true});`




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: