Sekrab Garage

Angular, Firebase, and Netlify solutions

Fixing the trailing slash in Static Site Generation

AngularTip November 7, 22
Series:

Trailing slashes

An issue with prerendering, Angular or other, is that it creates an index.html file inside a route folder. The default behavior of different hosts is to add a trailing slash to serve that static file. Can we fix that?
  1. one month ago
    Fixing the trailing slash in Static Site Generation
  2. 30 days ago
    Express Slashing Syndrome, the trailing slash and other topics

The first call to our SPA preferred URL: domain.com/route will 301 redirect to domain.com/route/ with a trailing slash. When hydrated, it would rewrite location back to the original, without registering any network redirects. According to a very old article on Google Search Central, it has no effect on searchability. Nevertheless, it made a lot of people nervous since the search console displays the following when trying to crawl prerendered URLs.

Search console dipping with redirects

Let’s address this issue and try to fix it.

Prerendering

There are multiple ways to generate pre-rendered pages, one of them is through the built-in Angular builder,

ng run prerender

Another is through Express. Read about how to Prerender Angular in Express in a previous article.

All of the normal ways create index.html pages with the content, inside folders reflecting the route:

domain/{route}/index.html

That in the world of hosting means if user browses to domain/route it automatically redirects to domain/route/ and displays the index file. That redirect is a 301.

Image on Sekrab Garage

Following are solutions to this problem

Angular solution: out of the box

Here is how Angular defines the server.ts to take care of this problem:

// server.ts
server.get('*.*', express.static(distFolder, {
  maxAge: '1y'
}));

// All regular routes use the Universal CommonEngine
server.get('*', (req, res) => {
  res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});

If you pay close attention, this has a server.get(*.*) for all static files (JavaScript and CSS). The pre-rendered pages are served by the second rule.

In ngExpressEngine, the CommonEngine checks if the page path already exists, it serves the file, else rolls back to SSR generated main.js. This is not always an option.

// CommonEngine code
async render(opts: RenderOptions): Promise<string> {
	  // ...
    if (opts.publicPath && opts.documentFilePath && opts.url !== undefined) {
       // ...
        if (pageExists) {
          // Serve pre-rendered page.
          return fs.promises.readFile(pagePath, 'utf-8');
        }
      }
    }
}

One instance that won't work is cloud hosting that prefers static files first. Like Firebase or Netlify.

Note: in order to test the behavior, keep the Network tab open, and turn off JavaScript as well. You will spot the 301 right at the top.

Firebase solution: trailingSlash

What happens is that it looks for the physical file, before it serves the ssr serverless function (or the general rewrite to index file if you are not using SSR). The default behavior is to add a trailing slash when it finds the index.html.

Firebase hosting configuration: trailingSlash can take care of this issue.

// firebase.json 
{
  "hosting": {
    "public": "functions/public",
     // Removes trailing slashes from URLs
		 "trailingSlash": false
     // ...
  }
}

This displays /route/index.html as /route, without a 301. But it would redirect /route/ to /route with a 301. If you never use /route/ anywhere, not in your sitemaps, nor inline links, nor when you share on social media, you should be fine. If however it still bugs you, there is another solution to put this to rest, that solution works well in Express servers as well. We will discuss this solution in the next episode.

Netlify solution, or lack thereof

Physical file preference will kick in first, before it runs the netlify.toml hosting file. The default is to add the / if it finds an index.html. According to their documentation there is a way to unify all links to have a trailing slash. In an Angular app, this is not what we are looking for. It rather is the other way around.

There is another default behavior of Netlify: to look for /route.html if user browses to /route. And that is what we want. In fact, that is the only way it's done on Netlify. The solution then is to rewrite our prerender function to generate /route.html instead of /route/index.html. This also will be covered in the next episode along with Express server solution.

Note: Netlify documentation states that you need to enable Pretty Urls to make use of this behavior. I tried it. It works without tampering with settings.

Stay tuned for the next episode coming soon.

  1. one month ago
    Fixing the trailing slash in Static Site Generation
  2. 30 days ago
    Express Slashing Syndrome, the trailing slash and other topics