keepcalculator

06 Numbers and Powers

Expose Self-Hosted Service Securely Without Cloudflare Tunnel

Card No. 06·6802 · Updated · Standard formula, shown below

This guide shows you how to securely expose a self-hosted service (like a web app, API, or home server) to the internet without relying on Cloudflare Tunnel. You’ll use a standard reverse proxy (like Nginx or Caddy) with TLS encryption, a firewall that only allows necessary ports, and optional authentication. By the end, you’ll have a publicly reachable service that’s protected against common threats. You’ll need a domain name, a machine with a public IP (or port forwarding), and basic command-line comfort.

What You’ll Need

  • A domain name (e.g., example.com) with DNS control.
  • A server or computer that can accept inbound connections (static IP or dynamic DNS).
  • Root/sudo access to that machine.
  • A reverse proxy installed (Nginx or Caddy are recommended).
  • Ports 80 and 443 open on your router/firewall (if behind NAT).
  • Basic knowledge of editing config files and using the terminal.

Step-by-Step

  1. Set up DNS
    Create an A record for your service, e.g., service.example.com, pointing to your public IP. If you have a dynamic IP, use a dynamic DNS service like DuckDNS or No-IP.
  2. Install a reverse proxy
    For Nginx on Ubuntu/Debian: sudo apt update && sudo apt install nginx. For Caddy: follow the official install script. Caddy automatically handles TLS, which simplifies things.
  3. Configure the reverse proxy
    For Nginx, create a config file in /etc/nginx/sites-available/ and symlink it to sites-enabled. Example for a local service on port 8080:
    server {
        listen 80;
        server_name service.example.com;
        location / {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

    For Caddy, add to Caddyfile: service.example.com { reverse_proxy localhost:8080 }.

  4. Enable TLS (HTTPS)
    For Nginx, install Certbot: sudo apt install certbot python3-certbot-nginx, then run sudo certbot --nginx -d service.example.com. For Caddy, TLS is automatic—just ensure ports 80 and 443 are open.
  5. Harden the firewall
    Allow only ports 80 (HTTP), 443 (HTTPS), and SSH (22) if needed. Use UFW on Ubuntu: sudo ufw allow 80/tcp, sudo ufw allow 443/tcp, sudo ufw allow 22/tcp, then sudo ufw enable. Block all other inbound ports.
  6. Add authentication (optional but recommended)
    Set up HTTP Basic Auth in Nginx with htpasswd or use Caddy’s basicauth directive. This adds a login prompt before reaching your service.
  7. Test locally first
    Access https://service.example.com from your local network to ensure the proxy works. If it fails, check the service is running and the proxy config is correct.
  8. Open ports on router (if needed)
    If your server is behind a home router, forward ports 80 and 443 to the server’s local IP. This is usually done in the router’s admin panel under “Port Forwarding”.
  9. Monitor logs
    Check Nginx logs at /var/log/nginx/access.log and error.log for suspicious activity. Consider setting up fail2ban to block repeated failed login attempts.
  10. Keep software updated
    Regularly update your OS, reverse proxy, and the service itself to patch security vulnerabilities.

Common Mistakes

  • Leaving port 80 open without redirecting to HTTPS—always redirect HTTP to HTTPS to prevent unencrypted traffic.
  • Using a weak password for Basic Auth—use a strong, unique password or better, use client certificates.
  • Forgetting to set the Host header—some services require the correct Host header; Nginx’s proxy_set_header Host $host is essential.
  • Opening too many ports—only expose what’s necessary. Don’t open port 8080 directly; let the proxy handle it.
  • Not testing from an external network—local testing doesn’t verify that port forwarding works correctly.

Tips

  • Use a non-standard port for SSH and disable password authentication to reduce brute-force attacks.
  • Set up automatic HTTPS redirect in Nginx: add return 301 https://$host$request_uri; in the port 80 server block.
  • Consider using a dedicated user for the service to limit damage if it’s compromised.
  • Use a firewall that only allows connections from your own IP for administrative interfaces, if possible.
  • Enable gzip compression in the proxy to speed up responses.

Troubleshooting

  • Cannot reach the service from outside—Check that ports 80/443 are forwarded correctly, the firewall allows them, and the service is listening on all interfaces (not just localhost). Use netstat -tulpn to verify.
  • Certificate errors—Ensure DNS points to the correct IP and that port 80 is open for Let’s Encrypt validation. Try renewing the certificate manually.
  • 502 Bad Gateway—The proxy can’t reach your service. Confirm the service is running and the proxy_pass URL is correct (e.g., http://localhost:8080).
  • Service works locally but not externally—Often a firewall or NAT issue. Double-check your router’s port forwarding rules and that the server’s IP is static.
  • Slow performance—Check if the service is CPU-bound or if the proxy needs more worker processes. Also, ensure DNS is resolving quickly.

Frequently Asked Questions

Is it safe to expose a service without Cloudflare Tunnel?

Yes, if you follow security best practices: use HTTPS, strong authentication, keep software updated, and limit exposure. A reverse proxy with TLS is a standard, secure method.

Do I need a static IP?

Not necessarily. You can use dynamic DNS to update your domain’s A record when your IP changes. Many home routers support this natively.

Can I use this for a service on a different port?

Yes, the reverse proxy can forward to any local port. Just set the proxy_pass to the correct port.

What if I don’t have a domain name?

You can use a free subdomain from services like DuckDNS or use a self-signed certificate, but that will cause browser warnings. A domain is strongly recommended.

How do I block specific IPs?

Use your firewall (e.g., ufw deny from IP) or configure Nginx’s deny directive in the server block.

Can I expose multiple services?

Yes, create a separate server block for each subdomain or use path-based routing with Nginx’s location blocks.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →