How to make a fast Angular SEO-friendly app
By: Golosay Volodymyr
Tags: Server Side Rendering, Angular, Web Development, Firebase, SEO

When you do an Angular app, sooner or later you will think about SEO. Firstly I thought google anyway will parse my app because I heard it can. Days passed, but there was no result, so I started to think about what can I do. My goal was to be SEO-friendly with a high PageSpeed Insights score and do not spend all money in the world on servers. It looks like I found a way!

85 PageSpeed Insights score for the real Angular app with SSR

As a result, I have an approximately 80+ PageSpeed Insights score, it’s scalable, and it’s almost free to host it. Before achieving these results, I tried different choices:

1-st try: Prerendering

Firstly I tried prerendering for the most important pages. In theory, it should provide the best performance, but I had lots of issues with the PWA feature (Progressive Web App). If you don’t need PWA support, probably it’s the best choice for you.

2-nd try: SSR with Google cloud functions

With this approach, my app worked for a long period. Social networks and crawlers can parse any page of the app and it supports perfectly the PWA. But it has a super huge issue with performance — cold start. Cloud functions are stateless, and the execution environment is often initialized from scratch, which is called a cold start. It can take approximately 3–5 seconds before starting execution + SSR time 300–600ms. It’s not acceptable in 2020 to ask users to wait so long.

3-rd and last try: SSR with Google cloud Run

With Google Cloud Run visitors don’t have to wait for a cold start. The app is SEO-friendly and supports the PWA feature. Also, Google provides quite large free tier limits, so probably it will cost nothing for you to host an app.

So how to set up your app?

First of all, you need to install @nguniversal/express-engine . You can do it with angular schematics.

ng add @nguniversal/express-engine

Thanks to Angular schematics this is almost all you have to do with the app to have SSR. You already have an npm command, which will build a production build of the app and server.

Now we should configure deployment to Cloud Run. There are a few requirements:

You need to install Google Cloud SDK.

Also, you need to install Firebase CLI.

You should enable Cloud Run in google console and enable billing. Even with enabled billing, you still have free usage limits. While your app is not heavily loaded, it will cost nothing.

Cloud Run uses Docker images, so we should prepare it. First of all, we need to create a Docker file. For our case it will be pretty simple:

We are on the final step, now we should automate docker image building and deploying. To deal with that we can see different options, I prefer to use npm scripts.

Why multiple scripts? Because we need to do multiple staff.

We need to build a Docker image. We need to deploy it to Google cloud. We need to deploy our static files to the Firebase hosting, for fast getting assets.

Let’s add the first script to the package.json file.

"scripts": { "ng": "ng", "start": "ng serve", "build": "ng build --prod", ... "docker-build": "docker build ./ -t gcr.io/CLOUD_ACCOUNT/CLOUD_PROJECT && docker push gcr.io/CLOUD_ACCOUNT/CLOUD_PROJECT", }

Command npm run docker-build will build a docker image from our Dockerfile and add a tag. With this tag, we automatically can deploy it directly to Cloud Run.

Now we will add a docker-deploy script, which will deploy the created image to the cloud run.

"docker-deploy": "gcloud run deploy ssr --image gcr.io/CLOUD_ACCOUNT/CLOUD_PROJECT --platform managed --region us-central1"

The region can be different in your case. It depends on your initial configuration in the cloud console when you enable Cloud Run.

To run these scripts in single command we can create docker script:

"docker": "npm run docker-build && npm run docker-deploy

After deployment, you need to enable all traffic to the recently deployed revision in the google console.

Your container will be stored in Google Cloud Storage with 5GB space for free. After crossing this limit you have to pay for space, so probably you need to delete previous revisions if they are unnecessary.

Google Cloud Run SSR with Firebase hosting

At this point, you are done with Cloud Run part. In case you use Firebase hosting you need to do some additional settings and deployments.

First of all, we need to update firebase.json. Let’s add rewrites for all URLs to the Cloud Run service.

Almost done. Now we need to add some scripts more to the package.json file.

"back-up-index": "mv dist/ANGULAR_PROJECT_NAME/browser/index.html dist/ANGULAR_PROJECT_NAME/browser/_index.html", "restore-index": "mv dist/ANGULAR_PROJECT_NAME/browser/_index.html dist/ANGULAR_PROJECT_NAME/browser/index.html", "hosting-ssr": "npm run back-up-index && firebase deploy --token=FIREBASE_CI_TOKEN --project=FIREBASE_PROJECT_NAME --non-interactive --only hosting && npm run restore-index"

To use Cloud Run with Firebase don’t forget to remove index.html from dist folder. Because this file has more priority than firebase rewrites.

That’s why we need to rename index.html to _index.html before deploying.

To deploy assets to firebase hosting now we only need to run:

npm run build && npm run hosting-ssr

That’s it.

So to recap:

Google Cloud Run has a good balance between performance and price. If you need a faster solution — App Engine your choice or probably other services.

Use @nguniversal/express-engine for Server-Side Rendering for Angular app.

If you use Firebase hosting, don’t forget to remove index.html from hosting to not break rewrites.

Thanks for reading! If you have any thoughts about it or you know how to improve performance for an Angular SSR please feel free to comment.