Issue
A website I am working on is using Angular Universal 10.1.1 and is hosted on an IIS Server. An example of this happening is https://tragoa.com/welcome
If I navigate to the page from the root like foo.com then the website goes straight to the correct route foo.com/welcome without a 301 redirect. However, if I attempt to load foo.com/welcome directly then it will return that request with a 301 redirect to foo.com/welcome/ and that redirect request is returned with a 200 OK and then the trailing slash is stripped in the url bar. So it goes:
- Request with no trailing slash (/welcome)
- A 301 redirect returned to request with location including the trailing slash (/welcome/)
- 200 OK returned
- Trailing slash stripped in the URL bar in browser
The main issue here is the unwanted redirect
This only occurs when the page is using the prerendered HTML. It does not redirect if there is no prerendered index HTML for the given route.
This has caused issues with Lighthouse and some 3rd party redirect issues.
Does anyone know what could be causing this?
Some information that may help:
The base href of the html is <base href="/">
NOTE: I change this to not have the / then the routes get doubled up like foo.com/welcome/welcome
My server.ts get is this:
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { existsSync, readFileSync } from 'fs';
import 'localstorage-polyfill';
const domino = require('domino');
let distFolder = join(process.cwd(), '../spa/browser');
const template = readFileSync(join(distFolder, 'index.html')).toString();
const win = domino.createWindow(template);
win.Object = Object;
win.Math = Math;
global['window'] = win;
global['document'] = win.document;
global['branch'] = null;
global['object'] = win.object;
global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
global['localStorage'] = localStorage;
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
export function app(): express.Express {
const server = express();
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index.html';
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
server.get(
'*',
(req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env.PORT || 4000;
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
I use this command to create my site and then deploy the files "prerender": "ng build --configuration production && ng run my-app.spa:prerender:production"
Solution
This furthers the discoveries in the answer by @ahmed and offer a second (and possibly third solution).
Solution 1:
Before Angular Universal 11.1 there was no out-of-the-box hybrid rendering. This change made in 11.1 allowed for Angular Universal's renderer to handle both SSR and Prerendered pages if they exist. More information can be seen here: https://www.youtube.com/watch?v=RFwjJAZOzOA
Prior to this change there were two behaviors:
- The server running would return a non-prerendered page even though one exists
- The server would return a 301 Redirect to route to the path of the file with a trailing slash as noted in the question
With this hybrid rendering update if you route traffic as I have in the web.config
or similarly for Apache servers the Angular Universal rendering engine will simply return the prerendered page if it exists or SSR it. No 301 redirects to the file path needed.
Solution 2
Some people have had success adding redirect: false
as mentioned in this answer: https://stackoverflow.com/a/60533554/5832236 This is because the prerendered index.html
files are static files.
That property is part of express.static and server-static This is where that redirect happens: https://github.com/expressjs/serve-static/blob/9b5a12a76f4d70530d2d2a8c7742e9158ed3c0a4/index.js#L202 and you can test if it is this redirecting you by changing that value temporarily in the node_modules
My suggested answer
Update Angular Universal to above 11.1 and make sure you're routing all traffic to the main.js
server file to handle everything.
Here's the web.config
that worked for me in addition to the server.ts
in the question:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="true" />
<handlers>
<add name="iisnode" path="server/main.js" verb="*" modules="iisnode"/>
</handlers>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="100.00:00:00" />
<mimeMap fileExtension="woff" mimeType="application/font-woff" />
<mimeMap fileExtension="woff2" mimeType="application/font-woff2" />
<mimeMap fileExtension=".json" mimeType="application/json" />
</staticContent>
<rewrite>
<rules>
<rule name="MainRule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="server/main.js" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>
Answered By - Eric Belisle Giddings
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.