Exposing Self-Hosted Apps Safely with Caddy
Use Caddy for automatic TLS, http-to-https redirects, and controlled exposure of internal apps without opening more than the ports you intend.
Netbay Developer Relations
Netbay Engineering
On this page
Exposing a self-hosted app to the internet is a small act of trust: you are telling the whole world that one process on your network is safe to talk to. The skill is not in the exposure itself but in shrinking the surface — terminating TLS at the edge, redirecting plain HTTP away, and making sure only the intended service is reachable. Caddy is an ideal tool here because its default behavior does the right thing: it obtains certificates automatically, it redirects HTTP to HTTPS by default, and its declarative Caddyfile makes the exposed surface explicit and reviewable.
The Minimal Caddy Setup
Caddy usually runs as a binary or in a container. Its configuration is a Caddyfile — a domain-scoped block with directives. For a single app on a mail host, a two-line Caddyfile gives you automatic HTTPS and the http-to-https redirect for free:
notes.example.com {
reverse_proxy 127.0.0.1:3000
}With an A record for notes.example.com pointing at the host, Caddy will fetch a Let's Encrypt certificate on first request and renew it automatically. Plain HTTP requests to the same name are redirected to HTTPS by default. No manual cert renewal scripts, no expired-certificate alarms at 3 a.m.
Make Exposure Explicit, Not Implicit
Caddy's greatest discipline is that nothing is exposed unless you write it down. An app left listening on a public port with no Caddyfile entry is simply unreachable from Caddy's perspective, which is exactly the safety you want. Bind the actual app to 127.0.0.1 or a private Docker network, and reserve the internet-facing port for Caddy alone.
services:
notes:
image: some/notes-app:latest
ports:
- "127.0.0.1:3000:3000"
restart: unless-stopped
networks:
- internal
...The 127.0.0.1 binding stops the container from being reachable on the host's public interface at all. Only Caddy, running on the same host, can reach it — and only through the rules you declared.
Path-Based and Subpath Protection
Beyond hostname routing you can gate parts of an app from other middleware, or restrict by source IP when an app has no auth of its own:
admin.example.com {
root * /srv/app
@private not remote_ip 10.0.0.0/8 192.168.1.0/24
respond @private "Forbidden" 403
reverse_proxy 127.0.0.1:8080
}This Caddyfile rejects anyone who is not coming from an internal range before the request ever reaches the app — a reasonable guard for admin panels that predate multi-user auth.
Rate Limit and Set Sanity Headers
A public endpoint invites probing, and a self-hosted box rarely has the hardware to absorb a hammering that a cloud CDN shrugs off. Caddy can rate-limit and standardize response headers in the same Caddyfile, cheap hardening that runs at the edge before any work reaches your app. Adding a per-IP limit plus a few security headers makes the difference between "exposed and fine" and "exposed and barely holding on."
https://app.example.com {
rate_limit {
zone dynamic
events 10
window 1m
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
}
reverse_proxy 127.0.0.1:3000
}The rate_limit zone caps each source to ten requests per minute, which is generous for a personal tool and brutal for a brute-force scanner. The header block stamps transport-security and a couple of browser-hardening headers on every response, so the app's own defaults cannot silently weaken them.
Keep a Jump-Box Path
Not every admin action should be internet-reachable at all. For the control plane — the console, the database, anything that can reconfigure the host — prefer an SSH-only path over a public Caddy entry. Caddy stays for the user-facing apps; the dangerous surfaces stay behind a tunnel you open on demand. That division of labor is the most reliable exposure policy of all: the things that can destroy the stack are simply not on any public hostname.
Takeaway
Exposure and safety are the same act when done with the right tool: TLS is automatic, HTTP is redirected, and the exposed surface is a short, reviewable list. Keep apps bound privately and let a single edge process own the public ports. You can provision a Caddy-ready Linux VPS on netbayhosts.in in under a minute and have your first domain live with a green padlock in minutes after that.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance