What is noVNC, and how do you get it running?

noVNC is a VNC viewer that lives in a browser tab. You open a page, click Connect, and the remote desktop draws right there. Nothing gets installed on the machine you're sitting at, and it runs in modern desktop and mobile browsers.

The catch is that it's only the viewer. You still need a VNC server, and noVNC needs WebSockets where other VNC clients open a plain TCP connection. Unless your server speaks WebSocket itself, websockify sits in the middle and turns the WebSocket traffic back into a normal TCP socket.

A library, an app, and who already ships it

Underneath is a JavaScript library with one RFB object per connection. On top sits vnc.html, the app with the toolbar, settings and connect dialog, and that's the part you'll use unless you're embedding a console in your own page. If you've opened a VM console in OpenStack or OpenNebula, you've probably used noVNC already: both of them, and ThinLinc too, have it built in.

A laptop browser window showing a remote desktop session, connected by a dashed line to a slim server unit.

Samuel Mannehed and Pierre Ossman of Cendio make up the core team, and the code is mainly under MPL 2.0.

The client speaks these security types: none, classical VNC, RealVNC RSA-AES, Tight, VeNCrypt Plain, XVP, Apple Diffie-Hellman and UltraVNC MSLogonII, and the server sends the list it offers. noVNC takes the first type on that list that it supports, and an RFB 3.3 server simply names one. none is on the client's list, so a VNC server with no password hands its desktop to anyone who can load the page. Put a password on the server before you go any further.

How the browser, websockify and the VNC server fit

Three parts, two hops. With the VNC server on display :1 and websockify on port 6080:

A labeled four-step pipeline from browser to VNC server, with WebSocket, RFB object, and framebuffer updates painting into a canvas.

how the browser, websockify and the vnc server fit
browser (vnc.html + JS) --HTTP and WebSocket, TCP 6080--> websockify --plain TCP 5901--> VNC server on :1
  • The browser fetches vnc.html and its JavaScript over HTTP, then opens a WebSocket back to the same host and port, on the path websockify. If the page came over HTTPS, that socket is wss://. Inside it, the RFB object runs the ordinary RFB handshake: it agrees on a protocol version and a security type with the server, then reads the framebuffer size, pixel format and desktop name from ServerInit. After that it asks for the whole screen once and for changes only, paints them into a canvas, and sends your keystrokes and mouse back up the same socket.
  • websockify takes the WebSocket handshake and forwards bytes both ways between the browser and the VNC port. Give it --web and it serves the noVNC files too, so one port carries the page and the session.
  • The VNC server sees an ordinary RFB client. It listens on 5900 plus the display number, so :1 is TCP 5901.

websockify only reframes bytes. The VNC protocol runs end to end between the browser and the server, so the password and the security type get settled between those two, not by websockify. Servers with built-in WebSocket support, such as x11vnc/libvncserver and QEMU, take the browser connection directly, and you can leave websockify out.

Getting it running on Debian 13 or Ubuntu 24.04

The main path: a VNC server already up on display :1, websockify on localhost port 6080, and you on your workstation reaching it through SSH. Fedora and the upstream script come after, and only a few lines change there.

First make sure the server is actually listening. noVNC can't do a thing until something answers on 5901:

getting it running on debian 13 or ubuntu 24.04
ss -ltn | grep 5901

ss with -ltn lists listening TCP sockets with numeric ports. A LISTEN line on 127.0.0.1:5901 or 0.0.0.0:5901 and you're set. Empty output means the VNC server isn't running yet, so start that first.

Install the package. On both releases it pulls in websockify and puts the web app in /usr/share/novnc:

getting it running on debian 13 or ubuntu 24.04
sudo apt install novnc

Then start websockify as your normal user, serving the noVNC files and proxying to the VNC server:

getting it running on debian 13 or ubuntu 24.04
websockify --web /usr/share/novnc localhost:6080 localhost:5901

