Haydn Cowley

Write-ups

Putting a website online without opening a single port

My site runs on a Raspberry Pi at home, but my router has no ports open. An Nmap scan proves the firewall does what I think it does.

2 min readBy Haydn
Goal
Host a public website at home without exposing my home network
Result
65,531 ports filtered
Tools
Cloudflare Tunnel, UFW, Nmap

The usual way, and why I didn't

Normally, to host a website at home you'd forward a port on your router, opening a door from the whole internet into your house. Instead the Pi runs a Cloudflare Tunnel: it connects out to Cloudflare, and visitors reach my site through that connection. Nothing comes in through my router.

The web server itself only listens on the Pi (127.0.0.1), so even devices on my own network can't reach it directly. The tunnel is the only way in.

Mistake I made: the site worked on my Mac but nowhere else. The DNS record was called http.haydnhomelab.com, because I'd typed "http" in the subdomain box. Fixing it and clearing my devices' saved "not found" answers sorted it.

Proving the firewall works

UFW blocks everything coming in by default and only allows what I've opened. I scanned every TCP port from another device on my network:

$ sudo nmap -p- <the pi>
Not shown: 65531 filtered tcp ports (no-response)
PORT    STATE SERVICE
22/tcp  open  ssh
53/tcp  open  domain
80/tcp  open  http
443/tcp open  https

Only the four ports I opened at the time showed up. The rest are filtered, not closed: the firewall drops the packets without replying, so a scanner can't even tell if anything is there. I scanned over Tailscale too, and that matched my rules for Tailscale.

What I learned

  • Outbound tunnels are a big shift: you can publish a service without accepting inbound connections.
  • "Filtered" vs "closed" matters. Silence gives an attacker less information.
  • Scan yourself after every firewall change. It's the only real proof.

How I used AI: Claude guided me through setting up the tunnel and UFW. I ran the Nmap scans myself and checked the results against my rules. The full, unedited notes are in my build log.