Skip to content
 
 

Latest commit

 

History

4,892 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Self-Hosted Telegram Web (server-side proxy)

English | Русский

A self-hosted build of Telegram Web A where the browser only ever connects to your own domain. All Telegram traffic (MTProto WebSocket, HTTP fallback, and the t.me "websync" pings) is tunneled through a small server-side relay that talks to Telegram from your server.

browser ──wss/https──▶ tg.example.com ──▶ Caddy/nginx ──▶ Node relay ──wss/https──▶ Telegram DC
        (only your origin)               (TLS)           (/tgproxy/*)   (from your server)

Why

The stock client connects the browser straight to *.web.telegram.org. This fork makes the browser talk only to your origin; your server (which has access to Telegram) relays the encrypted stream. The relay is a dumb byte forwarder — MTProto is encrypted end-to-end, so it never decrypts, stores, or logs payloads, auth keys, phone numbers, codes, or messages. It logs only technical events (route, token, upstream host, status).

It is not an open proxy: only /tgproxy/... routes exist and the DC token must match ^[1-5](-1)?$, so the relay can reach only Telegram's zws<dc>.web.telegram.org data centers.

Use cases

  • Telegram's endpoints are unreachable, but a plain HTTPS site on your domain is — and running a VPN on the device isn't an option. The client is just a normal web page on tg.example.com.
  • Managed or locked-down machines — a work laptop, a kiosk, a shared computer — where you can't install a VPN client, a browser extension, or a desktop app, but a browser can open any HTTPS site.
  • A single, controlled egress point: all Telegram access leaves from your server's IP, which you manage, instead of from each client device.
  • Keeping client devices off third-party domains when you'd rather route everything through infrastructure you own.

Use it in accordance with the laws and network policies that apply to you.

Changes vs. upstream

Everything is gated behind TELEGRAM_SELF_HOSTED=1; unset, the project builds exactly like upstream.

File Change
src/lib/gramjs/Utils.ts getDC() returns a DC routing token ("2", "2-1"), not a Telegram host.
src/lib/gramjs/extensions/proxyBase.ts New helper resolving the proxy base URL (origin-relative by default).
src/lib/gramjs/extensions/PromisedWebSockets.ts WS → wss://<origin>/tgproxy/ws/<token>.
src/lib/gramjs/extensions/HttpStream.ts HTTP fallback → https://<origin>/tgproxy/http/<token>.
src/util/websync.ts Disables t.me/telegram.me _websync_ script injection.
webpack.config.ts connect-src 'self' CSP + new build-time env vars.
server/, deploy/selfhosted/ The Node relay and deploy files (Caddy/nginx/Docker).

Quick start (Docker + Caddy auto-TLS)

cp .env.selfhosted.example .env        # TELEGRAM_API_ID / TELEGRAM_API_HASH / DOMAIN / ACME_EMAIL
# point your domain's DNS at the server, then:
docker compose -f deploy/selfhosted/docker-compose.yml --env-file .env up -d --build

Get your api_id / api_hash from my.telegram.org (each operator uses their own; never commit them). Requires Node ^22.6 || ^24 for manual builds.

Manual build

cp .env.selfhosted.example .env
npm install
TELEGRAM_SELF_HOSTED=1 APP_ENV=production npm run build:production   # → dist/
cd server && npm install && PORT=8080 npm start                      # the relay
# front with deploy/selfhosted/Caddyfile or nginx.conf for TLS + /tgproxy/*

URLs are origin-relative by default, so the same bundle works on any domain. Set TELEGRAM_PROXY_WS_URL / TELEGRAM_PROXY_HTTP_URL only if the relay is on a different origin.

Secure context required. The client uses crypto.subtle, which works only on https:// or http://localhost. In production behind Caddy/nginx with a real certificate this is automatic; testing from another device over plain http://<lan-ip> will not work.

Verify no direct Telegram traffic

  • DevTools → Network: every request hits your origin only — no web.telegram.org / t.me.
  • grep the bundle: grep -rEi "web\.telegram\.org|/_websync_|zws[1-5]|/apiws" dist/*.js && echo LEAK || echo clean
  • Relay logs: only event=ws_open token=2 host=zws2.web.telegram.org, never payloads.
  • The connect-src 'self' CSP blocks any stray direct connection.

Security model — read before sharing the URL

  • Sessions live in the visitor's browser (IndexedDB), never on the server. Opening your URL gives a fresh login screen — a visitor cannot enter your account; they log in to their own.
  • The URL is a public login portal: anyone with the link can log into their own account through your server. For a personal instance add access control (Caddy basic_auth, Tailscale/WireGuard, or an IP allowlist).
  • Operator trust: whoever runs the server serves the JS. This fork exfiltrates nothing, but users logging in trust the operator — as with any self-hosted web client.
  • Voice/video calls use a separate WebRTC transport and do not flow through this relay.

License

Fork of Telegram Web A by Ajaxy, under GNU GPL v3.0. This fork keeps the same license — see LICENSE.

About

Telegram Web A, GPL v3

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages