A Go port of WebOne, the HTTP 1.x proxy
that makes old web browsers, media players and messengers usable again on
today's web. Old browsers ask the proxy for http:// pages. The proxy fetches
them over modern HTTPS, patches what old browsers choke on, converts the text
to a code page they can read, and sends it back as HTTP/1.0 or 1.1.
D-Modem uses it so that callers dialling into the BBS can browse the web from period machines. The proxy can sign its HTTPS certificates with the BBS's own CA, so callers only have to install one certificate.
WebOne is written in C# by Alexander Tauenis and the WebOne Development Team,
under the BSD-style licence in LICENSE.txt. This repository is
a derivative work. It follows WebOne 0.19.0 at upstream commit
e940b43
(22 October 2026). The Go code is a new translation of that C#, file by file:
HttpTransit.cs became internal/proxy/transit.go, ConfigFileLoader.cs
became internal/config/loader.go, and so on. The files below are WebOne's
own and are kept as upstream has them, except where noted:
LICENSE.txt, unchanged. Its last clause applies to this port too: all users of a service based on this software must be told that their traffic is changed, and about the security problems that brings.webone.conf,codepage.conf,escargot.conf: WebOne's configuration and sets of edits.webone.confhas the Go port's options added as comments.html/(internal and error pages, icons) andlogo.webp.webone.service,webone.logrotate. The service file drops the .NET-onlyOPENSSL_CONFline and gains a note about running as the BBS's user.
The WebOne wiki documents the configuration, and that documentation holds for this port. The differences are listed below.
Go 1.26 or later:
go build -o webone ./cmd/webone
./webone # finds webone.conf (here, next to the program, ~/.config/webone, /etc)
./webone 8080 # on another port
./webone -cfg /etc/webone.conf -l /var/log/webone.log
./webone --helpOr with make:
make # builds ./webone
make test # gofmt, vet and the tests
sudo make install # see below
make dist GOOS=linux GOARCH=386 # a tarball for another machinemake install puts the program, html/ and logo.webp in
/usr/local/lib/webone and links /usr/local/bin/webone to it. It also
installs /etc/webone.conf, /etc/webone.conf.d/codepage.conf and
escargot.conf (leaving any that are already there), the systemd unit and
the logrotate rule. PREFIX, SYSCONFDIR and DESTDIR change where things
go, and make uninstall removes everything but the configuration.
The html/ directory goes next to the program, or wherever ContentDirectory=
points. Then set the old browser's HTTP proxy to host:8080, or its automatic
configuration URL to http://host:8080/auto.pac. Browsers with no proxy
setting can use http://host:8080/http://example.com/. Open
http://host:8080/ to see the proxy's status page.
What works:
- All three ways of asking: as a proxy, as a web server for its own pages,
and the
http://proxy/http://site/alternate mode. CERN-style FTP is supported too. - HTTP/1.0 and HTTP/1.1 clients, with keep-alive. The reply uses chunked transfer only for HTTP/1.1 clients.
- Fetching over HTTPS, HTTP/2 and HTTP/3 (see HTTP/3 and gRPC), with HTTP→HTTPS upgrades,
[ForceHttps], and redirect loop ("carousel") handling. - Sets of edits (
[Edit:...]): every condition and action WebOne has, .NET regular expressions included (through regexp2). - Code pages:
OutputEncoding,[Translit], andcodepage.conf. Windows, DOS, Mac, ISO and KOI8 code pages are included, as are UTF-16 and the CJK ones. - The HTTPS proxy (CONNECT): man-in-the-middle with a per-site
certificate, for clients from SSL 2.0 to TLS 1.3 (see Old SSL
clients), pass-through (
[ConnectPassThrough]), non-HTTP tunnels ([NonHttpSslServers],[NonHttpConnectRedirect]) and[NonHttpSslDecrypt]. - The CA: made on first start, or
--rebuild-ca. MD5, SHA-1 or SHA-2 signatures. External generators (SslSiteCerts,SslSiteCertGenerator)./!caand/weboneca.pemserve the CA certificate. - Internal pages: status,
/!codepages, PAC/WPAD,/!ftp/(the web FTP client),/!convert/and on-the-fly converters (ImageMagick and others),/!webvideo/and/!player/(yt-dlp and ffmpeg),/!redirect/,/!clear/, and thehtml/pages and error pages. - The Web Archive fallback, proxy logins, IP and URL lists, traffic dumps
(
--dump), ActiveX codebase emulation, and log files.
The BBS keeps a CA (see the BBS's Certificates wiki page). Callers install
its root, ca/ca.crt, from ca/install-ca.zip. Give the proxy a sub-CA of
its own:
bbs ca subca proxy # makes ca/proxy-ca.crt and ca/proxy-ca.keyand point the proxy at the directory:
[SecureProxy]
SslEnable=yes
SslCaDirectory=/var/lib/bbs/ca ; the BBS's ca/ directory, beside its database
;SslHashAlgorithm=SHA1 ; leave out to sign as the sub-CA is signed
[Server]
DnsServer=127.0.0.1:53 ; optional: look sites up through the BBS's DNSWith SslCaDirectory set:
- The proxy signs each site's certificate with
proxy-ca.key, and sendsproxy-ca.crtafter it. Callers who installed the BBS root trust both. /!cahands out the rootca.crt, which is the one callers install.- Unless
SslHashAlgorithmis set, certificates are signed with the sub-CA's own algorithm. A CA made withbbs ca init -sha1therefore gives SHA-1 certificates, which Windows 95 to XP SP2 need. - Site certificates last at most 397 days. They are cached, and each subject keeps one serial number.
- The BBS's files are never changed, and
--rebuild-carefuses to touch them. A directory with noproxy-cafalls back to signing with the root'sca.key, and logs a warning. Making the sub-CA is the better way.
Permissions. The BBS keeps ca/ at mode 0700 and the keys at 0600, owned
by the user in its [general] section. Either run the proxy as that user
(see webone.service), or give the proxy's user read access to
ca/proxy-ca.key, ca/proxy-ca.crt and ca/ca.crt (for example, through a
shared group), plus search access to ca/. The proxy says plainly when it
cannot read them.
SSL 2.0 sends only the site's own certificate, not the sub-CA after it. So
callers whose browsers speak only SSL 2.0 must install ca/proxy-ca.crt as
well as the root.
SslTrustOwnCA=yes makes the proxy also trust that CA when it fetches pages
itself. Use it for sites the BBS serves under its own CA.
Go's own TLS starts at TLS 1.0 and does not accept an SSL 2.0-format hello. For those clients, the CONNECT/MITM mode uses retrossl, D-Modem's SSL 2.0, SSL 3.0 and TLS 1.0 server. The proxy looks at each client's first bytes:
- An SSL 2.0-format hello (Netscape 1 to 4, IE 2 to 6, AIM, early Opera), or a hello that offers SSL 3.0 at best, goes to retrossl.
- Everything else goes to Go's TLS, as before.
SslProtocols decides what is served: Ssl2 and Ssl3 turn on SSL 2.0 and
3.0, and Tls turns on TLS 1.0, for both servers. Without SslProtocols,
SSL 3.0 and TLS 1.0 to 1.3 are on, and SSL 2.0 is off. SslCipherSuites
also accepts retrossl's suite names, such as TLS_RSA_EXPORT_WITH_RC4_40_MD5.
retrossl offers AES, 3DES, RC4, IDEA, RC2 and DES, and the 40- and 56-bit
export suites for export builds of old browsers. Sessions resume, which
saves slow machines an RSA operation on every connection, and clients that
support secure renegotiation may renegotiate. Site certificates must have RSA keys,
which the proxy's do. These protocols are easy to break, so the traffic
between an old browser and the proxy should be treated as readable. Clients
that use the plain HTTP proxy instead are not affected: the browser asks for
http://, and the proxy fetches over modern HTTPS.
HTTP/3. The proxy fetches sites over HTTP/3 (QUIC, through
quic-go) the way browsers do: a site
is first reached over TCP, and once it says in Alt-Svc that it has HTTP/3,
later requests to it use QUIC. If QUIC fails (UDP blocked, say), the site
goes back to TCP for ten minutes, and a request without a body is retried
there at once. RemoteHttpVersion (and AddRequestHttpVersion in edit
sets) now takes 3.0 too:
| Rule | Toward servers |
|---|---|
auto (default), >1.1 |
HTTP/1.1, HTTP/2, and HTTP/3 once a site offers it |
=3.0 or >2.0 |
HTTP/3 only, straight away (fails where a site has none) |
<3.0 |
HTTP/1.1 and HTTP/2, never HTTP/3 |
=1.1, <2.0 |
HTTP/1.1 only |
HTTP/3 needs TLS 1.3, so it is off when SecurityProtocols leaves TLS 1.3
out, and it is not used through an UpperProxy (QUIC can't go through an
HTTP proxy). DnsServer applies to it as to TCP.
gRPC. gRPC needs HTTP/2 from end to end, trailers included, so it can't
go through the proxy's HTTP/1 handling. It goes through CONNECT instead,
which is what gRPC clients do with a proxy (https_proxy=http://host:8080):
- A CONNECT to port 443 that the proxy decrypts: a client that offers only
h2in ALPN (as gRPC clients do) gets HTTP/2 from the proxy, and its requests go on to the site over HTTP/2 untouched, streamed both ways with their trailers. The URL lists still apply; edit sets do not. Browsers offer HTTP/1.1 too (or no ALPN at all), so they are handled as before. [NonHttpSslServers]tunnels: the proxy now asks the server for what the client asked for in ALPN and gives the client the server's answer, so gRPC (and anything else that needs ALPN) works through them.[ConnectPassThrough]and plain non-HTTP tunnels pass bytes as they are, so gRPC already worked there.
gRPC-Web runs over HTTP/1.1 and goes through the ordinary proxy.
UseMsHttpApi(Windows' HTTP.sys server) does not exist here. It is ignored with a warning, and the proxy always uses its own server, as WebOne does by default.MultipleHttp2Connectionsis accepted, but Go decides this itself.
These are bugs or rough edges in WebOne 0.19.0 that the port does not copy:
- Edit sets'
OnContentTypemasks are tested against the response's content type in the right order. %UrlNoDomain%,%UrlNoQuery%,%UrlNoPort%,%UrlHttp%and%UrlHttps%no longer produce//or??.- Several
Set-Cookieheaders reach the client as separate lines, instead of joined with commas. Connection: Keep-Aliveis sent only to clients that keep connections: ones that ask for it, and HTTP/1.1 ones unless they sayclose. Other clients getConnection: Close. WebOne promises keep-alive to everyone and then closes.- Replies to
HEAD, and204/304replies, have no body or chunk terminator. - Request bodies sent chunked are accepted (WebOne has this stubbed out).
$Variablenames are replaced longest first, so$UA-Firefoxis not cut short by$UA.OpenForLocalIPsdecides what is "local" from the address (loopback, private, link-local) instead of pinging the client with a TTL of 1.- Error pages escape what they pass to
html/Err-*.htm, so a URL with&in it is shown whole. - The web FTP client reads multi-line FTP replies properly instead of timing
them. It accepts
125as well as150, and falls back to the control connection's host when a server behind NAT gives a private address for passive mode. - Code pages round-trip: bytes a code page leaves undefined come back as the same bytes, as .NET does.
/!webvideo/withdump-jsonreturns yt-dlp's output, where WebOne returned its error stream.
/!convert/?src=opens only files in the proxy's own directory (no paths). WebOne opens any path it is given./!webvideo/does not take yt-dlp options that read or write files or run programs (exec,output,batch-file,config-locations,cookies,netrc-cmdand similar) from the URL. They still work from[WebVideoOptions]in the configuration.- Converters that hang are stopped when the client goes away. WebOne instead measures CPU use with Windows performance counters.
| Option | Section | What it does |
|---|---|---|
SslCaDirectory |
[SecureProxy] |
sign with the D-Modem BBS's CA (above) |
SslTrustOwnCA |
[SecureProxy] |
trust the proxy's CA for fetched sites too |
DnsServer |
[Server] |
look sites up through this DNS server (host or host:port) |
RemoteHttpVersion also takes 3.0 (see HTTP/3 and gRPC).
SslProtocols and SslCipherSuites work as in WebOne, with SSL 2.0 and 3.0
served by retrossl (above).
The version is reported as 0.19.0-go.
cmd/webone the program: command line, CA loading, start-up
internal/config webone.conf: loading, [Include:], variables, sets of edits
internal/proxy the server, request handling, CONNECT (old SSL through retrossl, HTTP/2 for gRPC), HTTP/3 fetching, internal pages, FTP, video
internal/certs the CA, site certificates (MD5/SHA-1 signing), the BBS's CA
internal/textenc code pages
internal/rx .NET-style regular expressions, cached
internal/logx the log, in WebOne's format
go test ./... covers configuration parsing, edit sets, code pages, the
certificates (including a BBS-style sub-CA chain), and the proxy end to end:
HTTP/1.0 with edits and a code page, HTTPS fetching, alternate mode,
keep-alive with chunked replies, internal pages, CONNECT with MITM (SSL
2.0 and 3.0 clients included), gRPC-style HTTP/2 streams through CONNECT,
HTTP/3 fetching after Alt-Svc, and pass-through. retrossl has its own tests.
This port is under the same terms as WebOne: see LICENSE.txt. Copyright (C) 2019+, WebOne Development Team. The Go port is copyright the D-Modem project contributors.