Skip to content

document running nuxt dev server behind nginx + SSL + proxy #12003

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
quartze opened this issue Oct 14, 2021 · 41 comments
Closed

document running nuxt dev server behind nginx + SSL + proxy #12003

quartze opened this issue Oct 14, 2021 · 41 comments

Comments

@quartze
Copy link

quartze commented Oct 14, 2021

Environment


  • Operating System: Linux
  • Node Version: v16.11.1
  • Nuxt Version: 3-3.0.0-27235451.ece0e01
  • Package Manager: Yarn
  • Bundler: Webpack
  • User Config: -
  • Runtime Modules: -
  • Build Modules: -

Describe the bug

Trying to start development server for people on nginx engine. When i'm starting with cli nuxt, i'm getting infinite reloads on page with error WebSocket connection to wss://example.com:24678/_nuxt/ failed`. Is there any chance to start nuxt with proxy on nginx?

Reproduction

Just cloned default project.

Additional context

No response

Logs

Nothing in nuxt3
@quartze
Copy link
Author

quartze commented Oct 14, 2021

I think it can be prevented if we change connections from domain to ip address.
Anyway, can i modify it now in nuxt.config.js?

@lustremedia
Copy link
Contributor

How does your nginx config look like?

@quartze
Copy link
Author

quartze commented Oct 14, 2021

@lustremedia

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
	worker_connections 768;
}

