Guides

Cloudflare Tunnel and external proxies

Let Synq run a Cloudflare Tunnel on your server and route domains through it, or put your own tunnel, Cloudflare's proxy, Nginx Proxy Manager or a load balancer in front with External ingress & TLS.

Normally the Synq edge answers the internet directly on ports 80 and 443 and gets its own Let's Encrypt certificates. Sometimes something else should face the internet instead:

Managed Cloudflare Tunnel

A Cloudflare Tunnel is a small program, cloudflared, that keeps an outbound connection from your server to Cloudflare. Visitors connect to Cloudflare, which serves HTTPS and sends the requests down the tunnel to the server's edge. With the managed tunnel, Synq does the Cloudflare side for you: it creates or picks the tunnel, runs cloudflared on the server, and adds the ingress rule and DNS record for each domain you route through it.

There is one tunnel per server, set up under Settings → Cloudflare Tunnel (self-hosted panels). Changing the token or the tunnel, restarting the connector and disconnecting need an organization admin; the panel shows the API's refusal to anyone else. Routing a domain needs write access to its project.

Create an API token in Cloudflare

In the Cloudflare dashboard, open My Profile → API Tokens and create a custom token with these three permissions:

PermissionWhy Synq needs it
Account → Cloudflare Tunnel → EditList, create and configure tunnels, and read a tunnel's run token.
Zone → DNS → EditAdd, update and (only when you ask) delete the CNAME record for a routed domain.
Zone → Zone → ReadFind the zone a domain belongs to.

Under Account Resources, include the account that holds your domains. Under Zone Resources, include the zones (or all zones of that account) whose domains you want to route. The settings panel lists the same three permissions, with a link to the token page.

Connect the token

Open Settings → Cloudflare Tunnel, pick the server if you have more than one, paste the token and press Connect. Synq checks the token with Cloudflare and lists the accounts it can use. The token is stored encrypted and is never shown again, not even to admins; to change it, press Replace token and paste a new one (a new token for the same account keeps the tunnel).

If the token is missing a permission, the panel names the one to add. Fix it in Cloudflare and press Connect again with the same token.

Choose or create the tunnel

Pick the Cloudflare account if the token sees more than one. The panel then lists the account's zones (the domains you can route) and its tunnels:

  • choose an existing tunnel, or
  • choose Create a new tunnel and keep or change the suggested name (synq-<server>).

Only tunnels managed from the Cloudflare dashboard (remotely managed) can be used. A tunnel run from a config file is shown but can't be picked: Synq could not edit its ingress rules.

Press Save and start connector. Synq fetches the tunnel's run token, stores it encrypted, and starts cloudflared on the server. The first start pulls the image and can take a minute.

Check the connector

The panel now shows the account, the tunnel, Cloudflare's status for it (Healthy, Degraded, Down or Inactive, with its connection count) and the connector on the server (container state, restarts, image and start time). When the connector is not running, its last log lines are shown. Health is read when the panel opens and when you press Refresh.

Restart connector restarts the container, or recreates it from the stored token if it was removed.

Route a domain through the tunnel

Routing is turned on per domain, for custom domains of a deployed project on a server that has a tunnel:

  1. Open the domain's menu, either on the Domains page (the row's ⋯ menu) or in the project's Domains & Routes tab, and choose Cloudflare Tunnel.
  2. Press Turn on. When it's done the dialog shows the tunnel, the zone and the CNAME target (<tunnel-id>.cfargotunnel.com).

If the name already has DNS records that point somewhere else (an A record to the server, for example), nothing is changed yet: the dialog lists those records and asks before replacing them. Press Replace and turn on to replace them with the tunnel's CNAME, or Cancel and change them in Cloudflare yourself.

A routed domain shows Cloudflare Tunnel where its certificate issuer normally is, on the Domains page and in the project's Domains & Routes tab. The settings panel lists every domain routed through the server's tunnel.

What Synq changes in Cloudflare

For each routed domain:

  • Tunnel ingress rule: app.example.com → http://localhost:80 (the server's edge), added before the tunnel's catch-all rule. Other rules in the tunnel are left alone. The request keeps its Host header, so the edge picks the right app.
  • DNS record: a proxied CNAME from the domain to <tunnel-id>.cfargotunnel.com, with the comment "Managed by Synq (Cloudflare Tunnel)". A record that already points at the tunnel is kept as it is.
  • In Synq: the domain is marked verified with External TLS. Cloudflare serves HTTPS, so Synq requests no Let's Encrypt certificate for it.

Once per server, Synq creates a tunnel in your account only when you choose Create a new tunnel. On the server it runs one container, synq-cloudflared (image cloudflare/cloudflared, pinned), with host networking and restart unless-stopped. The run token reaches it through a root-only env-file, never on the command line.

Domain Issues for a routed domain are judged through the Cloudflare API, not public DNS: a missing or changed DNS record, a missing ingress rule, or a tunnel with no connection each open an issue that says what to fix.

Stop routing a domain

Open the domain's Cloudflare Tunnel dialog again and press Turn off. Synq removes the domain's ingress rule from the tunnel. Tick Also delete the DNS record in Cloudflare to delete the CNAME as well; otherwise it stays, still pointing at the tunnel (which no longer serves the name) until you change it.

The domain then goes back to the normal flow: point its DNS at the server and verify it, and Synq issues a certificate again. (A domain that had External ingress & TLS on before routing keeps it.)

Change the tunnel or disconnect

Change tunnel picks another tunnel, and Disconnect stops and removes cloudflared from the server and forgets both tokens. The tunnel itself stays in your Cloudflare account; delete it there if nothing else uses it. Both are refused while any domain is routed through the tunnel: turn routing off for those domains first (the panel names them).

Limits

  • One tunnel and one token per server. Several servers can use the same Cloudflare account, each with its own tunnel.
  • Only remotely-managed tunnels, and only exact hostnames: wildcard domains and free subdomains can't be routed.
  • The domain must be in a zone of the connected account. Otherwise turning it on fails and says so.
  • Every routed name goes to http://localhost:80 on the server that runs the project. The traffic between Cloudflare and the server travels inside the tunnel.
  • Cloudflare's free Universal SSL certificate covers the zone apex and one level of subdomains (app.example.com, not api.app.example.com). Deeper names need an Advanced Certificate in Cloudflare.
  • Behind the tunnel the edge sees the connector's address, not the visitor's; see Real visitor IPs.
  • If the panel's encryption secret changes (or the panel is restored elsewhere), the stored token can't be read any more. The panel asks you to paste it again; the tunnel and routed domains keep working meanwhile.

What External ingress & TLS does

When a domain is added with External ingress & TLS:

  • Synq asks for no DNS record and doesn't check DNS. The domain doesn't need to point at this server.
  • No certificate is requested. Verify marks the domain verified straight away, and its SSL state reads External TLS (on the Domains page: Handled upstream, issuer Your proxy or CDN).
  • The edge serves the domain as plain HTTP on port 80 of the server, for your proxy to forward to.
  • The HTTPS redirect is left to your proxy.

Turn it on only if something else really handles HTTPS

If nothing in front of the server serves HTTPS, a domain with External ingress & TLS is reachable over plain HTTP only. When in doubt, leave it off and let Synq issue the certificate.

Run your own cloudflared

If you already run cloudflared yourself, or want to manage the tunnel by hand, you can keep it and use External ingress & TLS instead of the managed tunnel:

  1. In Cloudflare, add a public hostname to your tunnel, for example app.example.com, with the service http://localhost:80 on the server that runs the project. Keep the original Host header (the default). If cloudflared runs in a Docker container, give it host networking (--network host) so localhost:80 reaches the edge.
  2. Open the project's Domains & Routes tab, press Add domain, choose Custom domain, type the hostname, set Maps to port, and turn on External ingress & TLS. Press Add domain.
  3. Press Verify. The domain turns verified with External TLS; no TXT record is needed on your own server. Once the project is deployed, https://app.example.com serves it through your tunnel.

Ports

If every domain on the server goes through a tunnel, you can keep ports 80 and 443 closed to the internet. Any domain that still uses Let's Encrypt over HTTP-01 needs port 80 open, or DNS-01 instead (see Wildcard certificates).

Nginx Proxy Manager or a load balancer

The same switch works for any proxy that ends HTTPS itself:

  1. Configure the proxy to forward the hostname to the Synq server on port 80, keeping the Host header.
  2. Add the domain in Synq with External ingress & TLS on, and press Verify.
  3. Point DNS at the proxy, not at the Synq server. For a homelab, point your internal DNS name at Nginx Proxy Manager; a changing home IP doesn't matter to Synq, but visitors from outside still need a public address, VPN or tunnel.

Cloudflare's proxy without a tunnel

If the server is reachable directly and you just turn on Cloudflare's proxy (the orange cloud) for the DNS record, leave External ingress & TLS off: Synq keeps a real certificate on the server, and Cloudflare connects to it. The things to know:

  • Issue the first certificate with the proxy off. Set the record to DNS only, verify the domain in Synq, then turn the proxy on. With the proxy on, Cloudflare's "Always Use HTTPS" can redirect Let's Encrypt's challenge to HTTPS, and it no longer reaches the challenge on port 80.
  • Renewals use the same challenge 14 days before expiry. For a record that stays proxied, the reliable options are DNS-01 (connect Cloudflare under Settings → Credentials and turn on Use DNS-01 challenge) or a Cloudflare Origin CA certificate (see Upload your own certificate).
  • SSL/TLS mode. Full (strict) works with Synq's Let's Encrypt certificate or an uploaded Origin CA certificate. Flexible makes Cloudflare talk to the server over plain HTTP; Synq sees X-Forwarded-Proto: https and doesn't redirect, so there is no redirect loop, but traffic between Cloudflare and the server is unencrypted.

Real visitor IPs

Rate limits, IP bans, country rules, request logs and the visitor map all need the visitor's real address, not the proxy's.

  • Cloudflare's proxy: handled automatically. The edge trusts Cloudflare's published address ranges and reads the CF-Connecting-IP header, which Cloudflare sets. A visitor who connects directly with a forged header is not coming from Cloudflare, so the header is ignored.
  • A tunnel, a load balancer or your own proxy: requests arrive from the tunnel or proxy's address. cloudflared passes Cloudflare's CF-Connecting-IP header through, but the edge only trusts it from Cloudflare's ranges, so behind a tunnel or another proxy the edge sees the proxy's address. For an edge installed directly on a server, Synq reads SYNQ_EDGE_TRUSTED_PROXIES (comma-separated IPs or CIDR ranges) and SYNQ_EDGE_REAL_IP_HEADER (for example X-Forwarded-For) when it sets that edge up. To find the address to trust, look at the client IP in the project's request logs (Logs → Server).

Only list proxies you control: anything on the list can claim to be any visitor.

Standard (Docker Compose) installs

The edge container of a standard install (the installer, or synq up --compose) has its real-IP settings built into the image (Cloudflare's ranges and CF-Connecting-IP only). It ignores SYNQ_EDGE_TRUSTED_PROXIES and SYNQ_EDGE_REAL_IP_HEADER, so behind a tunnel or another proxy its IP-based rules (rate limits, IP bans, country rules) see the proxy's address. Keep such rules broad on these installs, or put Cloudflare's proxy in front instead.

On this page