Β
A simple, self-hostable relay server that acts as a dumb encrypted mailbox. Any application can use it to pass encrypted data between users. The server never sees plaintext β all encryption/decryption happens client-side.
- Encrypted Message Relay: Store and retrieve opaque encrypted payloads via simple REST endpoints
- Self-Hostable: Single binary, embedded SQLite, zero external dependencies
- Configurable TTL: Auto-purge messages after N days, or set to 0 for permanent storage
- Rate Limiting: Built-in IP-based rate limiting to prevent abuse
- API Key Auth: Optional API key authentication for private relays
- Docker Ready: Dockerfile and Docker Compose included
- Go 1.22+
- SQLite (embedded, WAL mode) via
mattn/go-sqlite3 net/http(standard library)
- Docker + Docker Compose
- GitHub Actions CI/CD
graph TD
Client["Client / SDK"] -->|HTTP| MW0["CORS"]
MW0 --> MW1["API Key Middleware"]
MW1 --> MW2["Rate Limiter"]
MW2 --> Mux["net/http ServeMux"]
Mux -->|"POST /api/messages"| H1["Create Message"]
Mux -->|"GET /api/messages/:address"| H2["Get by Address"]
Mux -->|"GET /health"| H4["Health Check"]
H1 & H2 --> Store["Store Interface"]
Store --> SQLite["SQLiteStore (WAL mode)"]
BG["Hourly Purge Goroutine"] --> Store
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/messages |
Store a new encrypted message |
GET |
/api/messages/:address |
Fetch messages for a wallet address (optional ?after=<seq>&limit=<n>) |
GET |
/health |
Server health check |
curl -X POST http://localhost:3000/api/messages \
-H "Content-Type: application/json" \
-d '{"to": "0xRecipient", "from": "0xSender", "payload": "encrypted_base64_data"}'Returns 201 Created with the stored message:
{
"id": "3f0cβ¦e91a",
"seq": 1,
"to": "0xRecipient",
"from": "0xSender",
"payload": "encrypted_base64_data",
"createdAt": "2026-10-26T10:00:00Z",
"expiresAt": "2026-11-02T10:00:00Z"
}idis the hex SHA-256 ofto,fromandpayload, so the same payload sent to two recipients gets two IDs. Sending an identical message again is safe: nothing new is stored, and the server answers200 OKwith the message it already has.seqis the message's position in the recipient's mailbox: 1, 2, 3, β¦ per address. Numbers only go up and are never reused, even after a message expires.
# Everything in the mailbox
curl http://localhost:3000/api/messages/0xRecipient
# Only messages newer than the last one you handled
curl "http://localhost:3000/api/messages/0xRecipient?after=42"
# At most 100 at a time
curl "http://localhost:3000/api/messages/0xRecipient?after=42&limit=100"Messages come back oldest first, ordered by seq. To avoid downloading the same messages on every poll, a client remembers the highest seq it has handled and passes it as after. The cursor belongs to the client, so each device reading the same mailbox keeps its own.
| Query parameter | Meaning |
|---|---|
after |
Only return messages with a seq greater than this. Default 0 (everything). |
limit |
Return at most this many messages, from 1 to 1000. Omitted, everything matching is returned. If you get a full page, ask again with after set to the last seq you received. |
- ThruBox Server β This repository (relay server)
- ThruBox Client β TypeScript SDK
- Go 1.22+ with CGo enabled (required for SQLite)
- GCC (for compiling go-sqlite3)
- Docker (optional, for containerized deployment)
git clone https://github.com/AOSSIE-Org/ThruBox-Server.git
cd ThruBox-Servergo mod downloadgo build -o relay-server ./cmd/relay
./relay-serverThe server starts on http://localhost:3000 with a SQLite database that auto-creates at ./data/relay.db.
docker compose up -dThruBox reads config.yaml from the working directory. Set RELAY_CONFIG_PATH
to load it from anywhere else:
RELAY_CONFIG_PATH=/etc/relay/config.yaml ./relay-serverThe image sets this for you and ships the file at /etc/relay/config.yaml, so
you can mount your own over it:
docker run -v ./my-config.yaml:/etc/relay/config.yaml:ro ghcr.io/aossie-org/thrubox-serverIf no config file is found at the resolved path the server logs a warning at startup and runs on built-in defaults rather than failing. Precedence is built-in defaults β the selected YAML file β environment variables, so an environment variable always wins over the file.
Exposing to the internet? See Exposing to the Internet (Self-Hosting) for complete step-by-step guides using Nginx reverse proxy or Cloudflare Tunnel.
go test ./...Tests live beside the code they cover. See
CONTRIBUTING.mdbefore submitting a PR that adds functionality without tests.
Edit config.yaml or use environment variables:
| Setting | YAML Key | Env Variable | Default |
|---|---|---|---|
| Config file path | β | RELAY_CONFIG_PATH |
config.yaml (/etc/relay/config.yaml in the Docker image) |
| Server port | server.port |
RELAY_SERVER_PORT |
3000 |
| Server port (fallback) | β | PORT |
unset β used when RELAY_SERVER_PORT is unset or empty |
| Server host | server.host |
RELAY_SERVER_HOST |
0.0.0.0 |
| Storage path | storage.path |
RELAY_STORAGE_PATH |
./data/relay.db |
| Message TTL | messages.ttl_days |
RELAY_MESSAGES_TTL_DAYS |
7 (0 = keep forever; the database then only grows) |
| Max payload | messages.max_payload_size |
RELAY_MESSAGES_MAX_PAYLOAD_SIZE |
524288 (500KB) |
| Rate limit | security.rate_limit |
RELAY_SECURITY_RATE_LIMIT |
30 req/min/IP |
| Trusted proxies | security.trusted_proxies |
RELAY_SECURITY_TRUSTED_PROXIES |
0 (ignore X-Forwarded-For) β see Behind a reverse proxy |
| API key | security.api_key |
RELAY_SECURITY_API_KEY |
`` (disabled) |
| CORS origins | security.allowed_origins |
RELAY_SECURITY_ALLOWED_ORIGINS |
`` (CORS disabled) |
The rate limiter counts requests per client IP. Where that IP comes from depends on security.trusted_proxies:
0(default) β the address of whoever opened the connection.X-Forwarded-ForandX-Real-IPare ignored, so a client cannot fake its IP. Use this when clients connect to ThruBox directly.Nβ the relay sits behindNproxies, and the client IP is theNth entry from the right ofX-Forwarded-For. Each proxy appends the address it received the request from, so the rightmostNentries were written by your proxies; anything further left came from the client and is never read.
Set this if you run behind a proxy. With the default 0, every request appears to come from the proxy's address, so all your users share one rate-limit bucket and get 429 Too Many Requests together. This covers Nginx, Cloudflare Tunnel, Docker port publishing behind a proxy, and hosting platforms such as Render, Railway or Fly.io.
A single proxy in front of ThruBox (Nginx, a platform's load balancer) is trusted_proxies: 1. If you are unsure how many hops your platform adds, set rate_limit low for a moment, make a few requests and read the ip in the rate limit exceeded log line: it should be your own public IP, not a private or proxy address.
Setting the value higher than the real number of proxies lets clients choose their own IP again, so do not overestimate it.
Block direct access. A non-zero trusted_proxies is only safe if every request reaches ThruBox through your proxy. A client that can connect to ThruBox directly can send its own X-Forwarded-For and pick any IP, which bypasses the rate limit again. When you set trusted_proxies above 0, make sure only the proxy can reach ThruBox: publish the container port on 127.0.0.1 only (-p 127.0.0.1:3000:3000, as in the guides below), or firewall the port. Note that the bundled docker-compose.yml publishes 3000:3000 on all interfaces; change it to 127.0.0.1:3000:3000 when running behind a proxy. On hosting platforms that route every request through their load balancer (such as Render), there is no direct path to worry about.
By default the relay serves no CORS headers, so a browser calling it from another origin is blocked at the preflight. There are two ways to run a browser client:
1. Reverse proxy (no relay configuration). Put the relay behind a
same-origin path in your own app β a Vercel rewrite, an nginx location, a
Vite server.proxy entry. The request is never cross-origin, so CORS never
applies. This is the setup the ThruBox client docs assume (see Option 1: Nginx Reverse Proxy).
2. Direct calls with an origin allowlist. List the origins you want to serve and browsers can call the relay directly, no proxy needed:
security:
allowed_origins:
- "https://app.example.com"
- "http://localhost:5173"or via the environment, comma-separated:
RELAY_SECURITY_ALLOWED_ORIGINS="https://app.example.com,http://localhost:5173"When an origin is allowed, the relay answers preflights and returns
Access-Control-Allow-Origin for that origin, Access-Control-Allow-Methods: GET, POST, OPTIONS, and Access-Control-Allow-Headers: Content-Type
β plus X-API-Key when security.api_key is set.
Notes:
- Entries must be full origins (
https://host[:port]), with no path. A bare hostname or a URL with a path is rejected at startup rather than silently never matching. "*"allows any origin. It cannot be combined with specific origins, and it is a poor fit for a relay with no API key configured β anything on the web can then read and write messages from a browser.- Credentialed CORS is not supported. The relay authenticates with the
X-API-Keyheader, not cookies, soAccess-Control-Allow-Credentialsis never sent. - An allowlisted origin still has to satisfy
security.api_keyand the rate limiter. CORS controls which origins a browser will let read a response; it is not authentication.
Deploying to a managed platform? Render, Railway, Heroku and Cloud Run inject a
PORTvariable and expect the process to bind to it. ThruBox reads it automatically, so no extra configuration is needed.RELAY_SERVER_PORTstill takes precedence whenever it is set to a non-empty value, so setting it to""(as an emptydocker-composeentry does) falls through toPORT.
When self-hosting ThruBox Server with Docker, the container runs locally and listens on port 3000 by default (as configured by server.port in config.yaml or RELAY_SERVER_PORT). To make your relay accessible to clients and client SDKs over the public internet, you should place it behind either a reverse proxy or a secure tunnel.
Internet Requests (HTTPS)
β
βΌ
ββββββββββββββββββββββββββββββββββββββββ
β Option 1: Nginx Reverse Proxy β (Public IP, ports 80/443, Certbot SSL)
β OR β
β Option 2: Cloudflare Tunnel β (No open inbound ports, outbound tunnel)
ββββββββββββββββββββ¬ββββββββββββββββββββ
β HTTP (localhost:3000)
βΌ
ββββββββββββββββββββββββββββββββββββββββ
β Local ThruBox Server (Docker) β (Container listening on port 3000)
ββββββββββββββββββββββββββββββββββββββββ
Important
Port Configuration & Security:
- Default Port: All examples below use port
3000, the default configured byserver.portinconfig.yamlor theRELAY_SERVER_PORTenvironment variable. If you configure a different port, you must update:- The Docker container port mapping (e.g.,
-p 127.0.0.1:<PORT>:<PORT>orports: - "127.0.0.1:<PORT>:<PORT>"). - The reverse proxy / tunnel upstream targets (e.g.,
proxy_pass http://127.0.0.1:<PORT>;in Nginx,service: http://localhost:<PORT>inconfig.yml, orURL: relay:<PORT>in Docker Compose).
- The Docker container port mapping (e.g.,
- Local Binding Only: Never expose the raw ThruBox container port directly to the public internet (
0.0.0.0:3000). Bind the port strictly to127.0.0.1:3000:3000(or127.0.0.1:<PORT>:<PORT>) on your host so only local services (such as Nginx orcloudflared) can communicate with it.
-
In your
docker-compose.yml, restrict the port binding to127.0.0.1:services: relay: build: . ports: # Port 3000 is the default (server.port / RELAY_SERVER_PORT). # If using another port, update both host and container port values. - "127.0.0.1:3000:3000" volumes: - relay-data:/data environment: - RELAY_STORAGE_PATH=/data/relay.db restart: unless-stopped volumes: relay-data:
Or run the container using
docker run:# Note: 3000 is the default port. If configured differently via RELAY_SERVER_PORT, update -p 127.0.0.1:<PORT>:<PORT>. docker run -d \ --name thrubox-relay \ -p 127.0.0.1:3000:3000 \ -v relay-data:/data \ ghcr.io/aossie-org/thrubox-server:latest -
Confirm the server is running and healthy on
localhost:3000(or your configured port):curl http://127.0.0.1:3000/health
Expected response:
{"status":"ok","timestamp":"2026-10-05T13:14:15Z"}
Choose one of the two deployment methods below:
- Option 1: Nginx Reverse Proxy: Best if you have a public IP address and can open/forward inbound ports
80and443. - Option 2: Cloudflare Tunnel: Best if you are behind CGNAT, on a home network, or cannot/prefer not to open inbound router ports.
Nginx acts as a front-facing web server that accepts HTTPS connections from the internet, handles TLS encryption/decryption, and forwards requests to the local ThruBox container on http://127.0.0.1:3000.
- A Linux server with Docker and ThruBox Server running locally on
http://127.0.0.1:3000. - A registered domain or subdomain (e.g.,
relay.example.com) with a DNSAorAAAArecord pointing to your server's public IP address. - Inbound ports
80(HTTP) and443(HTTPS) open on your firewall and forwarded on your router (if hosting from a local/home network).
# Debian / Ubuntu
sudo apt update && sudo apt install -y nginx
# RHEL / Fedora / AlmaLinux
sudo dnf install -y nginxEnable and start Nginx:
sudo systemctl enable --now nginxCreate a new configuration file for ThruBox:
- Debian / Ubuntu:
/etc/nginx/sites-available/thrubox.conf - RHEL / Fedora:
/etc/nginx/conf.d/thrubox.conf
server {
listen 80;
listen [::]:80;
# Replace with your actual domain or subdomain
server_name relay.example.com;
# Allow request payloads matching or exceeding ThruBox max_payload_size (default: 500KB)
client_max_body_size 10M;
location / {
# 3000 is the default port (server.port / RELAY_SERVER_PORT).
# Update this upstream target if running on a custom port (e.g. http://127.0.0.1:<PORT>).
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Forward request metadata for logging and accurate rate limiting
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Overwrite X-Forwarded-For with $remote_addr to prevent clients from spoofing their IP to bypass rate limits
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# Proxy send/read timeouts aligned with the server's 15s read/write timeouts (server idle timeout is 60s)
proxy_connect_timeout 60s;
proxy_send_timeout 15s;
proxy_read_timeout 15s;
}
}Key Configuration Directives Explained:
server_name relay.example.com;: Tells Nginx which domain name this server block handles. Replacerelay.example.comwith your actual domain or subdomain.client_max_body_size 10M;: Sets the maximum allowed HTTP request body size. Nginx's default is1M. Since ThruBox accepts encrypted payloads up tomessages.max_payload_size(default: 500KB, configurable), setting this prevents Nginx from rejecting valid large payloads with413 Request Entity Too Large.proxy_pass http://127.0.0.1:3000;: Proxies incoming requests to the ThruBox Docker container. Note that3000is the default port configured byserver.portorRELAY_SERVER_PORT; if you configure another port, update this upstream target (http://127.0.0.1:<PORT>).proxy_set_header X-Real-IP $remote_addr;andproxy_set_header X-Forwarded-For $remote_addr;: Passes the real client IP address. OverwritingX-Forwarded-Forwith$remote_addr(rather than appending via$proxy_add_x_forwarded_for) leaves a single, trustworthy entry. ThruBox only reads it when told it is behind a proxy, so also start ThruBox withRELAY_SECURITY_TRUSTED_PROXIES=1(orsecurity.trusted_proxies: 1); otherwise every request appears to come from Nginx and all clients share one rate-limit bucket. See Behind a Reverse Proxy.proxy_set_header HostandX-Forwarded-Proto: Preserves the original requested host and protocol (HTTP vs HTTPS).proxy_connect_timeout 60s;,proxy_send_timeout 15s;, andproxy_read_timeout 15s;: Sets timeouts for connecting to the backend container (60s connection establishment) and transmitting/reading requests (15s send/read, aligned with ThruBox Server's 15s read/write timeouts; server idle timeout is 60s).
On Debian / Ubuntu, enable the site by linking it into sites-enabled:
sudo ln -s /etc/nginx/sites-available/thrubox.conf /etc/nginx/sites-enabled/Test the configuration for syntax errors:
sudo nginx -tReload Nginx to apply changes:
sudo systemctl reload nginxSecuring relay traffic with TLS is critical to ensure that opaque encrypted payloads and metadata are transmitted securely over HTTPS:
-
Install Certbot and its Nginx plugin:
# Debian / Ubuntu sudo apt install -y certbot python3-certbot-nginx # RHEL / Fedora / AlmaLinux sudo dnf install -y certbot python3-certbot-nginx
-
Obtain and automatically install the SSL certificate:
sudo certbot --nginx -d relay.example.com
Certbot will verify your domain, obtain the certificate, update your Nginx configuration with SSL directives, and set up automatic HTTP-to-HTTPS redirection.
-
Verify automatic renewal:
sudo certbot renew --dry-run
Test that your server is reachable over the internet:
curl -i https://relay.example.com/healthExpected response:
HTTP/2 200
content-type: application/json
{"status":"ok","timestamp":"2026-10-05T13:14:15Z"}502 Bad Gateway: Nginx cannot reach the backend. Check if the ThruBox container is running (docker ps) and responds locally (curl http://127.0.0.1:3000/health). If using RHEL/Fedora/CentOS with SELinux, runsudo setsebool -P httpd_can_network_connect 1to allow Nginx to connect to network sockets.413 Request Entity Too Large: The uploaded message payload exceeds Nginx'sclient_max_body_size. Increaseclient_max_body_sizein your server block and reload Nginx.- Connection Timed Out / Unreachable: Verify that ports
80and443are allowed by your firewall (sudo ufw allow 80/tcp && sudo ufw allow 443/tcpon Ubuntu, orsudo firewall-cmd --permanent --add-service=http --add-service=https && sudo firewall-cmd --reloadon RHEL/Fedora). - Configuration Syntax Errors: Run
sudo nginx -tto pinpoint syntax issues. Check/var/log/nginx/error.logfor runtime errors.
Cloudflare Tunnel creates an outbound-only connection between your local environment and Cloudflare's global edge network.
Why use Cloudflare Tunnel?:
- Zero open inbound ports: You do not need to open ports
80or443on your firewall or router. - Works behind CGNAT & Dynamic IPs: Ideal for home networks, residential ISPs, or cloud VPS instances without a static public IP.
- Automatic SSL & DDoS Protection: Edge SSL certificates and DDoS mitigation are managed automatically by Cloudflare.
- A free Cloudflare account.
- A domain managed by Cloudflare (nameservers pointed to Cloudflare).
- ThruBox Server running locally on
http://localhost:3000(or your configured port).
You can configure Cloudflare Tunnel either via the cloudflared CLI on the host or using Docker Compose.
Install cloudflared following the official installation documentation:
# Debian / Ubuntu (x86_64)
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb
# macOS (Homebrew)
brew install cloudflared
# Windows (winget)
winget install --id Cloudflare.cloudflaredcloudflared tunnel loginThis command outputs an authentication link. Open it in your browser, log in to Cloudflare, and authorize your domain zone. The command will download a certificate to ~/.cloudflared/cert.pem (or %USERPROFILE%\.cloudflared\cert.pem on Windows).
cloudflared tunnel create thrubox-tunnelThis outputs a Tunnel ID (a UUID like a1b2c3d4-e5f6-7890-abcd-ef1234567890) and creates the credentials file at ~/.cloudflared/<TUNNEL_ID>.json (or %USERPROFILE%\.cloudflared\<TUNNEL_ID>.json on Windows).
Create the configuration file at ~/.cloudflared/config.yml (Linux/macOS) or %USERPROFILE%\.cloudflared\config.yml (Windows). Replace <TUNNEL_ID>, <YOUR_USERNAME>, and relay.example.com with your actual values:
tunnel: <TUNNEL_ID>
# Path to tunnel credentials file (use the path matching your OS):
# Linux: /home/<YOUR_USERNAME>/.cloudflared/<TUNNEL_ID>.json
# macOS: /Users/<YOUR_USERNAME>/.cloudflared/<TUNNEL_ID>.json
# Windows: C:\Users\<YOUR_USERNAME>\.cloudflared\<TUNNEL_ID>.json
credentials-file: /home/<YOUR_USERNAME>/.cloudflared/<TUNNEL_ID>.json
ingress:
- hostname: relay.example.com
# Note: 3000 is the default ThruBox port (server.port / RELAY_SERVER_PORT).
# Update this upstream target if running on a custom port (e.g., http://localhost:<PORT>).
service: http://localhost:3000
- service: http_status:404Associate your domain or subdomain with the tunnel:
cloudflared tunnel route dns thrubox-tunnel relay.example.comThis automatically creates a CNAME DNS record in Cloudflare pointing relay.example.com to your tunnel.
Run the tunnel in your console to verify connectivity:
cloudflared tunnel run thrubox-tunnelTest access by sending a request in a separate terminal:
curl https://relay.example.com/healthOnce verified, stop the foreground process with Ctrl+C.
Install cloudflared as a system service so it automatically runs on boot:
Linux (systemd):
sudo cloudflared --config /home/<YOUR_USERNAME>/.cloudflared/config.yml service install
sudo systemctl enable --now cloudflaredmacOS (launchd):
sudo cloudflared --config /Users/<YOUR_USERNAME>/.cloudflared/config.yml service install
sudo launchctl start com.cloudflare.cloudflaredWindows (Windows Service):
Run in an Administrator Command Prompt or PowerShell:
cloudflared --config "C:\Users\<YOUR_USERNAME>\.cloudflared\config.yml" service install
Start-Service cloudflaredIf you prefer a completely containerized deployment without installing packages on the host system:
-
Go to the Cloudflare Zero Trust Dashboard > Networks > Tunnels > Create a tunnel.
-
Name your tunnel (e.g.
thrubox) and select Cloudflared. -
In the setup instructions, copy the tunnel token from the Docker command (
--token <YOUR_TUNNEL_TOKEN>). -
In the Public Hostname tab, configure:
- Subdomain / Domain: e.g.,
relay.example.com - Service Type:
HTTP - URL:
relay:3000(default port; if you configured anotherserver.portorRELAY_SERVER_PORT, update this torelay:<PORT>)
- Subdomain / Domain: e.g.,
-
Update your
docker-compose.yml:services: relay: build: . volumes: - relay-data:/data environment: - RELAY_STORAGE_PATH=/data/relay.db restart: unless-stopped cloudflared: image: cloudflare/cloudflared:latest restart: unless-stopped command: tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN} depends_on: - relay volumes: relay-data:
-
Set your token in a
.envfile in the same directory:CLOUDFLARE_TUNNEL_TOKEN=eyJh...your_cloudflare_token_here...
-
Start your services:
docker compose up -d
In this setup,
cloudflaredcommunicates directly withrelay:3000(orrelay:<PORT>if using a custom port) over the private Docker bridge network. The ThruBox port does not even need to be mapped to the host!
Error 1033 (Argo Tunnel error):cloudflaredis running, but cannot establish a connection tohttp://localhost:3000(orhttp://relay:3000). Confirm that the ThruBox container is running and healthy.- SSL/TLS Redirect Loops: In the Cloudflare dashboard under SSL/TLS, set your encryption mode to Full (or Flexible if proxying to HTTP). Avoid "Off".
- DNS Record Issues: Check the DNS records in your Cloudflare dashboard to ensure the CNAME for
relay.example.compoints to<TUNNEL_ID>.cfargotunnel.comwith proxy enabled (orange cloud). - Inspect Service Logs:
- Host service:
sudo journalctl -u cloudflared -f - Docker container:
docker logs -f cloudflared
- Host service:
Once your ThruBox Server is exposed to the internet:
-
Protect Private Relays with an API Key: If your server is intended for private use, enable API key authentication by setting
security.api_keyinconfig.yamlor through the environment variable:RELAY_SECURITY_API_KEY="your-secret-api-key"Clients will be required to send this value in the
X-API-KeyHTTP header. -
Verify Rate Limiting & Client IP Handling: ThruBox has built-in IP rate limiting (default: 30 requests/minute/IP, configurable via
security.rate_limitorRELAY_SECURITY_RATE_LIMIT). By default it limits by the connecting address and ignoresX-Forwarded-ForandX-Real-IP. Behind a proxy that address is the proxy's, so setsecurity.trusted_proxies/RELAY_SECURITY_TRUSTED_PROXIESto the number of proxies in front of ThruBox (see Behind a Reverse Proxy):- Nginx: With the configuration above, set
trusted_proxies: 1. Nginx overwritesX-Forwarded-Forwith the connecting address, and ThruBox reads that one entry. - Cloudflare Tunnel: Cloudflare appends the address of the client that connected to its edge to
X-Forwarded-For, andcloudflaredpasses the header on, sotrusted_proxies: 1reads Cloudflare's entry, the client address Cloudflare itself saw, and ignores anything the client sent. This only holds if ThruBox is reachable solely through the tunnel (see Block direct access in Behind a Reverse Proxy). Confirm it with the log check described in Behind a Reverse Proxy. You can also enforce rate limits at the edge with Cloudflare WAF / Rate Limiting Rules.
- Nginx: With the configuration above, set
-
Configure CORS If Calling Directly from Browsers: If web applications hosted on other domains will call your relay directly, configure
security.allowed_originsinconfig.yamlor setRELAY_SECURITY_ALLOWED_ORIGINS(see CORS). If you serve your frontend and relay under the same domain using reverse proxy path routing, CORS configuration is not needed.
β Don't forget to star this repository if you find it useful! β
Thank you for considering contributing to this project! Contributions are highly appreciated and welcomed. To ensure smooth collaboration, please refer to our Contribution Guidelines.
See MAINTAINERS.md for the full list of Mentors and Maintainers for this repository.
This project is licensed under the GNU General Public License v3.0. See the LICENSE file for details.
Thanks a lot for spending your time helping ThruBox grow. Keep rocking π₯
Β© 2025 AOSSIE