http {

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;


	include /etc/nginx/mime.types;
	default_type application/octet-stream;


	ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	gzip on;

server {                                                                                                         
        listen 80;
        listen 443 ssl;
        server_name dev.domainname.pl;

        gzip on;
        gzip_types	text/plain application/xml;
        gzip_proxied    no-cache no-store private expired auth;
        gzip_min_length 1000;

        # This is a cache for SSL connections
        ssl_session_cache shared:le_nginx_SSL:1m;
        ssl_session_timeout 1440m;

        rewrite ^/(.*)/$ /$1 permanent;

        location / {
                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;
                proxy_redirect          off;
                proxy_buffering         on;
                proxy_cache_valid	200 1d;
                proxy_cache_use_stale   error timeout invalid_header updating http_500 http_502 http_503 http_504;

                proxy_pass              http://localhost:3000;
                proxy_read_timeout	1m;
                proxy_connect_timeout   1m;
        }
}
	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

@Diizzayy
Copy link
Member

Diizzayy commented Oct 19, 2021

@quartze A few changes have to be made to allow development to run smoothly as expected behind your NGINX Proxy with SSL.

Everything below was tested and works properly, so let me know if you encounter any issues.

  1. Modify your nuxt config to set vite's hmr clientPort to 443(default https / ssl port) and path to "hmr/".
    Explanation: The vite server uses port 24678 for HMR by default, we are not going to change this.
    Setting clientPort to 443 will allow us to then use NGINX to proxy the request back to vite.
import { defineNuxtConfig } from "nuxt3";

export default defineNuxtConfig({
  vite: {
    server: {
      hmr: {
        protocol: "wss",
        clientPort: 443,
        path: "hmr/",
      },
    },
  },
});
  1. Modify the NGINX conf for you site
    Firstly, web sockets require HTTP/1.1, so the following is required in either your server or location block
proxy_http_version 1.1; # required
proxy_set_header Upgrade $http_upgrade; # required
proxy_set_header Connection "upgrade"; # required

Add location block to match the hmr path /hmr that we set in step 1.

location /_nuxt/hmr/ {
        proxy_pass http://localhost:24678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
}

@quartze I went ahead and modified your server block as needed:
ps: I also made substantial improvements to your gzip compression, you should now get much faster load times ⚡️.

server {                                                                                                         
    listen 80;
    listen 443 ssl;
    server_name dev.domainname.pl;

	gzip on;
	gzip_disable "msie6";
	gzip_vary on;
	gzip_proxied any;
	gzip_comp_level 6;
	gzip_buffers 16 8k;
	gzip_http_version 1.1;
	gzip_min_length 256;

	gzip_types
		application/atom+xml
		application/geo+json
		application/javascript
		application/x-javascript
		application/json
		application/ld+json
		application/manifest+json
		application/rdf+xml
		application/rss+xml
		application/xhtml+xml
		application/xml
		font/eot
		font/otf
		font/ttf
		image/svg+xml
		text/css
		text/javascript
		text/plain
		text/xml;

    # This is a cache for SSL connections
    # ssl_session_cache shared:le_nginx_SSL:1m;
    # ssl_session_timeout 1440m;

    rewrite ^/(.*) /$1 break;

    location / {
        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;
        proxy_redirect          off;
        proxy_buffering         on;
        proxy_cache_valid	200 1d;
        proxy_cache_use_stale   error timeout invalid_header updating http_500 http_502 http_503 http_504;

        proxy_pass              http://localhost:3000;
        proxy_read_timeout	1m;
        proxy_connect_timeout   1m;
    }

    location /_nuxt/hmr/ {
        proxy_pass http://localhost:24678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

@quartze
Copy link
Author

quartze commented Oct 20, 2021

@Diizzayy working perfectly. Thanks for rewriting my code.
Can you add this to docs?

@bubooon
Copy link

bubooon commented Dec 19, 2021

Hey, I had the same issue and this helped me.
Makes sense to create a guide page with this setup similar to the previous version.

@Diizzayy
Copy link
Member

@quartze @bubooon having this setup added to the docs is a good idea. I'll get started on a PR.

@wstee
Copy link

wstee commented Apr 1, 2022

Trying to start production server.The nuxt throw an error when i let http redirect to https on nginx.
1648804090(1)

@ivan-mihalic
Copy link

Hi, I had the same issue with docker. I have nginx as proxy for https
here is my nginx config (I am not the author, my colleague made it)
port 80 is served by default so I have proxy for 443 and 24678 (WS)
if you forced ws protocol on https, then browser throws error due to trying communicate to unsecured endpoint from secured

https-proxy.conf

server {
    listen 443 ssl http2;
    server_name _;

    location / {
        proxy_pass http://app:80;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    ssl_protocols TLSv1.2;

    ssl_certificate     /var/https-proxy-cert.cert;
    ssl_certificate_key /var/https-proxy-cert.key;

    access_log /dev/stdout;
    error_log /dev/stderr;
}

server {
    listen 24678 ssl http2;
    server_name _;

    location / {
        proxy_pass http://app:24678;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    ssl_protocols TLSv1.2;

    ssl_certificate     /var/https-proxy-cert.cert;
    ssl_certificate_key /var/https-proxy-cert.key;

    access_log /dev/stdout;
    error_log /dev/stderr;
}

and nuxt.config.js

export default defineNuxtConfig({
  vite: {
    server: {
      hmr: {
        protocol: 'wss',
      },
    },
  }
});

@jamesallan93
Copy link

I have added ssl to my server name and nuxt3 was a blank page when using npm run dev.
In nuxt config i did what @Diizzayy commented.

vite: {
    server: {
      hmr: {
        clientPort: 443,
        path: "hmr/",
        protocol: "wss",
      },
    },
  }

And also added the " location /_nuxt/hmr/ " in nginx.
After all that it worked.
@Diizzayy Saved the day. Huge thx.

@kosmeln
Copy link

kosmeln commented Aug 11, 2022

The config that @Diizzayy and @jamesallan93 mentioned was working perfect until RC-7 :(
Let me know guys if you have another workaround please!

@Diizzayy
Copy link
Member

@kosmeln I'll have another look at the setup and update you guys accordingly on what changes may need to be made here

@kosmeln
Copy link

kosmeln commented Aug 11, 2022

Thank you in advance @Diizzayy !

@Diizzayy
Copy link
Member

@kosmeln I just got around to checking this out, and the setup I mentioned above works just fine still.

Perhaps you can give me more details about your exact setup and what's not working properly

@kosmeln
Copy link

kosmeln commented Aug 15, 2022

Sorry for late response @Diizzayy .
Before upgrading the hmr requests (if looking at dev tools) showed
Request URL: ws://localhost: 24678/_nuxt/hmr/

After upgrade the url changed to
Request URL: https://localhost/hmr/

So I had to tune vite server.hmr.path setting from

hmr: {
    protocol: 'wss',
    host: 'localhost',
    clientPort: 443,
    path: 'hmr/',
}

as you suggested earlier to

hmr: {
    protocol: 'wss',
    host: 'localhost',
    path: '_nuxt/hmr/',
}

Still not sure if this is correct and what has changed with the upgrade. Please let me know if you have any insights or what I can check further. Thanks in advance.

@cwe-dev
Copy link

cwe-dev commented Aug 17, 2022

Hi guys.
It work for me:
Nuxt 3 \ vite proxy.
I generate key and sert with mkcert.
Change package script to:
"dev": "nuxt dev --https --ssl-key key.pem --ssl-cert cert.pem"

https:localhost:3000 work nice. vite proxy work too.

@wangrongding
Copy link

Waiting for a good answer🤖

@HendrikJan HendrikJan mentioned this issue Jan 19, 2023
4 tasks
@danielroe danielroe added the 3.x label Jan 19, 2023
@danielroe danielroe transferred this issue from nuxt/framework Jan 19, 2023
@giovannimanzoni
Copy link

The final solution: open your mind and reflect on what you really need

After a week spent trying to get Nuxt3 working with SSL with Nginx for development.... I realized that I have no need of Nginx !!! My goal is to get Nuxt3 working in a domain with SSL. It was my principle that it should work behind the reverse proxy, but, after all, it is just a whim. so:

package.json

"scripts": {
    ...
    "dev": "nuxi dev --port YOUR_PORT --host YOUR_IP --https",  <-- use IP and --https !!
   ...
}

In your Unbound conf or hosts file add reference to your domain and your YOUR_IP

now open the browser on https://yourdomain.tld:YOUR_PORT :

  • no hmr problems
  • no wss problems
  • no crazy Nginx configuration
  • no infinite console errors
  • no problem of any kind
  • hot reloading work, all work as expected.

@danielroe danielroe removed the nuxt3 label Jan 22, 2023
@HendrikJan
Copy link

So as I see, there is a reasonable solution for this — should we maybe close this and add a small PR to add some docs to the "Going further" part in the nuxt docs?

It is reasonable only in certain circumstances. We are using Docker and a reverse proxy container to make sure our development setup is as close to our production setup.
The option to remove the reverse proxy and let Nuxt do the ssl would be a lot different from our production and therefore not reasonable for us.

(I read somewhere that letting Nuxt do ssl in production is not safe, am I right?)

@giovannimanzoni
Copy link

Hi, this is my definitive and working setup for Nuxt3 with vite and Nginx

nuxt.config.js

server: {
  hmr: {
    protocol: 'wss',
    clientPort: 443,
    path: 'hmr/',
  },
},

package.json

"dev": "nuxt dev --host localhost --port 3035"

domain.conf

map $sent_http_content_type $expires {
  "text/html"                 epoch;
  "text/html; charset=utf-8"  epoch;
  default                     7d;
}

upstream mydomain {
  zone upstreams 64K;
  server localhost:3035 max_fails=1 fail_timeout=2s;
  keepalive 2;
}

server{
  listen       443 ssl http2;
  server_name  mydomain.lan;
  ...
  rewrite ^/(.*) /$1 break;
  
  location /_nuxt/hmr/ {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_pass http://localhost:24678;
  }

  location / {
    expires $expires;
    sendfile on;
    sendfile_max_chunk 512k;        
    aio on;
    proxy_max_temp_file_size 0;
    proxy_pass http://mydomain;
    proxy_next_upstream error timeout http_500;
    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;  # PLEASE !! DO NOT ADD THIS !!!
    proxy_redirect          off;
    proxy_buffering         on;
    proxy_cache_valid       200 1d;
    proxy_cache_use_stale   error timeout invalid_header updating http_500 http_502 http_503 http_504;
    proxy_cache_key         $uri$is_args$args;
    proxy_read_timeout      1m;
    proxy_connect_timeout   1m;
  }
}

enjoy :)

@slavanossar
Copy link

slavanossar commented Sep 28, 2023

Just leaving a note for anyone who may be having issues with this solution after upgrading to nuxt@3.7.4, I was experiencing reload loops and WSS connection errors.

It seems like specifying a vite.server.hmr config along with a nginx proxy_pass directive is no longer required to get HMR working on a SSL dev server. Removing the HMR config from nuxt.config.ts and removing the location block for /_nuxt/hmr from the nginx config fixed the issue.

@tibesyc
Copy link

tibesyc commented Sep 28, 2023

App work when removing proxy_pass from nginx config and vite.server.hmr from nuxt.config.ts
But HMR not work for me

Need to add this in server block on nginx config

location /_nuxt/ {
    proxy_pass http://{localhost or container name}:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

@JefferySJones
Copy link

JefferySJones commented Oct 4, 2023

We also had issues on 3.7.4 --- To clarify, we actually had issues just when we went to upgrade, not even necessarily related to 3.7.4 but its dependencies.. We had the same issues on at least 3.7.0-3.7.4. Had to add these two rules to our existing nginx proxy pass to get HMR to work as per @tibesyc comment. We didn't need the cache bypass, host rule, nor did we need the http_version change.

proxy_set_header 		Upgrade 		$http_upgrade;
proxy_set_header 		Connection 		"upgrade";

@pi0
Copy link
Member

pi0 commented Oct 4, 2023

For anyone trying to run nuxt dev server behind a reverse proxy using latest nuxi CLI:

  • HMR is now same port of the same server (3000)
  • vite.server.hmr config should be removed

see nuxt/cli#241 (comment) for a working (apache2) example and feel free to open any issues in nuxt/cli repository if still having for setup.

Also worth to note, modern Nuxi can terminate SSL and listen on single port (including 80/443) or even tunnel to an external URL with custom domain. You probably can simplify your whole workflow, however hacking is always welcome!

@fabkho
Copy link

fabkho commented Nov 6, 2023

@JefferySJones's answer worked for me with nuxt@3.8.0.

@mgd216
Copy link

mgd216 commented Nov 17, 2023

So glad to see others using Nuxt / SSL / Nginx Proxy, I was going crazy trying to find the error:

[Vue Router warn]: No match found for location with path "/_nuxt/"

@fabko reference to @JefferySJones solution worked, redeployed NGINX proxy with new settings and worked great. THANK YOU!

@danielroe danielroe changed the title Development server with Nuxt3 + SSL + Nginx + Proxy document running nuxt dev server behind nginx + SSL + proxy Jan 26, 2024
@giovannimanzoni
Copy link

New problem in Nuxt 3.10.1, last config from 3.8.x do not work anymore.

my last problem after a lot configs was: SyntaxError: Failed to construct 'WebSocket': The URL 'wss://localhost:undefined/' is invalid

but now, I can not believe DevTools, hmr work !!:

[vite] connecting...
[vite] connected.

so:
nuxt.config.ts

vite: {
  // no server block !
},

package.json

"dev": "nuxt dev --host localhost --port 3035"

nginx.conf

map $sent_http_content_type $expires {
  "text/html"                 epoch;
  "text/html; charset=utf-8"  epoch;
  default                     7d;
}

domain.conf

upstream mydomain {
  zone upstreams 64K;
  server localhost:3035 max_fails=1 fail_timeout=2s;
  keepalive 2;
}

server{
  listen       443 ssl http2;
  server_name  mydomain.lan;
  ...
  rewrite ^/(.*) /$1 break;
  
  location /_nuxt/ {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_pass http://mydomain;
  }

  location / {
    expires $expires;
    sendfile on;
    sendfile_max_chunk 512k;        
    aio on;
    proxy_max_temp_file_size 0;
    proxy_pass http://mydomain;
    proxy_next_upstream error timeout http_500;
    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;  # PLEASE !! DO NOT ADD THIS !!!
    proxy_redirect          off;
    proxy_buffering         on;
    proxy_cache_valid       200 1d;
    proxy_cache_use_stale   error timeout invalid_header updating http_500 http_502 http_503 http_504;
    proxy_cache_key         $uri$is_args$args;
    proxy_read_timeout      1m;
    proxy_connect_timeout   1m;
  }
}

@larsalbrecht
Copy link

larsalbrecht commented Feb 13, 2024

Our configuration for dev:

(only nuxt specific settings)
nginx:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    location ~ /_nuxt {
        proxy_redirect                      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_read_timeout                  1m;
        proxy_connect_timeout               1m;
        proxy_pass                          http://mydomain:3000
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_cache_bypass $http_upgrade;
    }

    location / {
        proxy_redirect                      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_read_timeout                  1m;
        proxy_connect_timeout               1m;
        proxy_pass                          http://mydomain:3000
    }
}

We have a seperate nginx config for our (nginx) proxy, that does the ssl. Here we have also map $http_upgrade:
(whole file)

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 80;
    listen [::]:80;
    server_name _;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name _;

    ssl_certificate <path-server.crt>
    ssl_certificate_key <path-server.key>

    location / {
        proxy_pass http://<point to webserver>
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

@TrentOster
Copy link

Hi!
I made changes to the NGINX config and made changes to nuxt.config.ts
My application is running at /backoffice

nginx.conf

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 127.0.0.1:880 ssl http2;

    server_name example.domain.com;

    ssl_certificate /etc/ssl/example.crt;
    ssl_certificate_key /etc/ssl/example.key;
	port_in_redirect off;

    location ~ /_nuxt {
        proxy_redirect                      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_read_timeout                  1m;
        proxy_connect_timeout               1m;
        proxy_pass                          http://127.0.0.1:3000;  
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_cache_bypass $http_upgrade;
    }

    # My application is running at /backoffice
    location /backoffice {
        proxy_redirect                      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_read_timeout                  1m;
        proxy_connect_timeout               1m;
        proxy_pass                          http://127.0.0.1:3000;  
    }
    
   # php page
    location / {
	try_files $uri $uri/ /index.php?$args;
    }
}

nuxt.config.ts


export default defineNuxtConfig({
  app: {
    head: {
      title: 'BackOffice',
    },
  },

  devtools: {
    enabled: true,
  },

  vite: {
    define: { 'process.env': {} },
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('.', import.meta.url)),
        '@core': fileURLToPath(new URL('./@core', import.meta.url)),
      },
    },
  },

  hooks: {
    'vite:extendConfig'(viteInlineConfig) {
      viteInlineConfig.server = {
        ...viteInlineConfig.server,
      };
    },
  },

})

package.json

{
  "scripts": {
    "dev": "nuxt dev --host 127.0.0.1 --port 3000",
    "build": "node --max-old-space-size=4096 node_modules/nuxt/bin/nuxt.mjs build",
    "preview": "nuxt preview",
    "start": "HOST=127.0.0.1 PORT=3000  node .output/server/index.mjs"
  },
  "dependencies": {
    "nitropack": "^2.9.6",
    "vue": "^3.3.4",
  
  },
  "devDependencies": {,
    "@nuxt/devtools": "latest",
    "@nuxtjs/device": "^3.1.1",
    "@pinia/nuxt": "^0.5.1",
    "nuxt": "^3.11.2",
    "vite": "^5.2.11",
    "vite-plugin-vuetify": "1.0.2",
    "vue": "^3.3.8",
  },
}

But I get messages in the browser console on the page /backoffice

WebSocket connection to 'wss://example.domain.com/_nuxt/' failed: 
Uncaught (in promise) DOMException: Failed to construct 'WebSocket': The URL 'wss://localhost:undefined/_nuxt/' is invalid.

How can I fix the config to fix the error?

@s00d
Copy link

s00d commented Jul 25, 2024

But I get messages in the browser console on the page /backoffice

WebSocket connection to 'wss://example.domain.com/_nuxt/' failed: 
Uncaught (in promise) DOMException: Failed to construct 'WebSocket': The URL 'wss://localhost:undefined/_nuxt/' is invalid.

How can I fix the config to fix the error?

vite: {
    server: {
      hmr: {
        clientPort: 443, 
      },
    },

@command-tab
Copy link

@s00d I distilled much of the information in this thread into a blog post. Note the update at the end about the nuxt.config.ts config being unnecessary. The long and short of it, I think, is that you're setting Vite config options in nuxt.config.ts when you no longer need to; Just using the right set of nginx directives is enough. Here's an example of an nginx config I use locally with Nuxt 3 behind it:

server {
    # Redirect http to https
    listen 80;
    server_name myapp-local.example.com;
    return 301 https://myapp-local.example.com$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name myapp-local.example.com;

    include https.conf;  # Which contains directives like `ssl_certificate` and `ssl_certificate_key`

    access_log /opt/homebrew/var/log/nginx/myapp-local.access_log.txt;
    error_log /opt/homebrew/var/log/nginx/myapp-local.error_log.txt;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://127.0.0.1:3000;
    }

    location /_nuxt/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_pass http://127.0.0.1:3000;
    }
}

This works great with nginx 1.27.0 and nuxt 3.12.4.

@s00d
Copy link

s00d commented Jul 26, 2024

@command-tab In some versions, there was

WebSocket connection to 'wss://example.domain.com/_nuxt/' failed: 
Uncaught (in promise) DOMException: Failed to construct 'WebSocket': The URL 'wss://localhost:undefined/_nuxt/' is invalid.

Yesterday, I checked all this on version 3.11.2.
Not everyone uses the latest version, which is why I specified this information. I spent all of yesterday trying to set this up.

@pausan
Copy link

pausan commented Aug 6, 2024

Unfortunately finding the right configuration took me a few hours, so I thought I write something to help out in case other people run into this.

My setup is a Docker instance with apache 2.4.55 + backend + frontend with nuxt. The apache proxies requests to the frontend as required (same as nginx in this thread).

I experienced this issue both with nuxt 3.12.13 and nuxt 3.12.14, and in both cases the fix was different both on nuxt-side and apache config. Thus this writing. First time took me quite some time to understand what was going on, second time it was rather easy.

The tricky part, as other people pointed out, is that I'm using HTTPS in the local environment, thus, for HMR to work, the client needs to properly use WSS and use the proper port, so the apache inside docker needs to proxy the requests to the right address.

Fix for Nuxt 3.12.14

This fix only works for this version of Nuxt on my end (every package up to date as of this writing).

nuxt.config.ts

export default defineNuxtConfig({
   // ... long config ...
  modules: [
    // ... other modules ...
    (_options, nuxt) => {
      nuxt.hooks.hook('vite:extendConfig', (config) => {
        config.server = {
          ...config.server,
          hmr: {                    // <--- this is the fix on nuxt side
            protocol: undefined, 
            clientPort: 443,        // <-- this was not required on 3.12.13 but is required in 3.12.14
          }     
        }
      })
    },
  ],
}

000-apache-site-ssl.conf

    # required for the frontend
    ProxyPass        / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/

    # support WebSocket upgrade
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*) ws://127.0.0.1:24678/$1 [P,L]

Normal requests get a passthrough to Nuxt 3000 port (HTTP), whereas upgrade to websockets get forwarded to port 24678 which seems to be the default, and I rather leave it like that. Although I guess it can be changed with vite.server.hmr.port.

Fix for Nuxt 3.12.13

Unlike with Nuxt 3.12.14 (the most recent), in the previous version the issue was that the client was using a hardcoded protocol. By setting it to "undefined" the code automatically detects the proper protocol based on the caller URL. The WebSocket server on the nuxt side was already using the same port as nuxt, thus, the configuration in apache is different as well. I'm adding this because I invested time into fixing the issue here before I realized I was running on an "older" version of Nuxt.

nuxt.config.ts

export default defineNuxtConfig({
  
  // ... long config ...
  
  modules: [
    
    // ... other modules, maybe ...
    
    (_options, nuxt) => {
      nuxt.hooks.hook('vite:extendConfig', (config) => {
        config.server = {
          ...config.server,
          hmr: { protocol: undefined }     // <--- this is the fix on nuxt side
        }
      })
    },
  ],
}

000-apache-site-ssl.conf

    # required for the frontend
    ProxyPass        / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/

    # support WebSocket upgrade
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*) ws://127.0.0.1:3000/$1 [P,L]

Please note that above code will proxy-pass all requests to port 3000 both for
HTTP and for HTTPS

Understanding the issue / debugging the issue

I'm writing an extra section just in case this might help somebody in the future.

Apart of reading this issue, there were two things that helped me understand what was going on:

  • Code inspection (Chromium Dev Tools)
  • Tool to test WS / WSS protocol (websocat)

When the request failed, I clicked on the dev-tools on the file that was causing the issue to inspect the code (usually client.js) and then looked for the source code of that file in node_modules (and then in vite repo).

E.g when I loaded nuxt in dev mode, this is what I was getting with Nuxt 3.12.13:

Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but
attempted to connect to the insecure WebSocket
endpoint 'ws://example.com/_nuxt/'. This request has been blocked; this
endpoint must be available over WSS.

Then, there was a link to: https://prolocal.artypist.com/_nuxt/@vite/client around line 500ish.

Culprit was on this line:

const socketProtocol = "wss" || (importMetaUrl.protocol === "https:" ? "wss" : "ws");

It was forcing "wss" always, and it should have detected based on the request. Thus, setting to undefined causes vite to automatically detect, and that works
fine.

In vite the code looks like this:

const socketProtocol =
  __HMR_PROTOCOL__ || (importMetaUrl.protocol === 'https:' ? 'wss' : 'ws')

Reference: https://github.com/vitejs/vite/blob/bb4ba9f448da01782f585369f80c4517da087000/packages/vite/src/client/client.ts#L26

HMR_PROTOCOL is replaced in runtime by nuxt, but if we set to undefined/zero/false, the second part of the expression is evaluated.

Now, the second thing that helped was to be able to perform a request from the commandline and DevTools console in order to assess whether the request was properly being proxied or not.

These are the two basic pieces I used:

Example using wesocat command:

$ ./websocat --protocol vite-hmr ws://127.0.0.1:3000/_nuxt/
{"type":"connected"}

Example using js in devtools:

w = new WebSocket('ws://127.0.0.1:3000/_nuxt/', ['vite-hmr']);
w.addEventListener('open', (e) => { console.warn('open:', e) });
w.addEventListener('error', (e) => {console.warn('error:', e)}) ;

With wesocat I could try to reproduce the issue both on my host machine, and inside docker instance.

# Inside docker, the basic check to see if Vite was responding:
$ ./websocat --protocol vite-hmr ws://127.0.0.1:24678/_nuxt/
{"type":"connected"}

# Inside docker, the basic check to see if Vite was responding through Apache
$ ./websocat --protocol vite-hmr wss://127.0.0.1/_nuxt/
{"type":"connected"}

# In the host machine (outside docker), to see if Vite was responding
$ ./websocat --protocol vite-hmr wss://127.0.0.1/_nuxt/
{"type":"connected"}

Please note that if you don't use "vite-hmr" protocol, it won't respond. WebSockets Protocol header is required.

I decided to post here instead of the HMR apache issue, because regardless of the apache/nginx, this issue is more about running SSL in the local dev environment than what server is used in the backend to proxy the request, I'm sure same logic applies to nginx (but with different config).

Thanks everybody for all the comments in this thread! They helped me start making sense of what was going on. I hope this writing can contribute as well and help other people.

@bubundas17
Copy link

    # required for the frontend
    ProxyPass        / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/

    # support WebSocket upgrade
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*) ws://127.0.0.1:3000/$1 [P,L]

