WebSocket (WSS) Connection Issues with Phoenix LiveView on Clever Cloud, Works Fine on HTTP

Hello everyone,

I’m facing an issue with establishing WebSocket (wss://) connections for Phoenix LiveView in a deployed application on Clever Cloud. The application works perfectly in HTTP, but as soon as I switch to HTTPS (which Clever Cloud handles with Let’s Encrypt), the WebSocket connection fails.

Environment:

  • Elixir version: 1.14
  • Phoenix version: 1.7.10
  • Phoenix LiveView version: 1.0
  • Clever Cloud: Hosting the application
  • LiveView version: 0.18.18
  • WebSocket protocol: WSS
  • Frontend: JavaScript (Phoenix LiveSocket)
  • Backend: Phoenix framework with LiveView and PubSub

Steps Taken:

  1. I’ve configured Phoenix to handle both HTTP and WebSocket connections using the correct URL and port configurations.
  2. The application works perfectly over HTTP (i.e., it uses ws:// for WebSockets), but as soon as I enable HTTPS on Clever Cloud (which uses Let’s Encrypt), the WebSocket connection switches to wss://, and it fails to connect.
  3. I’ve already checked that the domain and DNS configuration on Clever Cloud are correct. The application is accessible via https://www.felicien.cool and the certificate is valid.

Problem:

  • The WebSocket (wss://) connection repeatedly fails to connect.
  • I can see that the connection is being established in the server logs (Phoenix is logging “CONNECTED TO Phoenix.LiveView.Socket”).
  • On the frontend, I see an error in the browser’s developer console that says WebSocket connection to 'wss://www.felicien.cool/live/websocket' failed.
  • The app works as expected over HTTP (i.e., using ws:// for WebSocket).

Current Configuration:

config.exs:

elixir

Copy

config :wordly, WordlyWeb.Endpoint,
  # Port 80 pour HTTP
  url: [host: "www.felicien.cool", port: 443],
  render_errors: [
    formats: [html: WordlyWeb.ErrorHTML, json: WordlyWeb.ErrorJSON],
    layout: false
  ],
  pubsub_server: Wordly.PubSub,
  check_origin: ["https://felicien.cool", "https://www.felicien.cool"],
  live_view: [signing_salt: "xxxx"]

runtime.exs:

 host = System.get_env("PHX_HOST") || "www.felicien.cool"
  port = String.to_integer(System.get_env("PORT") || "4000")

  config :wordly, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")

  config :wordly, WordlyWeb.Endpoint,
    url: [host: host, port: 443, scheme: "https"],
    http: [
      # Enable IPv6 and bind on all interfaces.
      # Set it to  {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
      # See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
      # for details about using IPv6 vs IPv4 and loopback vs public addresses.
      ip: {0, 0, 0, 0, 0, 0, 0, 0},
      port: port
    ],
    secret_key_base: secret_key_base

JavaScript (LiveSocket):

js

import { Socket } from "phoenix";
import { LiveSocket } from "phoenix_live_view";
import topbar from "../vendor/topbar";

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
let liveSocket = new LiveSocket("/live", Socket, { params: { _csrf_token: csrfToken } });

topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" });
window.addEventListener("phx:page-loading-start", () => topbar.show(300));
window.addEventListener("phx:page-loading-stop", () => topbar.hide());

liveSocket.connect();
window.liveSocket = liveSocket;

Troubleshooting Done:

  1. Verified that the WebSocket URL (wss://www.felicien.cool/live/websocket) is correct and accessible.
  2. Ensured the certificate is valid and the domain is correctly configured for SSL in Clever Cloud.
  3. Checked browser logs and network activity, where I see that the connection to the WebSocket URL fails.
  4. Confirmed that the issue only happens when switching to HTTPS; everything works in HTTP.

Questions:

  • Could there be a misconfiguration on my end that’s causing the WebSocket connection to fail when using wss://?
  • Is there anything specific in Clever Cloud’s configuration that I may need to check for WebSocket connections (like reverse proxy settings)?
  • Should I disable the https: configuration in Phoenix and let Clever Cloud handle everything, or is there a specific setting I need to adjust?
  • Could this be related to Clever Cloud’s load balancer, and if so, how can I ensure WebSocket connections are passed through correctly?

Any advice or insights on how to resolve this would be greatly appreciated!

Thank you in advance!

I encountered the same issue you described. In my case, I used Nginx to map the Phoenix server and handle SSL.

Initially, I set the host as an IPv4 address, but that didn’t work. Then, someone mentioned check_origin, which also failed to resolve the problem.

Interestingly, this websocket connection issue never occurred in the development environment. So, I decided to investigate further.

I discovered that a websocket connection is actually initiated via an HTTP request from my domain (e.g., example.com) to the host. Nginx handled the SSL and forwarded the request to Phoenix successfully. The problem was with the configuration in the Phoenix application.

Due to browser CORS requirements, the websocket HTTP request includes some static file requests that must be handled by the same domain name. I found that changing the environment variable $PHX_HOST to my domain resolved the issue.

Notice: I removed check_origin, as whatever settings I tried did not resolve the issue in my case.

I hope this information is helpful!

You need to have your check_origin set in your runtime.exs

config :wordly, WordlyWeb.Endpoint,
  ...
  check_origin: ["//www.felicien.cool", "//localhost"]

The format is //<host> without the scheme: