A browser-based SSH terminal built with NuxtJS — access your Linux server from any browser, anywhere, without installing anything.
- Multiple tabs — open several terminal sessions simultaneously, just like Windows Terminal
- Full terminal emulation — colors, cursor movement, scrollback, ANSI support via xterm.js
- Real-time connection — WebSocket-based communication with zero latency
- Status bar — live connection status, server IP, port, uptime timer, and latency
- PWA support — installable as a desktop/mobile app
- Keyboard shortcuts —
Ctrl+Tnew tab,Ctrl+Wclose tab
| Layer | Technology |
|---|---|
| Frontend | NuxtJS 3, Vue 3, xterm.js, xterm-addon-fit |
| Backend | Nitro (Nuxt server), Socket.io, ssh2 |
| Process manager | pm2 |
| HTTPS | Cloudflare Tunnel |
| Server | Hetzner VPS — Ubuntu 26.04 LTS |
Browser (xterm.js)
│
│ WebSocket (Socket.io)
│
Nuxt Backend (Node.js / Nitro)
│
│ SSH (ssh2 library)
│
Linux Server (bash shell)
The browser renders the terminal UI and sends keystrokes via WebSocket. The Nuxt server bridges the connection to the actual SSH session. All SSH credentials stay on the server — never exposed to the browser.
- Node.js 20+
- A Linux server with SSH access
- SSH key pair
git clone https://github.com/yourusername/webterminal.git
cd webterminalnpm install# On Linux/Mac
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
# On Windows PowerShell
ssh-keygen -t ed25519 -f C:\Users\YourName\.ssh\id_ed25519# Linux/Mac
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server-ip
# Windows PowerShell
type C:\Users\YourName\.ssh\id_ed25519.pub | ssh root@your-server-ip "cat >> ~/.ssh/authorized_keys"Create a .env file in the project root:
# Your server IP or hostname
SSH_HOST=your-server-ip
# SSH port (default: 22)
SSH_PORT=22
# SSH username
SSH_USER=root
# Path to your private key (for local development)
SSH_PRIVATE_KEY_PATH=/path/to/.ssh/id_ed25519
# OR paste the raw key content (for cloud deployments like Vercel)
# SSH_PRIVATE_KEY=-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----npm run devOpen http://localhost:3000 in your browser.
npm run buildscp -r .output root@your-server-ip:/root/webterminal/ssh root@your-server-ip
nano /root/webterminal/.envPaste:
SSH_HOST=127.0.0.1
SSH_PORT=22
SSH_USER=root
SSH_PRIVATE_KEY_PATH=/root/.ssh/id_ed25519export SSH_HOST=127.0.0.1
export SSH_PORT=22
export SSH_USER=root
export SSH_PRIVATE_KEY_PATH=/root/.ssh/id_ed25519
pm2 start /root/webterminal/.output/server/index.mjs --name webterminal
pm2 save
pm2 startupufw allow 3000Your app is now live at http://your-server-ip:3000.
Get a free HTTPS URL without a domain:
# Install cloudflared
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 \
-o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared
# Start tunnel with pm2
pm2 start /usr/local/bin/cloudflared \
--name cloudflare-tunnel \
-- tunnel --url http://localhost:3000
pm2 save
# Get your HTTPS URL
pm2 logs cloudflare-tunnel --lines 50 --nostream | grep trycloudflareYou'll get a free URL like https://random-words.trycloudflare.com.
| Variable | Required | Description |
|---|---|---|
SSH_HOST |
✅ | IP or hostname of the SSH target server |
SSH_PORT |
✅ | SSH port (usually 22) |
SSH_USER |
✅ | SSH username (e.g. root) |
SSH_PRIVATE_KEY_PATH |
One of these | Path to private key file (local dev) |
SSH_PRIVATE_KEY |
One of these | Raw private key content (cloud deploy) |
Warning: This app gives full shell access to your server. Take security seriously.
Recommended steps before sharing the URL:
- Add a login page / password protection
- Use HTTPS (Cloudflare Tunnel or Caddy)
- Disable SSH password authentication on your server
- Create a non-root user for the terminal session
- Never commit
.envor private keys to git
# Disable password SSH login (after confirming key works)
# In /etc/ssh/sshd_config:
PasswordAuthentication no
systemctl restart sshwebterminal/
├── app.vue # Main app — tab bar, layout
├── components/
│ └── TerminalTab.vue # Individual terminal tab + xterm.js
├── server/
│ └── plugins/
│ └── socket.ts # Socket.io server + SSH2 bridge
├── public/
│ └── terminal-icon.svg
├── nuxt.config.ts
├── package.json
└── .env # Never commit this!
pm2 list # see all running processes
pm2 logs webterminal # view live logs
pm2 restart webterminal # restart the app
pm2 stop webterminal # stop the app
pm2 monit # live monitoring dashboardPull requests are welcome. For major changes, please open an issue first.
MIT
Built with ❤️ using NuxtJS, Socket.io, ssh2, and xterm.js