Upgrade from Local + Atlas "Getting Started" to Nextjs v14 issue: WebSocket connection to 'ws://<URL>/_next/webpack-hmr' failed:

What issue or error are you experiencing?

On Mac, after upgrading from Local with Atlas add-on “getting started” to Nextjs v14 and Faustjs v3, Nextjs is throwing a websocket error. Research shows that it is possible that either the Local nginx config set of files or next.config.js may need an adjustment. After trying several variations of site.conf.hbs location without success, I thought I would reach out here.

When I type localhost:10023 directly into the browser instead of my-site.local, the websocket error does not appear. Instead, I get “HMR connected” in the browser console and:

GET http://localhost:10023/favicon.ico 404 (Not Found)

I have also tried various nextjs config changes so that my-site.local will hopefully work but with no luck:

  // async rewrites() {
  //   return [
  //     {
  //       source: "/:path*",
  //       destination: "http://au-all-local.local/:path*",
  //     },
  //   ];
  // },
  // webpack: (config, { isServer }) => {
  //   if (!isServer) {
  //     config.module.rules.push({
  //       test: /\.css$/,
  //       use: ["style-loader", "css-loader"],
  //     });
  //   }
  //   return config;
  // },
  // webpack: (config, { dev }) => {
  //   if (dev) {
  //     config.devServer.hot = false;
  //   }
  //   return config;
  // },

Here’s what I tried adding to site.conf.hbs:

   # Proxy to Next.js dev server on port 10023
    location / {
        proxy_pass http://localhost:10023;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Handle WebSocket connections for HMR
    location /_next/webpack-hmr {
        proxy_pass http://localhost:10023/_next/webpack-hmr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }

…and to site.conf.hbs, I tried adding:

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

…which unfortunately crashes WP and causes a 500 status code.


What steps can be taken to replicate the issue? Feel free to include screenshots, videos, etc

This is the contents of my package.json

"dependencies": {
    "@apollo/client": "^3.10.3",
    "@faustwp/cli": "^3.0.1",
    "@faustwp/core": "^3.0.1",
    "@fortawesome/fontawesome-free": "^6.5.2",
    "@fortawesome/fontawesome-svg-core": "^6.5.2",
    "@fortawesome/free-brands-svg-icons": "^6.5.2",
    "@fortawesome/free-regular-svg-icons": "^6.5.2",
    "@fortawesome/free-solid-svg-icons": "^6.5.2",
    "@fortawesome/react-fontawesome": "^0.2.0",
    "@wordpress/base-styles": "^4.48.0",
    "@wordpress/block-library": "^8.34.0",
    "classnames": "^2.5.1",
    "graphql": "^16.8.1",
    "next": "^14.2.3",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "sass": "^1.77.0"
  },
  "devDependencies": {
    "next-secure-headers": "^2.2.0"
  },
  "engines": {
    "node": ">=18",
    "npm": ">=8"
  }

System Details

  • Local Version: 9.0.2+6676

  • Atlas add-on Version: 1.7.3

  • Operating System (OS) and OS version: Mac OS Ventura 13.6.1


Local Logs

Attach your Local Logs here (Help Doc - Retrieving Local’s Log)
local-lightning.log (767.2 KB)
local-lightning-verbose.log (891.8 KB)


Security Reminder
Local does a pretty good job of scrubbing private info from the logs and the errors it produces, however there’s always the possibility that something private can come through. Because these are public forums, always review the screenshots you are sharing to make sure there isn’t private info like passwords being displayed.

I can try to help. Your NGINX config looks correct, maybe double check to make sure they are in the right block. Maybe a consolidated version here:

server {
  listen 80;
  server_name my-site.local;

  location / {
    proxy_pass http://localhost:10023;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

  location /_next/webpack-hmr {
    proxy_pass http://localhost:10023/_next/webpack-hmr;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;
  }
}




-Ensure next.config.js is correct for websocket. Which looks correct:


module.exports = {
  webpackDevMiddleware: (config) => {
    config.watchOptions = {
      poll: 1000,
      aggregateTimeout: 300,
    };
    return config;
  },
  devServer: {
    hot: true,
    port: 10023,
    public: 'http://my-site.local', // This should match your Nginx configuration
  },
  async rewrites() {
    return [
      {
        source: '/:path*',
        destination: 'http://localhost:10023/:path*',
      },
    ];
  },
};
  • Check and see if /etc/hosts file has an entry for my-site.local pointing to 127.0.0.1.

-Check the browser dev tools in the WS to see connections and see what URL the websocket is connecting to

  • Restart Next.js and NGINX

  • and besides checking terminal errors and ensuring the correct path matches the URL, try this Nginx config settings:

server {
  listen 80;
  server_name my-site.local;

  location / {
    proxy_pass http://localhost:10023;
    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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

  location /_next/webpack-hmr {
    proxy_pass http://localhost:10023/_next/webpack-hmr;
    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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;
  }


  location /favicon.ico {
    alias /path/to/your/nextjs/project/public/favicon.ico;
  }
}

Let me know if that helps any.

2 Likes

Hi, thanks for jumping in on this! I was able to make some changes but ran into ApolloError: fetch failed.

The /etc/hosts looks good! My site is called au-all-local.local in LocalWP.

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1	localhost
255.255.255.255	broadcasthost
::1             localhost

## Local - Start ##
127.0.0.1 au-all-local.local #Local Site
::1 www.au-all-local.local #Local Site
127.0.0.1 www.au-all-local.local #Local Site
## Local - End ##

I made the suggested changes to my Nextjs config, so the entire file now looks like this:

const { withFaust, getWpHostname } = require("@faustwp/core");
const { createSecureHeaders } = require("next-secure-headers");

/**
 * @type {import('next').NextConfig}
 **/
module.exports = withFaust({
  // configure Nextjs correctly for websockets
  webpackDevMiddleware: (config) => {
    config.watchOptions = {
      poll: 1000,
      aggregateTimeout: 300,
    };
    return config;
  },
  devServer: {
    hot: true,
    port: 10023,
    public: "http://au-all-local.local", // This should match Nginx configuration
  },
  async rewrites() {
    return [
      {
        source: "/:path*",
        destination: "http://localhost:10023/:path*",
      },
    ];
  },
  reactStrictMode: true,
  sassOptions: {
    includePaths: ["node_modules"],
  },
  images: {
    domains: [getWpHostname()],
    formats: ["image/avif", "image/webp"],
    remotePatterns: [
      {
        protocol: "http",
        hostname: "2.gravatar.com",
        port: "",
        pathname: "/avatar/**",
      },
    ],
  },
  i18n: {
    locales: ["en"],
    defaultLocale: "en",
  },
  async headers() {
    return [
      {
        source: "/:path*",
        headers: createSecureHeaders({
          xssProtection: false,
        }),Preformatted text
      },
    ];
  },
});

And then lastly, I think I’m most shaky on how to modify the /conf/nginx.conf.hbs file and/or the /conf/site.conf.hbs file. For example, site.conf.hbs has a lot of variables and a few objects with location {} already set, which leads me to a few clarifying questions

  1. Is the code suggested above meant to go in one of these files specifically? If so, which file(s)?
  2. Do the other location objects interfere or compete with the ones suggested? Or do they work in conjunction with one another?
  3. Should I be erasing file contents of one or both of these files and replacing it with the suggested configs above?

Thank you for your suggestions! Here are the two files in question for reference.

nginx.conf.hbs:

error_log "{{logs.errorLog}}";

events {
	worker_connections  1024;
}

http {
	include includes/mime-types.conf;

	server_names_hash_bucket_size 128;

	client_max_body_size 1000M;
	default_type       application/octet-stream;
	access_log         off;
	sendfile           off;
	keepalive_timeout  3;

	fastcgi_buffers 32 32k;
    fastcgi_buffer_size 32k;
    fastcgi_read_timeout 1800s;

	map $http_x_forwarded_proto $resolved_scheme {
        default "http";
        "https" "https";
    }

	map $resolved_scheme $fastcgi_https {
		default '';
		https on;
	}

	include site.conf;
}

site.conf.hbs:

upstream php {
  {{#each fastcgi_servers}}
  server {{this}};
  {{/each}}
}

server {
	listen 127.0.0.1:{{port}};
	listen [::1]:{{port}};
    root   "{{root}}";

    index index.php index.html index.htm;

    #
    # Generic restrictions for things like PHP files in uploads
    #
	include includes/restrictions.conf;

    #
	# Gzip rules
	#
	include includes/gzip.conf;

    #
	# WordPress Rules
	#
    {{#unless site.multiSite}}
    include includes/wordpress-single.conf;
    {{else}}
    include includes/wordpress-multi.conf;
    {{/unless}}

	#
	# Forward 404's to WordPress
	#
	error_page 404 = @wperror;
	location @wperror {
		rewrite ^/(.*)$ /index.php?q=$1 last;
	}

    #
	# Static file rules
	#
	location ~* \.(?:css|js)$ {
        access_log        off;
        log_not_found     off;
        add_header        Cache-Control "no-cache, public, must-revalidate, proxy-revalidate";
    }

    location ~* \.(?:jpg|jpeg|gif|png|ico|xml)$ {
        access_log        off;
        log_not_found     off;
        expires           5m;
        add_header        Cache-Control "public";
    }

    location ~* \.(?:eot|woff|woff2|ttf|svg|otf) {
        access_log        off;
        log_not_found     off;

        expires           5m;
        add_header        Cache-Control "public";

        # allow CORS requests
        add_header        Access-Control-Allow-Origin *;
    }

    #
    # PHP-FPM
    #
	location ~ \.php$ {
		try_files $uri =404;

		fastcgi_split_path_info ^(.+\.php)(/.+)$;

		fastcgi_param   QUERY_STRING            $query_string;
		fastcgi_param   REQUEST_METHOD          $request_method;
		fastcgi_param   CONTENT_TYPE            $content_type;
		fastcgi_param   CONTENT_LENGTH          $content_length;

		fastcgi_param   SCRIPT_FILENAME         $document_root$fastcgi_script_name;
		fastcgi_param   SCRIPT_NAME             $fastcgi_script_name;
		fastcgi_param   PATH_INFO               $fastcgi_path_info;
		fastcgi_param   PATH_TRANSLATED         $document_root$fastcgi_path_info;
		fastcgi_param   REQUEST_URI             $request_uri;
		fastcgi_param   DOCUMENT_URI            $document_uri;
		fastcgi_param   DOCUMENT_ROOT           $document_root;
		fastcgi_param   SERVER_PROTOCOL         $server_protocol;

		fastcgi_param   GATEWAY_INTERFACE       CGI/1.1;
		fastcgi_param   SERVER_SOFTWARE         nginx/$nginx_version;

		fastcgi_param   REMOTE_ADDR             $remote_addr;
		fastcgi_param   REMOTE_PORT             $remote_port;
		fastcgi_param   SERVER_ADDR             $server_addr;
		fastcgi_param   SERVER_PORT             $server_port;
		fastcgi_param   SERVER_NAME             $host;

		fastcgi_param   HTTPS                   $fastcgi_https;

		fastcgi_param   REDIRECT_STATUS         200;

		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

		fastcgi_pass php;
		fastcgi_buffer_size      64k;
		fastcgi_buffers          32 32k;
		fastcgi_read_timeout	 1200s;

		proxy_buffer_size        64k;
		proxy_buffers            32 32k;
		proxy_busy_buffers_size  256k;
	}
}

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.