N
Novu5mo ago
k0zn4n3j4

socket.io endpoint on reverse proxy?

I have novu running from the docker-compose file behind an nginx reverse proxy (redis and mongodb database are external cloud services). I am getting failed connection to websockets. My urls are: https://novu.our.website/api https://novu.our.website/web https://novu.our.website/ws I noticed that there is a call to a /socket.io/ endpoint so I added that to my nginx config (pictured). Now I send the token but get back an invalid namespace error. Can anyone help?
No description
15 Replies
k0zn4n3j4
k0zn4n3j45mo ago
I've tried with caddy server as well and still failure issues - in any case the reverse proxy is forwarding correctly, there is just a failure at the application level to sync socket.io namespaces. Is this a problem on Novu's end with configuration? I have followed the instructions in the documentation exactly. I'm leaving this up for the next person, and also so that you can potentially add this to Novu's documentation. The solution is that you must proxy pass <websocket_url>/socket.io to localhost:<websocket_port>/socket.io. I also switched to subdomains rather than using relative paths but I'm not sure if that played a part in the solution. It depends on how the namespace is constructed. In any case I'm sure the team can work it out.
Novu_Bot
Novu_Bot5mo ago
@k0zn4n3j4, you just advanced to level 1!
harrisyn
harrisyn4mo ago
I just migrated my setup to a new server and set it up with Caddy, using relative paths as well and it is working fine. Let me know if you want to match configs for anyone else who stumbles upon this. This is my example caddyfile that works
{
#debug
# Global options block. Entirely optional, https is on by default
# Optional email key for lets encrypt, uncomment the below to set the email i.e. email youremail@domain.tld
# email xxxx@xxxx.xx
}

(static) {
@static {
file
path *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.woff2 *.json
}
header @static Cache-Control max-age=5184000
}

(security) {
header {
# enable HSTS
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
# disable clients from sniffing the media type
X-Content-Type-Options nosniff
# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade
}
}

(404) {
handle_errors {
@404 {
expression {http.error.status_code} == 404
}
handle @404 {
respond * "Not found" 404
}
}
}

yourdomain.com {
reverse_proxy web:4200
@ws {
header Connection *Upgrade*
header Upgrade websocket
}
handle /api/* {
# CORS settings
header Access-Control-Allow-Origin *
header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE'
header Access-Control-Allow-Headers 'Content-Type, Authorization'

# Proxy settings, strip /api prefix before proxying
reverse_proxy api:3000
}

reverse_proxy @ws ws:3002
reverse_proxy /widget widget:4500
}
{
#debug
# Global options block. Entirely optional, https is on by default
# Optional email key for lets encrypt, uncomment the below to set the email i.e. email youremail@domain.tld
# email xxxx@xxxx.xx
}

(static) {
@static {
file
path *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.woff2 *.json
}
header @static Cache-Control max-age=5184000
}

(security) {
header {
# enable HSTS
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
# disable clients from sniffing the media type
X-Content-Type-Options nosniff
# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade
}
}

(404) {
handle_errors {
@404 {
expression {http.error.status_code} == 404
}
handle @404 {
respond * "Not found" 404
}
}
}

yourdomain.com {
reverse_proxy web:4200
@ws {
header Connection *Upgrade*
header Upgrade websocket
}
handle /api/* {
# CORS settings
header Access-Control-Allow-Origin *
header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE'
header Access-Control-Allow-Headers 'Content-Type, Authorization'

# Proxy settings, strip /api prefix before proxying
reverse_proxy api:3000
}

reverse_proxy @ws ws:3002
reverse_proxy /widget widget:4500
}
this config works with running docker containers, replace api, widget, ws with the actual names of your containers or addresses if you aren't running using the docker-compose file. the Context paths in the env are configured as
# Context Paths
# Only needed for setups with reverse-proxies
GLOBAL_CONTEXT_PATH=
WEB_CONTEXT_PATH=
API_CONTEXT_PATH=api
WS_CONTEXT_PATH=ws
WIDGET_CONTEXT_PATH=widget
# Context Paths
# Only needed for setups with reverse-proxies
GLOBAL_CONTEXT_PATH=
WEB_CONTEXT_PATH=
API_CONTEXT_PATH=api
WS_CONTEXT_PATH=ws
WIDGET_CONTEXT_PATH=widget
@unicodeveloper @Emil might help to add this to the documentation
Prosper
Prosper4mo ago
Please feel free to send a PR to the docs repo @harrisyn
Stephen
Stephen4mo ago
This still doesn't work for reverse proxy with nginx. Both web and ws client is not working. I'm planning to raise a PR for reverse proxy setup after i finish my testing
harrisyn
harrisyn4mo ago
@Stephen this is for caddy, not nginx. I was previously using similar for nginx, so if you are interested I can dig that up as well.
Stephen
Stephen4mo ago
Yes I understand but I tried to configure with nginx and ended up with namespace error as @k0zn4n3j4 mentioned. If you have done it can you please show the config if possible FYI for my requirement nginx serves as a reverse proxy and novu services reside in the path /novu. E.g api path is mydomain.com/novu/api ws path is mydomain.com/novu/ws
harrisyn
harrisyn4mo ago
are you running docker containers or a local install?
Stephen
Stephen4mo ago
Docker containers through Docker compose
harrisyn
harrisyn4mo ago
ok
harrisyn
harrisyn4mo ago
the attached is the base of my nginx
harrisyn
harrisyn4mo ago
my env context is same as above the "yourdomain.tld" is what you need to change I am curious, is there a specific reason you want to maintain /novu/* as your proxy paths? instead of using a subdomain?
Stephen
Stephen4mo ago
Ok this is the same workaround that I have done currently. To make the websocket work I'm using /socket.io/ path in nginx. I do not see any specific reason to maintain /novu path. but we have some other app running at the root of the domain so our lead wants to use a different path . Anyways thanks @harrisyn Subdomain is also a good idea. But isn't it better to put all our services behind nginx as reverse proxy or load balancer. By this way we do not need subdomains ?
harrisyn
harrisyn4mo ago
either way works, I find it cleaner to simply create a subdomain and point to the same IP, then use nginx to proxy it to the needed service. if you created a novu.yourdomain.tld, you could simply replace the server_name placeholder and you will be good to go (asides making sure you have the right certs for it) You could also simply upd ate the location paths i.e. instead of / you do /novu/ etc. it should give the same result
JSP
JSP4mo ago
Thanks @harrisyn, for sharing the nginx file. One more thing to share if someone misses it that you also need to update the Root URLs in the env file to use direct paths instead of ports.
# Root URL
REACT_APP_WS_URL=$HOST_NAME/ws # updated
# Uncomment this one when deploying Novu in the local environment
# as Web app local Dockerfile will have to load this to be used.
# Deployment version doesn't need as we inject it with API_ROOT_URL value.
#REACT_APP_API_URL=$HOST_NAME:3000
API_ROOT_URL=$HOST_NAME/api # updated
DISABLE_USER_REGISTRATION=false
FRONT_BASE_URL=$HOST_NAME:4200
WIDGET_EMBED_PATH=$HOST_NAME:4701/embed.umd.min.js
WIDGET_URL=$HOST_NAME/widget # updated
# Root URL
REACT_APP_WS_URL=$HOST_NAME/ws # updated
# Uncomment this one when deploying Novu in the local environment
# as Web app local Dockerfile will have to load this to be used.
# Deployment version doesn't need as we inject it with API_ROOT_URL value.
#REACT_APP_API_URL=$HOST_NAME:3000
API_ROOT_URL=$HOST_NAME/api # updated
DISABLE_USER_REGISTRATION=false
FRONT_BASE_URL=$HOST_NAME:4200
WIDGET_EMBED_PATH=$HOST_NAME:4701/embed.umd.min.js
WIDGET_URL=$HOST_NAME/widget # updated