Docker image for running a Telegram WEB Proxy compatible with Telegram Desktop 7.1+.
The proxy uses the WEB transport introduced in Telegram Desktop: Telegram connects to your domain over regular HTTPS and transports MTProto traffic through a hidden WebView using WebSocket or HTTPS fallback.
Based on tgwebproxy.
You need:
80 and 443 available;Example DNS record:
proxy.example.com A 203.0.113.10
The DNS record must point directly to your server.
If you use Cloudflare, set the record to DNS only rather than proxied mode.
Generate a random 16-byte secret:
openssl rand -hex 16
Example:
0123456789abcdef0123456789abcdef
Run the container:
docker run -d \
--name tgwebproxy \
--restart unless-stopped \
-p 80:80 \
-p 443:443 \
-v tgwebproxy-certs:/certs \
-e TGWP_DOMAIN=proxy.example.com \
-e TGWP_SECRET=0123456789abcdef0123456789abcdef \
powernic/tgwebproxy:latest
Replace:
proxy.example.com
with your domain and:
powernic/tgwebproxy:latest
with the actual Docker Hub image name.
The /certs volume should be persistent. It stores the automatically obtained TLS certificate and prevents unnecessary certificate reissuance after container recreation.
Create compose.yml:
services:
tgwebproxy:
image: powernic/tgwebproxy:latest
container_name: tgwebproxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
TGWP_DOMAIN: "${TGWP_DOMAIN}"
TGWP_SECRET: "${TGWP_SECRET}"
TGWP_LOG_LEVEL: "info"
volumes:
- tgwebproxy-certs:/certs
volumes:
tgwebproxy-certs:
Create .env:
TGWP_DOMAIN=proxy.example.com
TGWP_SECRET=0123456789abcdef0123456789abcdef
Generate the secret with:
openssl rand -hex 16
Start the proxy:
docker compose up -d
View logs:
docker compose logs -f
Restart:
docker compose restart
Update to the newest image:
docker compose pull
docker compose up -d
Stop:
docker compose down
The certificate volume is retained after docker compose down.
To remove it as well:
docker compose down -v
Normally you should not remove the certificate volume.
Open Telegram Desktop:
Settings
→ Advanced
→ Connection type
→ Add proxy
→ WEB
Enter:
Web proxy hostname:
proxy.example.com
Secret:
0123456789abcdef0123456789abcdef
You can also create a Telegram proxy link:
https://t.me/webproxy?server=proxy.example.com&secret=0123456789abcdef0123456789abcdef
Depending on Telegram support, the link may need to be opened directly inside Telegram.
| Variable | Default | Description |
|---|---|---|
TGWP_DOMAIN | — | Domain served by the relay. Required. |
TGWP_SECRET | generated | 16-byte secret encoded as hex or base64url. |
TGWP_SITE | built-in page | Directory containing the cover website. |
TGWP_CERT_DIR | certs | Directory used to cache ACME/TLS certificates. |
TGWP_LISTEN | :443 | HTTPS listen address. |
TGWP_PLAIN_LISTEN | :80 | HTTP listener used for redirect and ACME HTTP-01. |
TGWP_BEHIND_PROXY | false | Run behind nginx/Caddy/another TLS frontend. |
TGWP_DIAGNOSE | false | Enable diagnostic request logging. |
TGWP_LOG_LEVEL | info | debug, info, warn, or error. |
When the domain is opened in a regular browser, the proxy can show a normal static website.
Create:
site/
├── index.html
├── style.css
└── ...
Then mount it into the container:
services:
tgwebproxy:
image: powernic/tgwebproxy:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
TGWP_DOMAIN: "${TGWP_DOMAIN}"
TGWP_SECRET: "${TGWP_SECRET}"
TGWP_SITE: "/site"
TGWP_LOG_LEVEL: "info"
volumes:
- tgwebproxy-certs:/certs
- ./site:/site:ro
volumes:
tgwebproxy-certs:
A simple site/index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome</title>
</head>
<body>
<h1>Welcome</h1>
<p>This website is currently under construction.</p>
</body>
</html>
Using a realistic cover website is recommended instead of leaving the default placeholder.
Ports 80/tcp and 443/tcp must be reachable from the Internet.
For UFW:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Check listening ports:
ss -lntp | grep -E ':80|:443'
Check container status:
docker ps
Follow logs:
docker logs -f tgwebproxy
or with Compose:
docker compose logs -f tgwebproxy
For normal production use:
TGWP_LOG_LEVEL: "info"
For troubleshooting:
TGWP_LOG_LEVEL: "debug"
Avoid leaving debug/diagnostic logging enabled unnecessarily.
The WEB proxy capability should be treated as a credential and should not be exposed in access logs or public configuration files.
If port 443 is already occupied by nginx, Caddy, Traefik or another reverse proxy, enable:
environment:
TGWP_DOMAIN: "${TGWP_DOMAIN}"
TGWP_SECRET: "${TGWP_SECRET}"
TGWP_BEHIND_PROXY: "true"
In this mode TLS termination is handled by the frontend proxy.
The frontend must support and forward WebSocket upgrade headers.
When using a reverse proxy, make sure sensitive query parameters are not written to public or long-lived access logs.
openssl rand -hex 16
TGWP_SECRET..env to a public repository./certs volume persistent.443.Add .env to .gitignore:
.env
Pull the newest image:
docker compose pull
Recreate the container:
docker compose up -d
Remove unused old images:
docker image prune
tgwebproxy/
├── compose.yml
├── .env
└── site/
└── index.html
Start:
cd tgwebproxy
docker compose up -d
Check:
docker compose ps
docker compose logs --tail=100
The Docker image packages tgwebproxy.
Please refer to the upstream project for source code and licensing information:
Content type
Image
Digest
sha256:a9d3d463e…
Size
10.4 MB
Last updated
17 days ago
docker pull powernic/tgwebproxy