Skip to content

Cloudflare + Nginx

This page helps you put Goiabada behind Cloudflare’s proxy on a server that already runs Nginx for other sites.

Cloudflare terminates the browser’s HTTPS and connects to Nginx over HTTPS again, with a certificate Nginx holds. Nginx forwards each request to the Docker Compose file the setup wizard writes, which publishes the two servers on 127.0.0.1 alone:

Browser ── HTTPS ──▶ Cloudflare ── HTTPS ──▶ Nginx on the host ── HTTP ──▶ 127.0.0.1:9090 and :9091

You need a domain on Cloudflare, a server with Docker and Nginx, two hostnames under that domain, such as auth.example.com and admin.example.com, and the setup wizard. Both hostnames must share a registrable domain: see What every method needs.

  1. Point both hostnames at the server, through Cloudflare. In Cloudflare’s DNS, add an A record for each, with the server’s IP address, and leave them DNS only (grey cloud) until you have the certificates:

    auth A <your-server-ip> DNS only
    admin A <your-server-ip> DNS only

    If the server has an IPv6 address too, add an AAAA record for each with it.

  2. Generate and start Goiabada. Run the setup wizard, choose Production with reverse proxy, enter https://auth.example.com and https://admin.example.com, then start the files it wrote, as Docker Compose describes:

    Terminal window
    docker compose up -d
    curl http://127.0.0.1:9090/health # healthy
  3. Get the certificates and configure Nginx, as steps 3 and 4 of Reverse proxy show. Let’s Encrypt reaches the server directly while the records are DNS only.

  4. Turn Cloudflare’s proxy on. Switch both records to Proxied (orange cloud). Then, under SSL/TLS, set the encryption mode to Full (strict), so Cloudflare checks Nginx’s certificate, and turn on Always Use HTTPS.

  5. Have Nginx resolve Cloudflare’s addresses, so Goiabada sees each client’s address rather than Cloudflare’s. Cloudflare + Nginx on Client IP and proxy trust has the file to write.

  6. Open only 80 and 443, besides SSH. With ufw, allow your SSH port first, so turning the firewall on keeps your access.

    Set SSH_PORT to the port your SSH server listens on. In an SSH session, it’s the last field of $SSH_CONNECTION:

    Terminal window
    SSH_PORT="${SSH_CONNECTION##* }"; echo "SSH port: ${SSH_PORT:-unknown}"

    If that prints unknown, or you’re in a sudo -i or su shell, at a console, or in a tmux, screen or mosh session, which can keep an old value, look the port up and set it yourself, such as SSH_PORT=2222. sudo ss -tlnp shows SSH listening as sshd, or as systemd on a host that starts it through socket activation, as recent Ubuntu releases do, where systemctl cat ssh.socket shows its ListenStream.

    Then add the rules. Nothing changes unless SSH_PORT is a port from 1 to 65535 written without a leading zero, and each command runs only if the one before it succeeded. It runs the same in Bash and in sh:

    Terminal window
    case "$SSH_PORT" in
    [1-9] | [1-9][0-9] | [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] | [1-9][0-9][0-9][0-9][0-9]) port_ok=yes ;;
    *) port_ok=no ;;
    esac
    if [ "$port_ok" = yes ] && [ "$SSH_PORT" -le 65535 ]; then
    sudo ufw allow "$SSH_PORT"/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw enable
    else
    echo "SSH_PORT isn't a port from 1 to 65535; nothing was changed."
    fi

    Before you close this session, open a second SSH connection to check that you still get in. ufw comes with Ubuntu; on Debian, sudo apt-get install ufw first, or open the same ports in the firewall you use.

    Never open 9090 or 9091.

  7. Sign in at https://admin.example.com, as First sign-in describes. Then read the auth server’s request log, docker compose logs goiabada-authserver: the ip of your request should be your own address, not Cloudflare’s.

  • Two certificates protect each request: Cloudflare’s edge certificate on the browser’s side, and the Let’s Encrypt certificate Nginx serves to Cloudflare. Full (strict) makes Cloudflare refuse an Nginx certificate that isn’t valid for the hostname.
  • Renewals work with the proxy on. The Nginx configuration answers Let’s Encrypt’s challenge on both 80 and 443, where Cloudflare’s redirect to HTTPS sends it. sudo certbot renew --dry-run checks it.
  • Each server sees the client’s address, once Nginx resolves Cloudflare’s: see Client IP and proxy trust.
  • The admin console reaches the auth server inside the Compose network, not through Cloudflare: see The hop to the auth server.