SSR - Status code 301 is missing when the page comes from the cache ( Always returns 200 )

Viewed 172

We currently have a problem with SSR optimized engine. The scenario is that a product has been removed or renamed on the catalog, and the user tends to access that old none existing URL directly. Then we have implemented a redirected behavior sending a status code of 301 so that the customer gets redirected to a relevant page where its URL is retrieved from BE. (Through a generic HTTP interceptor catchError)

catchError((error: any) => {
 if (error instanceof HttpErrorResponse) {
  ...
  this.response.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
  this.response.set('location',this.service.urlBuilder(redirectUrl));
  return this.setMovedPermanentlyStatus();
  }
}

However, we also have those pages cached, so the next time that the page comes from the cache, it returns 200 instead of 301, which we tried to implement in the first place. We tried to rule out those with 301 not to be treated like others, but it still returns with 200 when they're coming from the cache. ( By extending SSR optimized engine with an exception like )

   protected fallbackToCsr(response: Response, filePath: string, callback: SsrCallbackFn): void {
    if (response.statusCode !== HttpResponseStatus.MOVED_PERMANENTLY) {
      response.set('Cache-Control', 'no-store');
      callback(undefined, this.getDocument(filePath));
    }
  }

We're looking for a solution to ensure that all 'the redirected pages' will always respond with 301 and not 200, even if they come from the cache.

1 Answers

By extending SSR optimized engine

How did you extended (and used) optimized engine? I'm curious about this, because as I see it, the instance is hardcoded (and not injected), just like this:

// apply optimization wrapper if optimization options were defined
    return optimizationOptions
      ? new OptimizedSsrEngine(engineInstance, optimizationOptions)
          .engineInstance
      : engineInstance;

If you actually managed to use your own instance of Optimized Engine, then you could do this: Override renderResponse

  protected renderResponse(
    filePath: string,
    options: any,
    callback: (err?: Error | null, html?: string) => void
  ): void {
    super.renderResponse(filePath, options, callback);
    const response: Response = options.res || options.req.res;
    if (response.statusCode === HttpResponseStatus.MOVED_PERMANENTLY) {
      // remove the result from cache so that it's executed again
      const request: Request = options.req;
      const key = this.getRenderingKey(request);
      this.renderingCache.clear(key);
    }
  }

However, this might not be too optimum. One way to improve: Use an additional internal Map<string,string> cache, and add code to store the renderingKey of the request and the Location value of the header inside the if above; so that, before calling super.renderResponse you will add code to check the map with the key; if present, then redirect to the result, and avoid the call to super.renderResponse.

Additionally, supposing that it's not possible to extend from OptimizedSsrEngine, the way to go would be to add a middleware on express server (see at server.ts file in your project), and make the implementation at low-level: the same approach, using a Map to store the redirects. You could add one middleware before the default to check that map, and calling next() only if the request key it's not in the map; and one after, to store the request key and the redirect if it's a redirect (ensure to add a call to next() from the default implementation).

Related