The first address is where websockify listens and the second is the target. Leave the host off the listen address and it binds every interface, so keep localhost: there until TLS is in place. It stays in the foreground and prints its settings:

getting it running on debian 13 or ubuntu 24.04
WebSocket server settings:
  - Listen on localhost:6080
  - Web server. Web root: /usr/share/novnc
  - No SSL/TLS support (no cert file)
  - proxying from localhost:6080 to localhost:5901

Listen on localhost:6080 is the line to look at. If it reads Listen on :6080, the host fell off and websockify is answering on every interface.

On your workstation, forward the port over SSH:

getting it running on debian 13 or ubuntu 24.04
ssh -L 6080:localhost:6080 <user>@<server-host>

Browse to http://localhost:6080/vnc.html and click Connect. If the server has a password, the status bar shows Credentials are required and asks for it. Once you're in, it reads Connected (unencrypted) to followed by the desktop name. Don't let "unencrypted" worry you. That label follows the app's Encrypt setting, which defaults to whether the page came over HTTPS, and here SSH is encrypting the hop underneath. Back in the websockify terminal, each browser connection logs a line ending in connecting to: localhost:5901.

The tunnel also keeps noVNC's HTTPS warning away. The app shows it only outside a secure context, and browsers count http://localhost as one.

Ctrl+C in the websockify terminal stops it. If you started it in the background with -D, match its full command line to stop it:

getting it running on debian 13 or ubuntu 24.04
pkill -f 'websockify --web /usr/share/novnc'

You'll also find /usr/share/novnc/utils/novnc_proxy in the package, the wrapper from the upstream quick start. It runs the same kind of websockify command, but its defaults bite: it listens on port 6080 on every interface and targets localhost:5900. Run it without --vnc against a server on :1 and websockify logs Failed to connect to localhost:5900 the moment you click Connect. Calling websockify directly keeps the bind address and the target in plain sight.

Letting colleagues in from their own browsers

If you're the only one connecting, stay on the SSH tunnel. When colleagues need the desktop from a browser alone, put TLS on websockify before you drop localhost: from the listen address. Load the page over plain HTTP from another machine and noVNC 1.5.0 or later (Debian 13, Fedora 43) puts Running without HTTPS is not recommended, crashes or other issues are likely. in the status bar, because the app leans on browser APIs that need a secure context.

letting colleagues in from their own browsers
websockify --web /usr/share/novnc --cert <cert.pem> --key <key.pem> --ssl-only 6080 localhost:5901
sudo ufw allow 6080/tcp

--ssl-only refuses unencrypted connections and the ufw line opens the port on hosts where ufw is enabled; the firewalld version is further down. The startup output now reads - SSL/TLS support and - Deny non-SSL/TLS connections. Browse to https://<server-host>:6080/vnc.html. The page came over HTTPS, so the app opens the WebSocket as wss:// on the same port, one certificate covers both, and the status bar now reads Connected (encrypted) to.

Use a certificate the browsers already trust. With a self-signed one, every user has to click through the certificate warning when the page loads, and that acceptance is what lets the socket open, since browsers don't prompt for a wss:// socket on their own. websockify adds no login of its own unless you set up an authentication plugin, so the VNC password stays the only gate.

No JavaScript needed. vnc.html reads its settings from the URL, and if you're embedding the library instead, the same switches exist as RFB properties.

URL setting RFB property Effect
resize=scale scaleViewport Scales the remote desktop locally to fit the browser window
resize=remote resizeSession Asks the server to resize the session whenever the window changes size
view_only=1 viewOnly Sends no keyboard or mouse events to the server
compression=0 to 9 compressionLevel (default 2) Higher levels save bandwidth and cost server CPU
autoconnect=1 none Connects as soon as the page loads

Put them after # rather than ?, because the fragment never reaches the server. This one connects as soon as the page loads, scales the desktop to the window and sends no input, which is what you want for someone watching a session:

scaled and view-only links
http://localhost:6080/vnc.html#resize=scale&view_only=1&autoconnect=1

view_only is a viewer setting, not access control. Whoever has the link can delete it from the URL, so it won't stop anyone who knows the VNC password from typing.

What changes on Fedora, older noVNC and the upstream script

The websockify command is the same everywhere. Only these lines change:

  • Fedora 43 ships noVNC 1.5.0. Install it with sudo dnf install novnc, which pulls in python3-websockify. The web root is the same /usr/share/novnc, and the wrapper is /usr/bin/novnc_proxy. Open the port with sudo firewall-cmd --add-port=6080/tcp, then run it again with --permanent so the rule survives a reload.
  • Ubuntu 24.04's noVNC 1.3.0 never shows the no-HTTPS warning, because the check isn't in that version. Don't read the silence as approval of plain HTTP.
  • novnc_proxy bind address: --listen HOST:PORT, as in --listen localhost:6080, works from noVNC 1.5.0 (Debian 13, Fedora 43), while the 1.3.0 script in Ubuntu 24.04 documents a bare port only. That's one more reason to call websockify directly there.
  • Upstream checkout or snap: from a clone of the repository, run ./utils/novnc_proxy --vnc localhost:5901 --listen localhost:6080; the script fetches websockify itself if none is installed. The snap installs with sudo snap install novnc and runs as novnc --listen 6080 --vnc localhost:5901.

When it doesn't connect

  • The status bar shows Failed to connect to server, and websockify logs Failed to connect to localhost:5901 followed by Connection refused. websockify took the browser's WebSocket but couldn't open the TCP side, because nothing listens on the target. Check with ss -ltn | grep 5901, then start the VNC server or fix the target port: display :2 is 5902.
  • The status bar warns that running without HTTPS is not recommended (noVNC 1.5.0 or later). You loaded vnc.html over plain HTTP from another machine. Go back to the SSH tunnel, or restart websockify with --cert, --key and --ssl-only.
  • websockify logs SSL connection but '.../self.pem' not found. The browser came in over https://, but websockify has no certificate, and changing the URL alone won't fix it. Pass --cert and --key.
  • novnc_proxy exits with Port 6080 in use. Try --listen PORT. Another websockify or noVNC instance holds the port. sudo ss -ltnp | grep 6080 names the process; stop it or pick another port.
  • Anything else: load vnc.html?logging=debug and watch the browser's JavaScript console. noVNC logs there, not to the websockify terminal.

Where noVNC sits next to a native viewer and Guacamole

All three put a desktop in front of you. What differs is who speaks VNC to the server. With noVNC it's the browser, through websockify. With TigerVNC's viewer it's a native app over plain TCP, and the same project ships the server side too. With Apache Guacamole the browser never speaks VNC at all: it talks Guacamole's own protocol to a gateway, and guacd on that gateway makes the VNC or RDP connection.

noVNC TigerVNC viewer Apache Guacamole
What runs in the browser The VNC client itself Nothing (native app) A client for the Guacamole protocol
Who speaks VNC to the server The browser, through websockify The viewer, over TCP guacd on the gateway
Protocols VNC VNC VNC, RDP and others through guacd plugins
Browser transport WebSocket only n/a WebSocket, with HTTP fallback
Server-side pieces websockify, unless the VNC server speaks WebSocket The VNC server guacd plus the web application in a servlet container

Keep websockify on localhost:6080 behind your SSH tunnel until someone else needs the desktop. That day, get a certificate their browsers already trust and restart it with --ssl-only, unless the same people also need RDP hosts, in which case Guacamole is the better front door.

FAQs

Who created noVNC?

Joel Martin founded the project, and he's now listed among its previous core contributors.

Does noVNC support touchscreen mouse gestures?

Yes. It has touch gestures that emulate the common mouse actions, so a phone or tablet browser on iOS or Android works as a viewer.

Which browsers does noVNC need?

Any recent one. The oldest versions known to work are Chrome 89, Firefox 89, Safari 15, Opera 75 and Edge 89. Nothing gets installed on the client; websockify and the noVNC files live on the server side.