A security-focused reverse proxy and web server — a single-binary alternative to nginx + Traefik with native bot detection and AI crawler prevention built into the request pipeline.
- Go 1.23+ (only if building from source)
- mkcert (development only)
You can download pre-compiled binaries and packages for your operating system from the Releases page.
Download the .deb package and install it (replace `1.11 with the latest version):
wget https://github.com/carlHandy/go-tinyproxy/releases/download/v1.1.1/go-tinyproxy_1.1.1_linux_amd64.deb
sudo dpkg -i go-tinyproxy_1.1.1_linux_amd64.debDownload the .rpm package and install it (replace 1.0.0 with the latest version):
wget https://github.com/carlHandy/go-tinyproxy/releases/download/v1.1.1/tinyproxy_1.1.1_linux_amd64.rpm
sudo rpm -i tinyproxy_1.0.0_linux_amd64.rpmDownload the .tar.gz archive, extract it, and move the binary to your path:
wget https://github.com/carlHandy/go-tinyproxy/releases/download/v1.1.1/tinyproxy_1.1.1_linux_amd64.tar.gz
tar -xzf tinyproxy_1.0.0_linux_amd64.tar.gz
sudo mv tinyproxy /usr/local/bin/(Note: If you are on macOS or an ARM64 machine, make sure to grab the darwin or arm64 archive instead!)
Download tinyproxy_1.0.0_windows_amd64.zip from the releases page and extract it.
Do not double-click
tinyproxy.exe— Windows will open and immediately close the console window before you can see any output. Always run it from a Command Prompt or PowerShell session:
# Open PowerShell or Command Prompt, cd to the extracted folder, then:
.\tinyproxy.exe serveIf the window still flashes and exits, the most common cause is a missing or invalid config/vhosts.conf. Make sure the config/ directory (with a valid vhosts.conf) is in the same folder as the binary before running.
If you prefer to compile it yourself:
git clone https://github.com/carlHandy/go-tinyproxy.git
cd go-tinyproxy
go build -o tinyproxy ./cmd/tinyproxy/
sudo mv tinyproxy /usr/local/bin/When installed via .deb or .rpm, go-tinyproxy runs as a systemd service and starts automatically on boot. Use the built-in CLI to manage it:
go-tinyproxy start # start the service
go-tinyproxy stop # stop the service
go-tinyproxy restart # restart the service
go-tinyproxy reload # reload config without downtime (sends SIGHUP)
go-tinyproxy status # show service status
go-tinyproxy config # open /etc/go-tinyproxy/vhosts.conf in $EDITOR (falls back to nano)
go-tinyproxy logs # tail live logs via journalctl
go-tinyproxy upgrade # download and install the latest release, then restartAfter editing the config, apply changes without restarting:
go-tinyproxy config # edit the file
go-tinyproxy reload # pick up the changes instantlyBefore you configure any virtual hosts, visiting your server's IP in a browser will:
- Redirect HTTP → HTTPS automatically
- Serve a built-in default page over TLS
Note: Direct IP access uses a self-signed certificate (ACME cannot issue certs for IPs), so your browser will show a security warning. This is expected until you point a domain at the server and configure a vhost.
Dev mode listens on :8080. Both http://localhost:8080 and https://localhost:8080 work — plain HTTP connections are automatically redirected to HTTPS. Generate the local certificates once with mkcert:
mkcert localhost 127.0.0.1 ::1
mkdir -p certs
mv localhost+2.pem certs/
mv localhost+2-key.pem certs/Then start the server:
ENV=dev go run ./cmd/tinyproxy/Production mode listens on :443 with automatic TLS via Let's Encrypt (ACME), and spins up an HTTP→HTTPS redirect on :80.
go-tinyproxy(Note: If not installed via a package manager to run as a service, you may need to run with sudo to bind to ports 80 and 443).
Certificates are obtained automatically for every domain defined in config/vhosts.conf and cached in the certs/ directory. The server must be publicly reachable on port 80 for the ACME challenge.
Edit config/vhosts.conf. The format is a custom block DSL — not YAML or TOML.
vhosts {
example.com {
port 443
proxy_pass http://backend:8080
}
}
vhosts {
example.com {
port 80
root /var/www/html
}
}
vhosts {
example.com {
port 80
root /var/www/html
fastcgi {
pass 127.0.0.1:9000
index index.php
param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name
}
}
}
vhosts {
example.com {
port 443
proxy_pass http://backend:8080
ssl {
cert /etc/certs/example.com.crt
key /etc/certs/example.com.key
}
}
}
vhosts {
example.com {
proxy_pass http://backend:8080
socks5 {
address 127.0.0.1:1080
username proxy_user
password proxy_pass
}
}
}
vhosts {
example.com {
proxy_pass http://backend:8080
security {
frame_options DENY
content_type nosniff
xss_protection "1; mode=block"
csp "default-src 'self'"
hsts "max-age=31536000; includeSubDomains"
rate_limit {
requests 100
window 1m
}
}
}
}
Defaults applied to every vhost: X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Strict-Transport-Security: max-age=31536000; includeSubDomains, 100 req/min rate limit, 10 MB max body size.
Bot detection is opt-in per vhost. Enable it with a bot_protection block:
vhosts {
example.com {
proxy_pass http://backend:8080
bot_protection {
enabled true
block_scanners true
honeypot true
}
}
}
enabled — activates the middleware for this vhost.
block_scanners — intercepts requests to known vulnerability-scanning paths: /.env, /.git, /wp-admin, /phpMyAdmin, /actuator, /etc/passwd, and others. Handles URL-encoded variants (/.%65nv) and path normalisation tricks (//wp-admin).
honeypot — instead of returning 403, serve convincing fake content tailored to the requested path (fake .env with bogus credentials, fake WordPress login, fake phpMyAdmin, fake Spring Boot actuator JSON, etc.). Adds a random 150–750 ms delay to slow down automated scanners. Every hit is logged with HONEYPOT prefix for easy filtering. When omitted, blocked requests receive a plain 403.
block_path <path> — block a path prefix that isn't in the built-in list. Applies the same URL-decode and path-normalisation logic as block_scanners, and respects honeypot. Repeat the directive for multiple paths:
bot_protection {
enabled true
block_scanners true
honeypot true
block_path /jenkins
block_path /phpMyAdmin
block_path /actuator
block_path /wp-admin
}
block <token> — add extra User-Agent substrings to block beyond the built-in list:
bot_protection {
enabled true
block MyCustomScraper
}
allow <token> — permanently allow a User-Agent substring, overriding all block rules:
bot_protection {
enabled true
allow FriendlyPartnerBot
}
AI crawlers and scrapers blocked by default when enabled true:
| Category | Agents |
|---|---|
| AI crawlers | GPTBot, ClaudeBot, CCBot, PerplexityBot, YouBot, anthropic-ai, cohere-ai, Bytespider |
| SEO bots | AhrefsBot, SemrushBot, MJ12bot, DotBot, PetalBot |
| Scrapers | python-requests, Scrapy, libwww-perl, masscan, zgrab |
Always permitted through, regardless of block rules:
Googlebot, bingbot, DuckDuckBot, Slurp (Yahoo), Baiduspider, facebookexternalhit, Twitterbot, LinkedInBot, Applebot
Allowlist matching uses word-boundary detection — a UA like EvilGooglebot/1.0 is not treated as Googlebot.
All TLS connections enforce:
- TLS 1.2 minimum
- Forward-secret cipher suites only (ECDHE-AES-GCM, ECDHE-ChaCha20-Poly1305)
- Preferred curves: X25519, P-256
# Build
go build ./cmd/tinyproxy/
# Run tests
go test ./...
# Vet
go vet ./...