Whats the equivalent for nginx? My config is based on nginx.

@fabkho
Copy link

fabkho commented Sep 4, 2024

Does anybody else here get a bunch of 502 errors on the /_nuxt/ directory after hitting page refresh? This does not happen always, but probably 2/3 of the time.
The errors look like this:
image

These are just random files and always different.

This is the relevant part of my ngnix config:

server {
    listen 127.0.0.1:80;
    #listen 127.0.0.1:80; # valet loopback
    server_name anny.test www.anny.test *.anny.test;
    return 301 https://$host$request_uri;
}

server {
    listen 127.0.0.1:443 ssl;
    #listen VALET_LOOPBACK:443 ssl; # valet loopback
    server_name anny.test www.anny.test *.anny.test;
    root /;
    charset utf-8;
    client_max_body_size 512M;
    http2  on;

    location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
        internal;
        alias /;
        try_files $uri $uri/;
    }

    ssl_certificate "/Users/{username}/.config/valet/Certificates/anny.test.crt";
    ssl_certificate_key "/Users/{username}/.config/valet/Certificates/anny.test.key";

    location /admin {
        proxy_pass https://localhost:8091;
        proxy_set_header X-Real-Ip $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

@command-tab
Copy link

@fabkho I encountered this issue, too, and it was resolved when I replaced all instances of localhost with 127.0.0.1 in the nginx config. I can't explain why, since they're equivalent according to /etc/hosts.

@fabkho
Copy link

fabkho commented Sep 5, 2024

@command-tab that helped! Thanks! Now the full-screen nginx error page does not occur anymore, but i get still the console errors. Weird 🤔

@mmghv
Copy link

mmghv commented Dec 13, 2024

@danielroe This should be closed as fixed by #29049.

Tested on latest nuxt@3.14.1592 dev server running on docker behind nginx SSL proxy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests