I am currently developing multiple Vue.js SPAs locally and have hit various problems when trying to serve these apps behind an NGINX reverse proxy. Specifically, I'm getting many errors related to either websockets, or sockjs-node, which kills the Hot-Reload functionality for the vue apps. (404 Not Found)'s.
Each of these SPAs are running on their own bare-bones Node.js server which dish out the static files on the root of their server. They should be accessed via the NGINX reverse proxy:
http://localhost:6200/ -> http://localhost/my-app1/
http://localhost:6205/ -> http://localhost/my-app2/
http://localhost:6210/ -> http://localhost/my-app3/
For development, I run all of the vue.js apps using npm run serve and then access those sites though the NGINX proxy: http://localhost/my-appX/. The sites loads without any functional issues, however hot reloading does not work.
After Looking at the developer console in the browser, there are numerous GET requests to EG:
http://localhost/sockjs-node/info?t=1605955915882. This URL is obviously wrong, as we are currently accessing the site through the proxy - I would have expected to see http://localhost/my-appX/sockjs-node/info?t=1605955915882. Below is my current configurations prior to playing with various things in attempt to get it to work:
My NGINX configuration:
upstream nodefrontend_myapp1 {
server localhost:6200;
}
upstream nodefrontend_myapp2 {
server localhost:6205;
}
upstream nodefrontend_myapp3 {
server localhost:6210;
}
# 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-app1/ {
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_myapp1/;
}
# Note: several other sites are running via the below routes
location /my-app2/ {
proxy_pass http://nodefrontend_myapp2/;
# ...
}
location /my-app3/ {
proxy_pass http://nodefrontend_myapp3/;
# ...
}
}
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 each apps vue.config.js, so I did:
sockPath: "/my-appX/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-appX/sockjs-node/147/0dj5ee1p/websocket.
To check if the issue was the webserver or NGINX, I ran wscat -c ws://localhost:6200/my-appX/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-appX/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_myappX/;
to this:
proxy_pass http://nodefrontend_myappX;
And retested the websocket via wscat -c ws://localhost/my-appX/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 I'm at a loss.
Has anyone been able to run multiple Vue.js sites behind an NGINX reverse proxy (as VHOSTS), and have the hot reload functionality 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
sockPathproperty (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-appX/) 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
========= UPDATE 1) found fix, not sure if this is the only fix.
I found out my vue.config.js, devserver "public" property was set incorrectly. According to the webpack devserver documentation, the devserver client ( / sockjs-node) code running in the browser will guess the servers location by looking at its window.location variable. However, since I'm accessing the server via a proxy, it looks like it cant handle that by default. SO their solution is to set the "public" property to make the client talk DIRECTLY to the server (cross origin requests)... A bit of a hack if you ask me.
So the client sockjs-node now talks directly from: http://localhost/my-appX/ to http://lcoalhost:62xx/.
This is what (eg) my-app1's vue.config.js now looks like:
module.exports = {
// options...
publicPath: "",
devServer: {
//disableHostCheck: true,
https: false,
public: "localhost:6200", //// This line makes the client browser talk DIRECTLY to this address / server for hot reloading.
host: "0.0.0.0" ,
port: 6200,
},
}
With this said, Everything now works as expected via http. But updating NGINX to upgrade requests to https, causes the webpack-devserver sockjs-node to spit out CORS errors. This is because the client/browser is upgraded to HTTPS, but the webpack-devserver is only http. However to fix this, I simply made the vue.config.js devserver https, and the CORS errors disappear.
This seems more like an issue / limitation with the webpack-devserver sockjs-node than a stackoverflow issue. Will leave the issue open here.
RefererHTTP request header?