中文说明 | English
cfui is a web control panel for Cloudflare Tunnel (cloudflared). It runs the local tunnel process, manages selected Cloudflare Tunnel ingress rules through the Cloudflare API, updates DNS records for DDNS use cases, exposes S3-compatible storage through WebDAV, and provides an MCP endpoint for AI clients.
The web UI is built into the binary. Configuration is stored in a local SQLite database under the data directory.
-
Local Cloudflare Tunnel runner
- Manage multiple Cloudflare Tunnel profiles from the browser.
- Paste Cloudflare Tunnel tokens and edit each saved profile independently.
- Start or stop each tunnel profile independently; multiple profiles can run at the same time.
- Configure auto-start, auto-restart, protocol, region, retries, graceful shutdown, metrics, post-quantum mode, edge IP version, edge bind address, TLS verification, and extra cloudflared arguments.
- Show tunnel status, active protocol, last error, and version/build information.
-
Remote Tunnel Manager
- Optional feature for managing Cloudflare-hosted tunnel ingress configuration.
- Select any saved tunnel profile for remote management.
- Load an existing tunnel by Account ID and Tunnel ID. API credentials are shared, while Account ID and Tunnel ID can be stored per tunnel profile.
- Read the Cloudflare Tunnel name through the Cloudflare API and apply it to a profile when the local profile is still using an auto-generated name.
- Add, edit, and delete public hostname rules with hostname, path, service type, service URL, host header, origin server name, and TLS verification options.
- Check each public hostname's Cloudflare DNS mapping and origin connectivity, with per-rule results and compact success/issue statistics.
- Decode Account ID and Tunnel ID from the selected tunnel token when possible.
- Verify Cloudflare API permissions before using Cloudflare-managed operations.
-
DDNS
- Optional feature that reuses Remote Tunnel Manager Cloudflare credentials.
- Detect public IPv4 and IPv6 from configurable IP source URLs.
- Create and update Cloudflare
AandAAAArecords. - Supports Cloudflare proxy mode, TTL, DNS comments, retry settings, manual sync, and recent sync history.
-
S3 WebDAV
- Mount one or more S3-compatible bucket paths as WebDAV endpoints.
- Supports generic S3-compatible services and Cloudflare R2 presets.
- Each mount can use its own endpoint, region, bucket, root prefix, mount path, Access Key ID, Secret Access Key, WebDAV username/password, and auth toggle.
- Includes S3 connection testing and WebDAV connection testing.
- Includes a browser file panel for listing, uploading, downloading, deleting, renaming, and creating folders.
- WebDAV can be served from the main HTTP service or from a dedicated HTTP port. These modes are mutually exclusive.
- Dedicated WebDAV mode supports manual start/stop, optional auto-start, direct-port endpoints, custom public URLs, and Cloudflare Tunnel rule handoff through the current tunnel profile.
- Browser
GETrequests on a WebDAV path show a read-only file listing or file download. WebDAV methods such asPROPFIND,PUT,DELETE,MKCOL,MOVE,COPY,LOCK, andUNLOCKkeep WebDAV behavior.
-
MCP access
- Optional Model Context Protocol endpoint at
/mcp. - Uses bearer tokens generated in the UI.
- Exposes tools for tunnel status/configuration, tunnel start/stop, recent logs, Remote Tunnel Manager operations, and DDNS operations when DDNS is enabled.
- Optional Model Context Protocol endpoint at
-
Logs and operations
- Recent logs API and optional real-time log streaming in the UI.
- Log filtering, copying, downloading, and clearing from the browser.
- Panic recovery and request logging middleware.
-
Feature switches
- Remote Tunnel Manager, DDNS, MCP, and S3 WebDAV are optional tabs.
- Disabled features stay hidden in the UI.
- DDNS depends on Remote Tunnel Manager because it reuses the same Cloudflare API credentials.
-
Optional local access protection
- Existing and new installations remain accessible without a cfui account until protection is explicitly enabled from the Features page.
- Adds username/password sign-in for the local management API, plus password rotation, session revocation, sign-out, and disable controls in the existing UI.
- Credentials and sessions are stored only in the local SQLite database; passwords use Argon2id and raw session tokens are not stored.
- There are no authentication environment variables. MCP and WebDAV keep their own independent authentication controls.
-
Configuration backup and restore
- Export or import selected local configuration sections from the Features tab.
- Password protection is optional, and sensitive credentials are excluded by default.
- Import replaces only the selected sections and keeps all unselected sections unchanged.
-
Cloudflare OAuth console
- Optional run mode for managing Cloudflare account resources with a Cloudflare OAuth access token.
- Lists accounts, zones with plan/name-server detail, DNS records, Cloudflare Tunnel control-plane records, Workers, R2, D1, KV, Snippets, WAF, Cloudflare public status, and selected zone settings when the granted scopes allow it.
- OAuth sessions and PKCE state are stored in the local SQLite database, not JSON files.
- Multiple OAuth identities can be saved and switched; the current identity controls all Cloudflare API calls.
- Local cfui tools such as the cloudflared runner, MCP, and S3 WebDAV remain separate local capabilities.
-
Internationalized UI
- English, Chinese, and Japanese UI translations are included.
docker run -d \
--name cfui \
-p 14333:14333 \
-v cfui-data:/app/data \
-v cfui-logs:/app/logs \
--restart unless-stopped \
ghcr.io/dockers-x/cfui:latestOpen:
http://localhost:14333
Docker Hub image:
docker run -d \
--name cfui \
-p 14333:14333 \
-v cfui-data:/app/data \
-v cfui-logs:/app/logs \
--restart unless-stopped \
czyt/cfui:latestservices:
cfui:
image: ghcr.io/dockers-x/cfui:latest
container_name: cfui
restart: unless-stopped
ports:
- "14333:14333"
# Optional: expose this if you use dedicated S3 WebDAV mode.
# - "14334:14334"
volumes:
- cfui-data:/app/data
- cfui-logs:/app/logs
environment:
BIND_HOST: 0.0.0.0
PORT: 14333
TZ: UTC
# Optional: inject Cloudflare API credentials without saving them in the UI.
# CFUI_TUNNEL_MGMT_ENABLED: "true"
# CLOUDFLARE_ACCOUNT_ID: your-account-id
# CLOUDFLARE_TUNNEL_ID: your-tunnel-id
# CLOUDFLARE_API_TOKEN: your-api-token
healthcheck:
test: ["CMD", "sh", "-c", "wget --no-verbose --tries=1 --spider http://localhost:$$PORT/ || exit 1"]
interval: 30s
timeout: 3s
start_period: 5s
retries: 3
volumes:
cfui-data:
cfui-logs:Download a release binary, then run:
chmod +x cfui
./cfuiOpen:
http://localhost:14333
- Create or select one or more Cloudflare Tunnels in Cloudflare Zero Trust.
- Copy the tunnel token from the tunnel install command.
- Paste the token into a tunnel profile on the Tunnel Configuration page.
- Save the configuration.
- Start the tunnel profile from the UI. Repeat for any other profiles you want to run.
The local tunnel runner does not require Cloudflare API credentials. API credentials are only needed for Remote Tunnel Manager, DDNS, and optional R2 bucket management.
Local access protection is optional and disabled by default, so upgrading does not require migrating historical users or changing the existing startup flow. To enable it, open Features → Local access protection, choose a username and password, and sign in through the custom cfui dialog.
Enable protection before exposing a new cfui instance to an untrusted network. While protection is disabled, anyone who can reach cfui already has access to its management API and can be the first person to enable protection. No setup wizard or environment-variable credential path is added.
cfui can store multiple tunnel profiles. A tunnel profile contains the local cloudflared token and the remote-management identity for one Cloudflare Tunnel.
- Each profile can be edited, started, stopped, and restarted independently.
- Several local tunnel profiles can run at the same time when their settings do not conflict.
- Remote Tunnel Manager uses the selected profile's Account ID and Tunnel ID with the shared Cloudflare API credentials.
- If a profile's Account ID or Tunnel ID is blank, cfui tries to decode them from that profile's tunnel token.
- S3 WebDAV Cloudflare Tunnel publishing uses the current tunnel profile retained for legacy integrations.
Remote Tunnel Manager and DDNS use Cloudflare API credentials. An API Token is recommended.
Required permissions for Remote Tunnel Manager and DDNS:
Account -> Cloudflare Tunnel -> EditZone -> Zone -> ReadZone -> DNS -> Edit
Cloudflare's API may expose the first permission as Cloudflare Tunnel Write, Cloudflare One Connector: cloudflared Write, or Cloudflare One Connectors Write. cfui accepts those current names and the legacy aliases, but it always checks DNS Write independently from Zone Read.
Optional permission for R2 bucket list/create:
Account -> Workers R2 Storage -> Edit
S3 WebDAV file access does not use the Cloudflare API token. It uses the S3-compatible Access Key ID and Secret Access Key configured on the S3 WebDAV page.
Open the Features tab and use the Configuration backup and restore card. Both export and import provide a section list:
- Tunnel configuration
- Remote Tunnel Manager
- DDNS
- S3 WebDAV
- Application and OAuth base settings
- Sensitive credentials
Sensitive credentials are off by default. When selected, they include credentials only for the normal sections present in the backup, such as tunnel tokens, Cloudflare API credentials, S3 access keys, and WebDAV password hashes. OAuth sessions, OAuth PKCE state, MCP tokens, validation reports, logs, runtime status, and remote Cloudflare resources are never included.
A backup password is optional. A non-empty password encrypts the complete payload with scrypt and AES-256-GCM. Without a password, the backup is readable JSON. Exporting sensitive credentials without a password requires a second confirmation because the downloaded file will contain readable credentials.
Import inspects the file before saving and lets you select only sections available in that backup. Each selected normal section replaces its current local counterpart; unselected sections remain unchanged. Selecting Sensitive credentials updates matching tunnel or mount credentials. The upload limit is 8 MiB.
After a successful import, cfui reloads DDNS, S3 WebDAV, and OAuth services only when their effective settings changed. Removed tunnel profiles receive an asynchronous stop-and-forget request. Surviving running tunnels are not restarted automatically; cfui lists the profiles that need a restart for new runner settings or tokens to take effect.
cfui supports three top-level run modes. They choose the default workspace and whether the local cloudflared runner auto-starts; they do not remove the other workspace from the process.
classic: default. Start in the local cfui workspace and auto-start eligible local tunnel profiles.oauth: start in the Cloudflare OAuth workspace. The local tunnel runner is not auto-started.both: start in the local cfui workspace while keeping the Cloudflare workspace entry available.
The workspaces are route-isolated:
/and/local: local cfui workspace for cloudflared runner, Remote Tunnel Manager, DDNS, MCP, S3 WebDAV, and feature settings./cloudflare: Cloudflare OAuth workspace for Cloudflare account resources. The local workspace is only exposed as a switch entry.
Set the mode with:
CFUI_RUN_MODE=oauth ./cfuiOAuth mode calls Cloudflare APIs directly with the current OAuth identity's bearer token. It does not create API tokens on behalf of the user. cfui can store multiple OAuth identities in SQLite and rename, switch, or remove them from the OAuth console; no OAuth session or PKCE state is written to JSON. Before each sign-in, the OAuth console can build a module-based scope set for the next identity; when adding an identity while already signed in, cfui opens Cloudflare through the logout endpoint first so browser cookies do not silently reuse the current account. The WebUI starts new sign-ins from base read scopes and lets users explicitly add more scopes; CFUI_OAUTH_SCOPES remains the fallback for direct /oauth/start use. When OAuth is not configured, the Cloudflare workspace shows OAuth Client ID and relay callback inputs, a compact source/health line, a small action to save the provided Client ID plus relay domain, and a self-host dialog that first explains the provided-domain path versus the self-hosted Worker path before showing the target callback URL, full Worker script, and deployment notes. The setup guide is expanded until OAuth is configured, then collapses behind a toggle for later reference. The relay callback can be edited from the WebUI; the saved SQLite value overrides CFUI_OAUTH_RELAY_URL and must exactly match the Cloudflare OAuth client's redirect callback URL. Available Cloudflare pages are controlled by the scopes granted to the current OAuth app session; local MCP and S3 WebDAV remain in the local cfui workspace and are not shown inside /cloudflare.
The OAuth console currently exposes an orange-cloud-style Overview dashboard, accounts, zones with plan/name-server detail, DNS records, Cloudflare Tunnel control-plane records with status/type/remote config/active connection detail and linked local runner state, Workers scripts, R2 buckets, objects, and account-level metrics, D1 databases, KV namespaces, zone-level Snippets, custom WAF rules, managed WAF ruleset overrides, and managed WAF exceptions, Zone Analytics, Account Usage, Cloudflare public status, and selected zone settings. The Overview uses one backend endpoint to aggregate account, zone, DNS, Workers, Tunnel, R2, D1, KV, Snippets, WAF, and public status counts, then shows capability-aware quick actions plus a recent-zone panel that opens the selected zone or its DNS records; missing scopes or per-product API failures are shown as unavailable metrics instead of breaking the whole console. The Overview can also run GET /api/cf/validation, copy a no-token validation report, and save server-generated validation snapshots to local SQLite through POST /api/cf/validation-reports; saved summaries can be listed, opened, and deleted without uploading arbitrary report JSON from the browser. These reports separate OAuth app allowlist/requested/granted scope gaps from Cloudflare API availability failures and include structured action items, missing scope names, limited API checks, and unavailable API checks while omitting OAuth/refresh tokens, connector tokens, DNS content, object bodies, KV values, SQL rows, and WAF expressions. DNS records can be searched locally; A/AAAA/CNAME proxy state can be toggled from the list, and records can be created, updated, and deleted when dns.write is granted. Workers include list and detail views with script metadata, settings, Tail consumers, a bounded read-only script preview when workers-scripts.read is granted, per-script metrics through the GraphQL Analytics API when analytics.read or account-analytics.read is granted, and Live Tail streaming through a backend SSE proxy when workers-tail.read is granted. R2 buckets can be created and deleted when workers-r2.write is granted; bucket object lists, local search over loaded objects, bounded UTF-8 previews, bounded hexdump previews for non-UTF-8 objects, common image/audio/video/PDF previews through the same-origin download proxy, text object create/update, direct file upload up to 128 MiB, chunked browser-to-cfui upload up to 5 GiB followed by backend streaming to R2, same-origin file download, object delete, and account-level storage metrics are available through backend Cloudflare REST calls because the current Cloudflare Go SDK only exposes bucket-level R2 APIs. R2 metrics are read on demand and are not persisted as local snapshots. KV namespaces can be created, renamed, deleted, drilled into keys, used to view UTF-8 values, edit/delete values with workers-kv-storage.write, create text keys, upload/download raw value bytes through the same-origin backend proxy, and bulk delete loaded keys through Cloudflare's bulk KV API. D1 databases can be created and deleted with confirmation when d1.write is granted, include create-time jurisdiction / primary location hint / read replication options, and show placement metadata when Cloudflare returns it; D1 also includes a SQL query console plus table browsing, paged row loading, a type-aware row editor with explicit SQL NULL support for nullable columns, and parameterized row update/delete. Snippets can be created/deleted, existing snippet code can be loaded and edited through the backend /content REST endpoint, and trigger rules can be added, enabled, disabled, or deleted when snippets.write is granted. Custom WAF rules can be created, edited, enabled, disabled, or deleted when zone-waf.write is granted. The simple WAF editor supports the safe action subset (block, challenge, managed_challenge, js_challenge, log, and skip); skip rules support Cloudflare action parameters for current ruleset, products, and phases, and the form includes a dedicated rate-limit builder for common ratelimit fields. Existing and complex WAF rules can also be edited through an Advanced JSON section for action_parameters, ratelimit, logging, and exposed_credential_check; unchanged advanced JSON fields are not submitted, and null clears a field. WAF updates use read-modify-write and preserve untouched advanced fields. Managed WAF ruleset overrides are isolated to the http_request_firewall_managed phase and only create or edit execute rules with a managed ruleset ID plus ruleset, tag/category, and managed-rule override fields. Managed WAF exceptions are isolated to the same phase and only create or edit skip rules for the current managed ruleset, selected managed rulesets, or selected managed rule IDs. Complex rules still expose ref/version, raw action parameters, rate-limit, logging, exposed credential check metadata, and a copyable audit JSON payload for auditing. Zone Analytics uses the Cloudflare Go SDK dashboard endpoint for 24h/7d/30d traffic summaries when analytics.read or account-analytics.read is granted, including copyable summary export text and compact request trend bars for the returned time series. Account Usage uses backend GraphQL Analytics API queries to show Workers, last-hour Workers errors, R2 operations, D1, and KV usage for the selected account with the same analytics scopes; R2 storage and object count are best-effort overwritten from REST accounts/{account_id}/r2/metrics using the same Standard-class snapshot as orange-cloud. Account Usage also makes a best-effort backend call to accounts/{account_id}/subscriptions for billing period and Workers/R2 paid-plan context. Cloudflare OAuth normally does not expose a billing scope, so subscription 403 responses are shown as unavailable billing context and do not block usage data. Cloudflare Status uses the public Statuspage API through the cfui backend and does not require an OAuth scope. Zone settings support SSL/TLS mode, TLS 1.3, Minimum TLS Version, development mode, security level, cache level, browser cache TTL, Always Use HTTPS, Automatic HTTPS Rewrites, Brotli, Rocket Loader, IPv6, WebSockets, HTTP/2, HTTP/3, Early Hints, Email Obfuscation, Hotlink Protection, Server Side Excludes, Always Online, Browser Integrity Check, IP Geolocation, Opportunistic Encryption, and 0-RTT when zone-settings.write is granted; full cache purge is shown separately when cache_purge.write is granted. Native R2 multipart remains follow-up work unless OAuth REST exposes a multipart endpoint or users provide R2 S3 credentials. The UI hides modules when the current OAuth token does not include the required scope.
Zone detail now acts as an orange-cloud-style domain tool hub: when scopes allow it, the selected zone exposes direct entries into DNS, Analytics, WAF, Snippets, and Zone Settings, plus quick actions for development mode, security level, and full cache purge.
OAuth Tunnel ingress panels show the linked local tunnel profile state and include a local workspace action. Remote hostname changes stay in the OAuth console, while starting, stopping, or restarting the local cloudflared connector remains in /local.
R2 object detail views also expose copy-key, copy-object, and move-object actions, including same-account cross-bucket copies/moves, plus metadata including size, content type, ETag, storage class, last modified time, and encoding status. Non-UTF-8 R2 objects and KV values show a bounded hexdump sample in the detail panel. KV values can also be uploaded and downloaded as raw bytes through the same-origin backend proxy. Inline media/PDF previews are capped at 50 MiB; larger objects stay download-only. The upload form uses direct upload for files up to 128 MiB and switches larger files to chunked browser-to-cfui upload up to 5 GiB.
Snippet trigger rules, custom WAF rules, managed WAF exceptions, and managed WAF overrides include a common expression helper for host, path, request method, source IP/CIDR, and source country matches.
To use OAuth mode:
- Create a Cloudflare OAuth app and set its redirect URI to the Worker relay URL.
- Save the OAuth client ID in the OAuth setup page, or set
CFUI_OAUTH_CLIENT_IDin Docker/Compose. If both exist, the environment variable wins. The provided relay uses client ID27efa323c59c87976675dc75bcad907b. - Keep
CFUI_OAUTH_RELAY_URLat the default relay (https://cfoauth.pushcat.eu.org/oauth/callback) or point it to your own Worker relay. The OAuth setup page's Default action saves both the provided client ID and this relay URL. You can also edit the relay callback from the OAuth setup page; that saved SQLite value overrides the environment default. The page provides copy, save, and check actions; the check verifies whether the relay/healthendpoint supportsstate-v1callbacks. - Deploy or use the Worker relay from
docs/cloudflare-oauth-worker.js. The Cloudflare OAuth app redirect URI should be only the Worker's public HTTPS callback URL, for examplehttps://cfoauth.pushcat.eu.org/oauth/callback; do not appendcfui_callback_url. cfui encodes the current browser-facing/oauth/callbackURL into OAuthstate, and the Worker reads that state to forwardcodeandstateback to the correct cfui instance. If one Worker serves public cfui domains, configure the Worker variableCFUI_ALLOWED_CALLBACK_ORIGINSwith a comma-separated cfui origin allowlist, not full callback URLs. For example, useCFUI_ALLOWED_CALLBACK_ORIGINS=https://cfui.tinycat.heiyu.spacefor one exact cfui domain, orCFUI_ALLOWED_CALLBACK_ORIGINS=https://cfui.*.heiyu.space,https://admin.example.comfor multiple exact/wildcard origins. Each*matches exactly one DNS label and cannot replace the last two labels. Intentionally set*only for an open multi-user relay. Loopback, private/LAN IPs,.local,.internal,.lan,.home.arpa, and.testcallback hosts are allowed by default.CFUI_CALLBACK_URLremains an optional fallback only. The Worker exposes/healthwith astate-v1marker, and cfui can check whether the deployed relay is new enough from the OAuth setup page orGET /api/oauth/relay-check.
Zone overview uses GET /api/cf/dns/count to fetch DNS record totals without loading every record. For D1, cfui loads the database list first and then best-effort refreshes each database with GET /api/cf/d1/databases/{database_id} so table count and file size match Cloudflare's detail endpoint. Failed detail lookups do not block the database list, SQL console, or table browser.
The default OAuth scope template is:
account-settings.read zone.read dns.read
You can override them with CFUI_OAUTH_SCOPES. The scopes selected when creating the Cloudflare OAuth app are the maximum allowlist for that OAuth client. The scopes cfui sends during sign-in, from the UI selector or CFUI_OAUTH_SCOPES, must be a subset of that allowlist; Cloudflare does not merge both lists into the final token.
For Cloudflare Tunnel creation and local profile linking, add:
argotunnel.read argotunnel.write
When a Cloudflare OAuth scope is rejected, compare it with Cloudflare's permission group list:
curl -H "Authorization: Bearer <TOKEN>" \
"https://api.cloudflare.com/client/v4/user/tokens/permission_groups"The permissionGroupKeys values map to Cloudflare's internal permission keys. For example, Tunnel uses the historical argotunnel key, so cfui requests argotunnel.read / argotunnel.write.
After signing in, the OAuth scope dialog can also load Cloudflare permission groups through the same API and copy a token-free diagnostic JSON for troubleshooting.
For zone settings actions, add the required scopes for your OAuth app, for example:
account-settings.read zone.read dns.read dns.write zone-settings.read zone-settings.write cache_purge.write
For R2 bucket write actions, add:
workers-r2.read workers-r2.write
For Worker Tail streaming, add:
workers-tail.read
Worker script deployment/editing is not part of the current orange-cloud baseline in cfui, so the built-in scope selector does not request workers-scripts.write.
For Snippets and trigger rule management, add:
snippets.read snippets.write
For custom WAF rule management, add:
zone-waf.read zone-waf.write
For Zone Analytics, add one of:
analytics.read account-analytics.read
S3 WebDAV is a feature-gated module. Enable it from the Features page, then create a mount from the S3 WebDAV page.
Use Generic S3 for most S3-compatible providers. cfui does not manage buckets in this mode. Provide:
- Endpoint URL
- Region
- Bucket name
- Access Key ID
- Secret Access Key
- Path-style setting if your provider requires it
- Optional root prefix
- WebDAV mount path, for example
/webdav/my_s3/
Use Cloudflare R2 when you want R2 endpoint presets or optional bucket list/create from cfui.
For file access, create an R2 API token from the R2 console and copy:
- Access Key ID
- Secret Access Key
- S3 endpoint, for example
https://<ACCOUNT_ID>.r2.cloudflarestorage.com
For optional bucket list/create, configure Remote Tunnel Manager with a Cloudflare API token that has Workers R2 Storage -> Edit.
If a mount path is /webdav/datasync/, the WebDAV endpoint is:
https://your-cfui-host/webdav/datasync/
File example:
curl -u 'user:password' \
-o db.sql \
'https://your-cfui-host/webdav/datasync/path/to/db.sql'Check a WebDAV object:
curl -u 'user:password' \
-X PROPFIND \
-H 'Depth: 0' \
'https://your-cfui-host/webdav/datasync/path/to/db.sql'Range download:
curl -u 'user:password' \
-H 'Range: bytes=0-1023' \
-o part.bin \
'https://your-cfui-host/webdav/datasync/path/to/db.sql'Enable MCP from the Features page, then create a bearer token from the MCP page. Connect MCP clients to:
/mcp
The MCP token is shown only when it is created. Saved tokens are listed in masked form.
| Variable | Description | Default |
|---|---|---|
BIND_HOST |
HTTP server bind address | 0.0.0.0 |
PORT |
Main HTTP server port | 14333 |
DATA_DIR |
Data directory | ./data |
LOG_DIR |
Log directory | ${DATA_DIR}/logs |
LOG_LEVEL |
debug, info, warn, error |
info |
CFUI_RUN_MODE / CFUI_MODE |
classic, oauth, or both |
classic |
CFUI_TUNNEL_MGMT_ENABLED / CFUI_TUNNEL_MANAGEMENT_ENABLED |
Enable Remote Tunnel Manager | unset |
CFUI_TUNNEL_ACCOUNT_ID / CLOUDFLARE_ACCOUNT_ID / CLOUDFLARE_APP_ID |
Cloudflare account ID | unset |
CFUI_TUNNEL_ID / CLOUDFLARE_TUNNEL_ID |
Cloudflare tunnel ID | unset |
CFUI_TUNNEL_API_TOKEN / CLOUDFLARE_API_TOKEN |
Cloudflare API token | unset |
CFUI_TUNNEL_API_EMAIL / CLOUDFLARE_API_EMAIL |
Cloudflare account email for global API key auth | unset |
CFUI_TUNNEL_API_KEY / CLOUDFLARE_API_KEY |
Cloudflare global API key | unset |
CFUI_OAUTH_CLIENT_ID |
Cloudflare OAuth client ID; overrides the WebUI-saved value when set | unset |
CFUI_OAUTH_RELAY_URL / CFUI_OAUTH_REDIRECT_URI |
Default OAuth Worker relay callback URL registered in Cloudflare; a WebUI-saved SQLite value overrides it | https://cfoauth.pushcat.eu.org/oauth/callback |
CFUI_OAUTH_SCOPES |
Default space-separated OAuth scope template used by direct /oauth/start; the WebUI starts from the base read scopes and lets users explicitly select more |
account-settings.read zone.read dns.read |
CFUI_OAUTH_AUTH_URL |
Cloudflare OAuth authorization endpoint override | Cloudflare default |
CFUI_OAUTH_LOGOUT_URL |
Cloudflare logout endpoint used before adding another identity | Cloudflare default |
CFUI_OAUTH_TOKEN_URL |
Cloudflare OAuth token endpoint override | Cloudflare default |
CFUI_OAUTH_REVOKE_URL |
Cloudflare OAuth revoke endpoint override | Cloudflare default |
CFUI_OAUTH_USERINFO_URL |
Cloudflare OAuth userinfo endpoint override | Cloudflare default |
Environment-provided Cloudflare credentials override saved UI values at runtime. OAuth relay callback configuration is the exception: a WebUI-saved SQLite value overrides CFUI_OAUTH_RELAY_URL.
cfui stores configuration in SQLite:
${DATA_DIR}/data.db
Logs are stored under:
${LOG_DIR}
Old config.json and legacy app_configs database data are migrated into structured SQLite tables automatically. A migrated config.json is renamed to config.json.migrated.
Legacy single-tunnel settings are migrated into the first tunnel profile. Tunnel profiles are stored in the tunnel_profiles table, and the internal default profile key is retained for old single-tunnel endpoints and legacy integrations.
Main endpoints:
GET /api/statusPOST /api/controlGET /api/configPOST /api/configGET /api/tunnelsPOST /api/tunnelsGET /api/tunnels/{key}PUT /api/tunnels/{key}DELETE /api/tunnels/{key}POST /api/tunnels/{key}/activate-localGET /api/logs/recentGET /api/logs/streamGET /api/featuresPOST /api/featuresGET /api/oauth/statusGET /api/oauth/relay-checkPATCH /api/oauth/configPOST /api/oauth/loginPOST /api/oauth/logoutPOST /api/oauth/sessionPATCH /api/oauth/sessionGET /api/version
Optional module endpoints:
/api/cf/*/api/tunnel-manager/*/api/ddns/*/api/mcp/*/api/s3/*/mcp/webdav/*
Remote Tunnel Manager endpoints accept an optional tunnel_key query parameter, for example /api/tunnel-manager/config?tunnel_key=office, so remote ingress rules can be managed for any saved profile. GET /api/tunnel-manager/tunnel?tunnel_key=office reads Cloudflare Tunnel metadata such as the tunnel name.
OAuth Cloudflare endpoints:
GET /api/cf/overviewGET /api/cf/validationGET /api/cf/validation-reportsPOST /api/cf/validation-reportsGET /api/cf/validation-reports/{report_id}DELETE /api/cf/validation-reports/{report_id}GET /api/cf/accountsGET /api/cf/statusGET /api/cf/usage/accountGET /api/cf/zonesGET /api/cf/zones/{zone_id}GET /api/cf/dns/countGET /api/cf/dnsPOST /api/cf/dnsPUT /api/cf/dns/{id}DELETE /api/cf/dns/{id}GET /api/cf/tunnelsGET /api/cf/workersGET /api/cf/workers/{script}GET /api/cf/workers/{script}/metricsGET /api/cf/workers/{script}/tailGET /api/cf/r2/metricsGET /api/cf/r2/bucketsPOST /api/cf/r2/bucketsDELETE /api/cf/r2/buckets/{bucket}GET /api/cf/r2/objectsGET /api/cf/r2/objectPUT /api/cf/r2/objectPOST /api/cf/r2/object/uploadGET /api/cf/r2/object/downloadDELETE /api/cf/r2/objectGET /api/cf/d1/databasesPOST /api/cf/d1/databasesGET /api/cf/d1/databases/{database_id}DELETE /api/cf/d1/databases/{database_id}POST /api/cf/d1/queryGET /api/cf/d1/tablesGET /api/cf/d1/tablePATCH /api/cf/d1/tableDELETE /api/cf/d1/tableGET /api/cf/kv/namespacesPOST /api/cf/kv/namespacesPUT /api/cf/kv/namespaces/{namespace_id}DELETE /api/cf/kv/namespaces/{namespace_id}GET /api/cf/kv/keysPOST /api/cf/kv/keys/bulk-deleteGET /api/cf/kv/value/downloadPOST /api/cf/kv/value/uploadGET /api/cf/snippetsPOST /api/cf/snippetsGET /api/cf/snippets/{name}/contentPUT /api/cf/snippets/{name}/contentDELETE /api/cf/snippets/{name}GET /api/cf/snippets/rulesPOST /api/cf/snippets/rulesPATCH /api/cf/snippets/rules/{id}DELETE /api/cf/snippets/rules/{id}GET /api/cf/wafPOST /api/cf/waf/rulesPATCH /api/cf/waf/rules/{id}DELETE /api/cf/waf/rules/{id}GET /api/cf/waf/managed-overridesPOST /api/cf/waf/managed-overrides/rulesPATCH /api/cf/waf/managed-overrides/rules/{id}DELETE /api/cf/waf/managed-overrides/rules/{id}GET /api/cf/waf/managed-exceptionsPOST /api/cf/waf/managed-exceptions/rulesPATCH /api/cf/waf/managed-exceptions/rules/{id}DELETE /api/cf/waf/managed-exceptions/rules/{id}GET /api/cf/analytics/zoneGET /api/cf/zone-settings
Requirements:
- Go 1.27 or newer
- Git
- Make, optional
Build:
make buildRun:
make runTest:
make testManual build:
VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "dev")
BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S_UTC')
GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
CGO_ENABLED=0 go build -trimpath \
-ldflags="-s -w \
-X 'cfui/version.Version=${VERSION}' \
-X 'cfui/version.BuildTime=${BUILD_TIME}' \
-X 'cfui/version.GitCommit=${GIT_COMMIT}'" \
-o cfui .Project layout:
cfui/
├── internal/config/ # SQLite-backed configuration and migration
├── internal/ddns/ # DDNS detection and Cloudflare DNS sync
├── internal/mcpbridge/ # MCP endpoint and token store
├── internal/s3dav/ # S3-compatible filesystem, file API, WebDAV adapter
├── internal/server/ # HTTP server, API handlers, dedicated WebDAV server
├── internal/service/ # Local cloudflared runner
├── internal/tunnelmgr/ # Cloudflare Tunnel ingress management
├── locales/ # UI translations
├── web/dist/ # Embedded frontend
├── version/ # Build-time version metadata
└── main.go
- Do not expose cfui directly to the public internet without a trusted access control layer.
- Treat Tunnel tokens, Cloudflare API tokens, S3 keys, and WebDAV passwords as secrets.
- Prefer scoped Cloudflare API tokens over global API keys.
- Disable WebDAV authentication only on trusted networks.
- Rotate credentials if they were pasted into logs, chat, shell history, or screenshots.
- Check that the tunnel token is correct.
- Review recent logs in the UI.
- Authentication/configuration errors are not auto-restarted.
- Metrics registration conflicts may require restarting the cfui process.
- Verify that the correct tunnel profile is selected.
- Verify Account ID and Tunnel ID for that profile.
- Check API token permissions from the Remote Tunnel Manager page.
- If Account ID or Tunnel ID are blank, check whether they can be decoded from the selected tunnel token.
- Enable Remote Tunnel Manager first.
- Verify
Zone -> DNS -> Editpermission. - Check configured IP source URLs.
- Use manual sync to see the latest error.
- Check S3 endpoint, region, bucket name, path-style mode, and credentials.
- For R2, use the R2 S3 endpoint and an R2 S3 Access Key ID / Secret Access Key.
- Confirm the mount and WebDAV endpoint are enabled.
- Use the S3 test and WebDAV test buttons from the UI.
This project is licensed under the MIT License.