Live Reloading An Angular 2+ App Behind NGINX

May 14, 2018  |  5 minutes to read


Not my snappiest title, but this topic is too near to my heart to obscure with puns. Tight feedback loops are one of my favorite aspects of front-end development.


One of my favorite features of Angular 2+ is the Angular CLI. The CLI cleanly replaces the hodge-podge of custom gulp, grunt, or npm scripts that power the build behind every AngularJS (Angular 1) app.

Perhaps the best feature of the CLI is the ng serve command. This command builds the app, serves it (using webpack-dev-server), and then watches for changes to source files, incrementally rebuilding and reloading the app when changes are detected.

Unfortunately, it’s pretty easy to break some features of this command. For example, say you use a local webserver like NGINX to hide your app behind a URL like https://localhost/my-app. This common setup allows the app to communicate with a server hosted at a different domain or port without causing cross-origin issues. However, it also blocks some of the traffic the ng serve feature uses to trigger the live reload feature, so your app will lose the ability to refresh itself when its source files are changed.

Fortunately, it’s possible to proxy this live reload traffic and regain live reload functionality.

The NGINX G inside the Angular shield
Another Angular shield mashup icon because there's not enough on the internet already.

1. Serve your Angular 2+ app from a unique port

First, update your Angular project’s angular.json file to ng serve on a unique port - one that isn’t already in use by another Angular app. This step is optional if you only plan on working on a single Angular 2+ app.

{
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    "projects": {
        "my-project": {
            "architect": {
                "serve": {
                    "options": {
                        "port": 4201
                    }
                }
            }
        }
    }
}

Note: only relevant properties are shown in the example above.

2. Reverse proxy traffic to this port using NGINX

Next, update your nginx.config to map a friendly URL to your app:

# proxy my-project traffic
location ^~ /my-project/ {
    proxy_pass http://127.0.0.1:4201/;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_http_version 1.1;
    proxy_cache_bypass $http_upgrade;
}

After restarting or reloading your NGINX instance (the simplest way is to run nginx -s reload) and starting up your Angular build (ng serve), you should be able to navigate to https://localhost/my-project and view your app.

One quick tip: make sure you update the <base> tag in your app’s index.html to point to your app’s new home. Continuing the example from above, this tag should look like <base href="/my-project/">. If you forget to update your <base> tag, you’ll end up with a blank screen and a lot of confusing errors in your developer tools.

3. Proxy live reload traffic

This is the most important step. The live reload feature of Angular 2+ communicates on the path https://domain:port/sockjs-node/, so we need to forward along this traffic as well:

 # proxy traffic for ng serve live reload
location ^~ /sockjs-node/ {
    proxy_pass http://127.0.0.1:4201;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_http_version 1.1;
    proxy_cache_bypass $http_upgrade;
}

You’ll need to restart or reload your NGINX instance again after making these changes.

4. Profit

Refresh your app. With your app on one screen and your editor on the other, make a change to a source file and hit Ctrl+S. Your app will refresh just like it did in pre-NGINX days!

An NGINX G acting like Pacman, about to eat the Angular logo
... wakka wakka wakka wakka wakka wakka wakka wakka...

A caveat

The observant reader will notice that the /sockjs-node/ NGINX proxy above is hardcoded to 127.0.0.1:4201, which will only work for one app - the app hosted at port 4201. What if you have multiple Angular 2+ apps on the go? In this case, your NGINX proxies would look something like this:

# proxy my-project-1 traffic
location ^~ /my-project-1/ {
    proxy_pass http://127.0.0.1:4201/;
    # etc...
}

# proxy my-project-2 traffic
location ^~ /my-project-2/ {
    proxy_pass http://127.0.0.1:4202/;
    # etc...
}

# proxy my-project-3 traffic
location ^~ /my-project-3/ {
    proxy_pass http://127.0.0.1:4203/;
    # etc...
}

All of these apps will attempt to use /sockjs-node/ to communicate with their respective ng serve live reload server. Unfortunately, our /sockjs-node/ NGINX proxy above will only route traffic to one of these apps - meaning you can only use the live reload with one app at a time. In order to start working on a different app, you’ll have to manually edit the port in your NGINX config. This is a pain, but it’s better than not having live reload at all.

Know a way around this? Answer my question on Stack Overflow!


Other posts you may enjoy:

I built a weird keyboard

June 26, 2023  |  14 minutes to read

Wordle Bot

January 25, 2022  |  6 minutes to read

Herding Gits

August 26, 2021  |  2 minutes to read

It's finally here! 🎉

May 7, 2021  |  1 minute to read

Capturing Alexa Errors with Sentry and GitLab

November 18, 2020  |  4 minutes to read