diff --git a/server/monitor-types/websocket.js b/server/monitor-types/websocket.js
new file mode 100644
index 000000000..47ac1df2a
--- /dev/null
+++ b/server/monitor-types/websocket.js
@@ -0,0 +1,69 @@
+const { MonitorType } = require("./monitor-type");
+const { UP } = require("../../src/util");
+const childProcessAsync = require("promisify-child-process");
+
+class websocket extends MonitorType {
+ name = "websocket";
+
+ /**
+ * @inheritdoc
+ */
+ async check(monitor, heartbeat, _server) {
+ //let status_code = await this.attemptUpgrade(monitor.url);
+ let statusCode = await this.curlTest(monitor.url);
+ this.updateStatus(heartbeat, statusCode);
+ }
+
+ /**
+ * Attempts to upgrade HTTP/HTTPs connection to Websocket. Use curl to send websocket headers to server and returns response code. Close the connection after 1 second and wrap command in bash to return exit code 0 instead of 28.
+ * @param {string} url Full URL of Websocket server
+ * @returns {string} HTTP response code
+ */
+ async curlTest(url) {
+ let res = await childProcessAsync.spawn("bash", [ "-c", "curl -s -o /dev/null -w '%{http_code}' --http1.1 -N --max-time 1 -H 'Upgrade: websocket' -H 'Sec-WebSocket-Key: test' -H 'Sec-WebSocket-Version: 13' " + url + " || true" ], {
+ timeout: 5000,
+ encoding: "utf8",
+ });
+ return res.stdout.toString();
+ }
+
+ /**
+ * Checks if status code is 101 and sets status
+ * @param {object} heartbeat The heartbeat object to update.
+ * @param {string} statusCode Status code from curl
+ * @returns {void}
+ */
+ updateStatus(heartbeat, statusCode) {
+ if (statusCode === "101") {
+ heartbeat.status = UP;
+ }
+ heartbeat.msg = statusCode;
+ }
+
+ // Attempt at using websocket library. Abandoned this idea because of the lack of control of headers. Certain websocket servers don't return the Sec-WebSocket-Accept, which causes websocket to error out.
+ // async attemptUpgrade(hostname) {
+ // return new Promise((resolve) => {
+ // const ws = new WebSocket('wss://' + hostname);
+
+ // ws.addEventListener("open", (event) => {
+ // ws.close();
+ // });
+
+ // ws.onerror = (error) => {
+ // console.log(error.message);
+ // };
+
+ // ws.onclose = (event) => {
+ // if (event.code === 1005) {
+ // resolve(true);
+ // } else {
+ // resolve(false);
+ // }
+ // };
+ // })
+ // }
+}
+
+module.exports = {
+ websocket,
+};
diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js
index 062f098d7..dcf803270 100644
--- a/server/uptime-kuma-server.js
+++ b/server/uptime-kuma-server.js
@@ -111,6 +111,7 @@ class UptimeKumaServer {
// Set Monitor Types
UptimeKumaServer.monitorTypeList["real-browser"] = new RealBrowserMonitorType();
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
+ UptimeKumaServer.monitorTypeList["websocket"] = new websocket();
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
@@ -549,6 +550,7 @@ module.exports = {
// Must be at the end to avoid circular dependencies
const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor-type");
const { TailscalePing } = require("./monitor-types/tailscale-ping");
+const { websocket } = require("./monitor-types/websocket");
const { DnsMonitorType } = require("./monitor-types/dns");
const { MqttMonitorType } = require("./monitor-types/mqtt");
const { SNMPMonitorType } = require("./monitor-types/snmp");
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index a83f91cab..b17b6be24 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -46,6 +46,10 @@
+
+