# Cloudflare Tunnel and external proxies
URL: https://synquic.tech/docs/guides/cloudflare-tunnel.md

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.

import { Step, Steps } from 'fumadocs-ui/components/steps';

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:

- a **Cloudflare Tunnel**, so the server needs no open inbound ports at all. Synq can run it for you: see
  [Managed Cloudflare Tunnel](#managed-cloudflare-tunnel);
- a **load balancer**, **Nginx Proxy Manager** or **your own `cloudflared`** that already owns HTTPS: use
  [External ingress & TLS](#what-external-ingress--tls-does);
- **Cloudflare's proxy** (the orange cloud) in front of a server that is reachable directly: usually no switch
  at all, see [Cloudflare's proxy without a tunnel](#cloudflares-proxy-without-a-tunnel).

## 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.

<Steps>

<Step>

### Create an API token in Cloudflare

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

| Permission | Why Synq needs it |
| --- | --- |
| **Account → Cloudflare Tunnel → Edit** | List, create and configure tunnels, and read a tunnel's run token. |
| **Zone → DNS → Edit** | Add, update and (only when you ask) delete the CNAME record for a routed domain. |
| **Zone → Zone → Read** | Find 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.

</Step>

<Step>

### 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.

</Step>

<Step>

### 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.

</Step>

<Step>

### 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.

</Step>

</Steps>

### 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](#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.

<Callout title="Turn it on only if something else really handles HTTPS" type="warn">
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.
</Callout>

## 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.

<Callout title="Ports" type="info">
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](/docs/guides/https-certificates#wildcard-certificates-with-a-dns-provider)).
</Callout>

## 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](/docs/guides/https-certificates#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.

<Callout title="Standard (Docker Compose) installs" type="warn">
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.
</Callout>

## Related

<Cards>
  <Card title="Custom domains" href="/docs/guides/custom-domains" description="The Domains & Routes tab and the two ways to set up DNS." />
  <Card title="HTTPS and certificates" href="/docs/guides/https-certificates" description="HTTP-01, DNS-01, uploads and the HTTPS redirect." />
  <Card title="Redirects, basic auth and security rules" href="/docs/guides/edge-rules" description="Rules that depend on the real visitor IP." />
</Cards>
