remix
remix copied to clipboard
LiveReload doesn't work when TLS is enabled
What version of Remix are you using?
1.3.5
Steps to Reproduce
-
Create a server with TLS keys
-
Attach remix route handler to it
-
Load site
-
Error appears in console:
WebSocket connection to 'wss://moxy.cow-augmented.ts.net:8002/socket' failed: An SSL error has occurred and a secure connection to the server cannot be made.
-
Change files, observe that they are not reloaded live. (Server rebuilds, but the ws push doesn't work.)
Expected Behavior
Expect that WebSocket.Server would be able to listen over https somehow.
Actual Behavior
WebSocket connection to 'wss://moxy.cow-augmented.ts.net:8002/socket' failed: An SSL error has occurred and a secure connection to the server cannot be made.
Tracked it down to this bit in ./node_modules/@remix-run/dev/cli/commands.js
let wss = new WebSocket__default["default"].Server({
port: config$1.devServerPort
});
It appears that the only way for a ws server to speak tls is to attach to an existing server.
Something like this:
const server = config.devServerCert && config.devServerKey ? https.createServer({
cert: config.devServerCert,
key: config.devServerKey,
}) : http.createServer()
server.listen(config.devServerPort)
let wss = new WebSocket.Server({ server });
@isaacs this might help. I'm running a different setup than yours, but I still serve the client via HTTPS and the live reload server has to connect via wss
due to https
being used as the protocol
Heres the actual full client deployment file
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-depl
spec:
replicas: 1
selector:
matchLabels:
app: client
template:
metadata:
labels:
app: client
spec:
automountServiceAccountToken: false
containers:
- name: client
image: pidgeon/client-remix
envFrom:
- secretRef:
name: dotenv
# ----------------------------------------------------------------------------------------
#
# Development only
#
# We use init containers to download the root certificate authority certificate,
# that was used to sign the intermediate and leaf certificate of Traefik,
# and mount the certificate to the client's container as NODE_EXTRA_CA_CERTS
#
# We need to do this because otherwise any HTTPS requests issued from the client to Traefik will fail
# ----------------------------------------------------------------------------------------
env:
- name: NODE_EXTRA_CA_CERTS
value: /certs/pebble-root-ca.pem
- name: REMIX_DEV_SERVER_WS_PORT
value: "3001"
volumeMounts:
- name: certs
mountPath: /certs
initContainers:
# ------------------------------------------
#
# Stage 1: Wait for Pebble to be available
#
# ------------------------------------------
- name: client-wait-for-pebble
image: alpine/curl
command: ["/bin/sh", "-c"]
args:
[
'while [[ "$(curl --insecure -s -o /dev/null -w %{http_code} https://pebble:15000/roots/0)" != "200" ]]; do echo "Waiting for Pebble..."; sleep 5; done',
]
# ----------------------------------------------------------------------------------------
#
# Stage 2: Get the root CA used to sign Traefik's leaf and intermediate certificate
#
# ----------------------------------------------------------------------------------------
- name: client-get-root-certs
image: alpine/curl
command: ["/bin/sh", "-c"]
args:
[
"curl --insecure -s -o /certs/pebble-root-ca.pem https://pebble:15000/roots/0",
]
volumeMounts:
- name: certs
mountPath: /certs
volumes:
- name: certs
emptyDir: {}
For my case, I use init containers to extend the certificate authorities, via NODE_EXTRA_CA_CERTS
, of the container that runs my Remix.Run client, which allows me to be able to perform HTTPS
requests to my ingress controller, which handles all the routing in my cluster, but it also completes the authority chain of the certificates that I'm using. Thus I don't need to provide any extra properties to my live reload server. Just some food for thought
Being able to run a dev
server on an https
domain (localhost
, lvh.me
) would be wonderful 👌
Is there any chance of the above commit being accepted in the near future? Using https
configuration locally with an Express server setup is blocking access to the wss://
for hot reloading at the moment.
Not sure if there's another known workaround.
Have you tried simply including the <LiveReload/>
component in your project and removing the protocol check (i.e., always using ws:
).
https://github.com/remix-run/remix/blob/40a7a390063836607c76aad9754b7881f8c82303/packages/remix-react/components.tsx#L1498-L1561
I've been using <LiveReload />
in the project (worth noting it works great without this HTTPS config) but unfortunately tweaking the LiveReload
component directly produces the following error
Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.
Additionally as this is an organisation repository, editing the library files directly probably wouldn't be a maintainable approach going forward.
Appreciate the suggestion though 🙇♂️
First, I wasn't saying to edit the Remix version, but to copy it into your project. It's a very straightforward component. I have custom LiveReload as well.
However, it doesn't look like you can connect to an insecure web socket from a secure route anyway, so this is a moot point.
Is there a reason you need HTTPS on your local machine? Have you considered running a reverse proxy like nginx in front of your Remix app? That way Remix doesn't care about HTTPS, only your browser.
I made this change for gitpod.io env, and it started working flawlessly.
- First commit is just copying the component into a custom one and importing instead of the official one
- Second commit adds an expression to see whether I am running on gitpod.io and changes the URL
You can do the same easily with the protocols :)
@kiliman I need HTTPS for Marketo which requires a custom domain for certain functions to work (for security reasons). If I use a custom domain for localhost without HTTPS on my computer the page won't load because it is not secure.
@petomalina I had to do a similar thing for CodeSandbox. If you look at my LiveReload
component, you'll see that I had to change the URL since CSB encodes the port in the host name.
https://codesandbox.io/s/remix-starter-template-xs4z6?file=/app/components/LiveReload.tsx
Doesn't seem like #4123 or #4107 will be merged anytime soon... so I solved it using the attached snippet.
The below connects to the web socket created when calling "remix watch" command, and spins up an additional web socket that uses an https server instance.
Once a message to the remix web socket is sent, it is then sent to the https one.
const { WebSocket } = require("ws");
const httpsServer = // ... [some code that spins up an https server with express]
if (NODE_ENV !== "production") {
const connectToRemixSocket = (cb, attempts = 0) => {
const remixSocket = new WebSocket(`ws://127.0.0.1:8002`);
remixSocket.once("open", () => {
console.log("Connected to remix dev socket");
cb(null, remixSocket);
});
remixSocket.once("error", (error) => {
if (attempts < 3) {
setTimeout(() => {
connectToRemixSocket(cb, attempts += 1);
}, 1000);
}
else {
cb(error, null);
}
});
};
connectToRemixSocket((error, remixSocket) => {
if (error) {
throw error;
}
const customSocket = new WebSocket.Server({ server: httpsServer });
remixSocket.on("message", (message) => {
customSocket.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message.toString());
}
});
});
});
}
NOTE: The above example is missing the https server portion. Just make sure to set the live reload component port to the same port as the https server.
app/root.tsx:
<body>
....
{/* assuming the https server is listening on port 2001 */}
<LiveReload port={2001} />
</body>
The new v2 dev server now fully supports TLS, as described in the docs.
Use Vite and you don't need LiveReload 🙏💯🙌