Vue.js / NGINX / HRM (webpack devserver) – websocket & sockjs-node 404 errors

I am currently developing a Vue.js SPA locally and have hit various problems when trying to serve the vue app behind a NGINX reverse proxy. Specifically, I’m getting many errors related to either websockets, or sockjs-node, which kills the Hot-Reload functionality. (404 Not Found)’s.

My goal is to have this SPA running on a bare-bones Node.js server to dish out the static files on the root of the server, IE hosted at: http://localhost:6200/. Through a NGINX reverse proxy, I can then access the server via VHOST: http://localhost/my-app/, which routes to http://localhost:6200/, as shown above.

Firstly I run the vue.js app using npm run serve and then access the site though the NGINX proxy: http://localhost/my-app/. The site loads without any functional issues, however hot reloading does not work.

Looking at the developer console, there are numerous GET requests to:
http://localhost/sockjs-node/info?t=1605955915882 which is obviously wrong, as we are currently accessing: http://localhost/my-app/.... Below is my current configurations prior to playing with various things in attempt to get it to work:

My NGINX configuration:

upstream nodefrontend_myapp {
    server localhost:6200;
}

# Redirect HTTP to HTTPS
server {
    listen 80 default_server;
    listen (::):80 default_server;
    server_name _;
    
    # 307 temporary redirect
    return 307 https://$host$request_uri; 
}

# HTTPS Proxy
server {
    listen 443 ssl http2;
    server_name localhost;
  
    # Proxy "my-app" URI traffic to correct server
    location /my-app/ {
        proxy_buffering off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # For websockets
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";

        # Proxy to my-app's root
        proxy_pass http://nodefrontend_myapp/;
    }
}

The vue.config.js configuration:

module.exports = {
    // options...

    // I want the site to be accessible *RELATIVE* to the current URL.
    // This will make vue.js resources have no leading slash, eg: <link> href="css/file123.css" </link>
    // And should allow me to fetch resources on the current VHOST: eg: http://localhost/my-app/css/file123.css
    publicPath: "",
    
    devServer: {
        //disableHostCheck: true,
        https: false,
        public: "0.0.0.0",
        host: "localhost" ,
        port: 6200,
        sockPath: "", /////////// This has been causing me issues

    },

}

So after looking at the webpack docs, it seemed like I should updated the sockPath in the vue.config.js, so I did:

sockPath: "/my-app/sockjs-node",

This stopped the 404 GET errors with sockjs-node, but then different errors appeared regarding websockets now not able to find its resources at: ws://localhost/my-app/sockjs-node/147/0dj5ee1p/websocket.

To check if the issue was the webserver or NGINX, I ran wscat -c ws://localhost:6200/my-app/sockjs-node/147/0dj5ee1p/websocket which DID work. This indicated NGINX was somehow killing the websocket. To double check, I tested the websocket through NGINX: wscat -c ws://localhost/my-app/sockjs-node/147/0dj5ee1p/websocket, which DID NOT WORK.

After hours of playing with the configurations and NGINX, I found an article online saying that I should change my NGINX proxy_pass URL to remove the trailing slash. I changed it from this:

proxy_pass http://nodefrontend_myapp/;

to this:

proxy_pass http://nodefrontend_myapp;

And retested the websocket via wscat -c ws://localhost/my-app/sockjs-node/147/0dj5ee1p/websocket and it worked!. However.. After removing that leading slash, Now the website doesnt load. My suspisions are the vue.config.js publicPath, but Im at a loss.

Has anyone been able to run a Vue SPA on a servers root, have an NGINX reverse proxy (such as above) access the SPA (via VHOST) WITH HRM working???

======

I did find a solution, but I dont like it:

  • Host the SPA statically on its server under a sub-route: http://localhost:6200/my-app
  • Make the NGINX reverse proxy_pass goto: proxy_pass http://localhost:6200/my-app/
  • Set the vue.config.js file’s publicPath to: publicPath: "/my-app"
  • Do NOT set the vue.config.js’s sockPath property (Delete it).

The key to this configuration is that the vue.js front-end browser knows its located at /my-app, can send requests to the server hosted at /my-app, and NGINX does not really modify any URIs.

Although this configuration works, I dont like that the webserver MUST host the static files on a sub-route and the browser MUST know its vhost (publicPath) – Unless this is standard practice?? I am semi-okay with the browser knowing its publicPath (/my-app/) as it needs it for history mode.

PS. In production, my origional configuration with a relative publicPath works fine. Its just HRM which breaks in development.

Anyone able to shed some light on the issue?
Cheers