Skip to main contentClaim $5 in free credit — one-time, per account. Claim $5 free
Krova CloudKrova Cloud

Custom domains

Point your own domain at a Cube with automatic HTTPS, and tune per-domain proxy settings.

Map a domain you own to a port on a Cube. Krova Cloud runs a reverse proxy in front of your Cube that terminates HTTPS, forwards requests to your app, and passes through the real visitor information your app needs.

Add a domain

  1. Point your domain at Krova Cloud by adding a CNAME record to dns.krova.cloud.
  2. In your Cube's networking tab, add the domain and the port your app listens on inside the Cube.
  3. HTTPS is provisioned and renewed automatically — there are no certificates to manage.

If your DNS is on Cloudflare, both modes work: DNS-only (grey cloud — Krova Cloud issues the certificate) or Proxied (orange cloud — Cloudflare's edge sits in front). If you use the orange cloud, you must set Cloudflare's SSL/TLS mode to Full — see Using Cloudflare.

Wildcard domains

Add *.example.com once and every subdomain routes to your Cube — no separate mapping per subdomain. This is the right choice when your app creates subdomains itself, for example one per customer.

A wildcard covers one level of subdomain: app.example.com and api.example.com, but not example.com itself and not a.b.example.com. That is a rule of the certificate standard, not a Krova Cloud limit — add the root domain or a deeper name as its own domain if you need it.

Every subdomain shares one Cube, one port and one set of settings, because they are all one mapping. If a specific subdomain needs different settings, add it as a separate domain — an exact match always wins over the wildcard.

Add the domain first — all three records, with their exact values, then appear on it, each showing whether we can see it yet. Use Check DNS after you publish them.

Three DNS records, all added once and never changed again:

  1. TXT _krova-verify.example.com — proves the domain is yours. A wildcard covers every subdomain, so we check ownership once, for the whole namespace. It does not add example.com itself as a domain.
  2. CNAME *.example.com → your ingress target — routes the traffic.
  3. CNAME _acme-challenge.example.com → the target shown on the domain — lets us issue and renew the HTTPS certificate.

The third record trips people up, twice. First, your own * record does not cover it — DNS wildcards match at any depth, so without an explicit record your wildcard answers _acme-challenge with something that has no certificate information in it. Second, on Cloudflare it must be DNS only (grey cloud); a proxied record returns Cloudflare's own addresses and the certificate authority finds nothing.

Wildcard domains are available on every plan, with no cap on the number of subdomains they serve.

Using Cloudflare

If your domain is proxied through Cloudflare (the orange cloud in your DNS records), Cloudflare's SSL/TLS encryption mode must be set to Full. This is the single most common cause of a custom domain not working behind Cloudflare.

In your Cloudflare dashboard, for the zone holding your domain:

  1. Go to SSL/TLSOverview.
  2. Set the encryption mode to Full.

The setting is per-zone, so it applies to every proxied record in that domain — not just the one pointing at your Cube.

Why Full, and not the other modes

  • Flexible — Cloudflare fetches your origin over plain HTTP. Krova Cloud always redirects HTTP to HTTPS, so Cloudflare receives that redirect and passes it to the browser; the browser retries over HTTPS, Cloudflare fetches the origin over plain HTTP again, and the request bounces forever. The browser gives up with ERR_TOO_MANY_REDIRECTS.
  • Full — Cloudflare connects to your origin over HTTPS without validating the origin's certificate. This is what Krova Cloud requires, and it works from the moment you add the domain.
  • Full (strict) — Cloudflare connects over HTTPS and validates the origin certificate against a public CA. A proxied domain is served an internal, self-signed origin certificate so that it works instantly, and a self-signed certificate is exactly what Full (strict) rejects — so Cloudflare returns error 526 instead of your site.

Cloudflare's own documentation often advises "Full or higher" — do not follow that advice here. "Higher" means Full (strict), which breaks proxied Krova Cloud domains with a 526. Choose Full, specifically.

If your domain is DNS-only (the grey cloud), Cloudflare is not in the request path at all and none of this applies — there is no SSL/TLS mode to change, and Krova Cloud issues a real Let's Encrypt certificate for the domain.

Troubleshooting

  • ERR_TOO_MANY_REDIRECTS, "too many redirects", or a redirect loop — your SSL/TLS mode is almost certainly Flexible. Set SSL/TLSOverview Full, then reload. Cloudflare caches redirects, so also purge the cache or test in a private window if the loop persists.
  • Cloudflare 526 ("Invalid SSL certificate") — your SSL/TLS mode is Full (strict). Change it to Full. Do not try to fix this by installing a certificate on the origin; the origin certificate is managed by Krova Cloud and is not something you can replace.
  • The domain works on grey cloud but breaks when you turn the orange cloud on — this is the same SSL/TLS mode issue. Set the mode to Full before switching the record to Proxied.

Krova Cloud does not detect your SSL/TLS mode — it lives in your Cloudflare zone, not ours. Set it to Full before you point an orange-cloud domain at us and neither failure can happen. If a domain is already looping, change the mode to Full, then purge your Cloudflare cache: Cloudflare can keep serving a cached redirect from before the fix.

Real visitor information

Your app receives the real client details on every request, so logging, rate-limiting, and geolocation work as expected:

  • X-Real-IP and X-Forwarded-For — the real visitor IP (spoof-safe; not the proxy's address).
  • X-Forwarded-Proto and X-Forwarded-Host — the original scheme and hostname.

Per-domain settings

Each domain has its own independent proxy configuration — changing one domain never affects another. You can tune all of these from the domain's settings (and over the REST API, at create time or any time after):

  • Security headers — let Krova Cloud enforce platform security headers, or pass your app's own through.
  • Request & response header overrides — inject or remove headers to/from your app.
  • Max request body size — cap upload sizes (or leave blank for no cap).
  • Response compression — optional gzip/zstd at the edge (off by default; most CDN-fronted domains are already compressed).
  • IP allow / deny lists — restrict who can reach the app by IP or CIDR (IPv4 and IPv6).
  • Basic Auth — put a username/password in front of the domain (the password is hashed and never stored in plaintext).
  • Connect over HTTPS — speak HTTPS rather than plain HTTP on the internal hop to your Cube. Leave this off unless something inside your Cube insists on HTTPS; see When your Cube terminates TLS itself.

See the REST API reference for the exact field names, types, and an example that sets these in one request.

Uploads & CORS

Upload size limits

Krova Cloud does not cap upload sizes by default — your app receives every byte you send. If an upload returns a 413 Request Entity Too Large, the limit is almost always inside your Cube, not at the Krova Cloud proxy:

  • nginx — raise client_max_body_size in your nginx config (e.g. client_max_body_size 500m;).
  • PHP — raise post_max_size and upload_max_filesize in php.ini.
  • Framework body parsers — check your framework's body-size limit (Express limit, Next.js bodyParser.sizeLimit, Django DATA_UPLOAD_MAX_MEMORY_SIZE, etc.).

If you want Krova Cloud to enforce a hard cap at the proxy (to reject over-size requests before they reach your app), set Max request body in the domain's settings. When a request declares its size in Content-Length, the proxy returns 413 cleanly before forwarding. Chunked uploads without a Content-Length header that exceed the cap may in rare cases return a 502 instead of 413 — this is a known Caddy limitation.

CORS

If a cross-origin frontend (e.g. a separate SPA on app.example.com calling an API at api.example.com) is being blocked by the browser, enable CORS in the domain's settings. Krova Cloud handles the entire CORS protocol at the proxy — your app never needs to touch Access-Control-* headers:

  • Preflight OPTIONS requests are answered at the edge (your app never sees them).
  • Access-Control-Allow-Origin is added to every response, including error responses (413, 403) and upstream-down interstitials, so cross-origin callers always get a parseable error.
  • Use the wildcard (*) for a public API that any origin may call. Use an explicit origin list (e.g. https://app.example.com) when you need cookies or Authorization headers (credentials).
  • The preflight cache (Access-Control-Max-Age) defaults to 600 s and can be set up to 86 400 s (Chromium's hard cap), reducing the number of preflight round-trips.

Removing a domain

Removing a domain takes down its route immediately. Update your DNS afterwards if you're pointing the domain somewhere else.

HTTPS is handled at the edge

Krova terminates TLS at its edge and forwards requests to your Cube as plain HTTP. Nothing about this is a compromise, and nothing about it leaves your traffic unencrypted: the hop from Krova's edge to your Cube stays entirely inside Krova's own infrastructure — over an internal-CA mutually authenticated connection to the host, then a private bridge into the Cube. Nothing crosses the public internet unencrypted at any point.

Services running inside a Cube should therefore listen on plain HTTP, not HTTPS. Inbound traffic reaches your Cube through Krova's edge rather than directly, so a certificate requested from inside the Cube can never be validated by a public CA — there is nothing for a validator to connect to. And if the in-cube proxy redirects port 80 to HTTPS anyway, the browser is sent to https://, Krova terminates that at the edge and forwards plain HTTP again, the panel redirects again, and the request loops forever.

If the software inside your Cube cannot be told to stop redirecting — CloudPanel, typically — you do not have to fight it. See When your Cube terminates TLS itself.

If you run Dokploy for a domain that Krova fronts, set Certificate: None for that domain in Dokploy. Krova is already handling the certificate at the edge; a certificate configured in the panel only causes the redirect loop above.

When your Cube terminates TLS itself

Some control panels hold their own certificate and cannot be told to stop — CloudPanel is the common one. Those Cubes answer plain HTTP with a redirect to HTTPS, which is exactly the loop described above. For them, turn on Connect over HTTPS in the domain's settings: Krova then speaks HTTPS on the internal hop instead of plain HTTP, the panel is satisfied, and the loop stops.

Turning it on is safe to try. Krova applies the change, then fetches the domain through the live route to confirm it really serves — and puts the domain straight back on plain HTTP if it does not. A Cube that cannot serve HTTPS internally ends up exactly where it started rather than offline.

This is not a security setting, and turning it on does not make your traffic more private: the internal hop was never exposed to begin with (see above). It exists only to satisfy software inside the Cube that refuses to answer over cleartext. If your app is happy on plain HTTP — which almost every app is — leave this off.

It is settable when you attach a domain and changeable at any time afterwards, from the domain's settings, the REST API (originScheme), the CLI, the SDK, MCP, or n8n.