> ## Documentation Index
> Fetch the complete documentation index at: https://getfloo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Service Routing

> How floo routes traffic across services in a multi-service app.

Multi-service apps share a single hostname. floo routes requests to the right service based on path prefix.

## Default path assignment

| Service type | Path prefix     |
| ------------ | --------------- |
| `web`        | `/` (catch-all) |
| `api`        | `/api`          |

Only `web` and `api` service types get public gateway routes. `worker` services have no public route — they are internal only.

A `full-stack` app with a `web` and `api` service gets:

* `https://full-stack.on.getfloo.com/` → `web`
* `https://full-stack.on.getfloo.com/api/` → `api`

The `web` service is the catch-all — any path not matched by a more specific prefix falls through to it.

## Why same origin matters

All services share one hostname, so cookies and session tokens set by the `web` service are visible to the `api` service without any cross-origin configuration. No CORS headers needed for same-origin requests.

If you separate services onto different domains, you'll need to configure CORS and handle cross-origin cookie restrictions.

## Per-service debug hostnames

Each service also gets its own hostname for direct access. The format includes the environment:

| Environment | Format                               | Example                             |
| ----------- | ------------------------------------ | ----------------------------------- |
| dev         | `<app>-dev-<service>.on.getfloo.com` | `full-stack-dev-api.on.getfloo.com` |
| production  | `<app>-<service>.on.getfloo.com`     | `full-stack-api.on.getfloo.com`     |

These are useful for debugging a specific service or connecting external tools directly to one service.

## Custom domains

Add a `domain` field to a service in `floo.app.toml` to bind a custom domain to that service:

```toml theme={null}
[services.api]
type = "api"
path = "./api"
port = 8000
ingress = "public"
domain = "api.example.com"
```

A custom domain overrides the default path-prefix routing for that service. Traffic to `api.example.com` goes directly to the `api` service, bypassing the shared hostname routing.

See [Custom Domains](/docs/guides/custom-domains) for DNS setup.

## Local development

During `floo dev`, each service runs on its own localhost port. Discovery vars (`API_URL`, `WEB_URL`, etc.) are injected so services can find each other without hardcoding ports.

See [floo dev](/docs/cli/dev) for the full local dev reference.

## localhost fallback diagnostics

Source validation treats localhost URLs as non-blocking warnings:

* A localhost URL behind an explicit Node or Vite development guard does not emit a warning.
* A runtime-configured URL that may fall back to localhost emits `unverified_localhost_fallback`. floo cannot verify the production value from source alone.
* A localhost URL without a recognized development-only guard emits `hardcoded_localhost_fallback`.

For production service-to-service calls, use the injected discovery variable such as `API_URL` or a relative gateway path such as `/api`. Keep a localhost default behind an explicit development-only guard.

## Limitations

* **WebSockets use the same route table as HTTP.** WebSocket upgrade requests
  route to public `web` and `api` services by host and path prefix.
* **Only `web` and `api` service types are routable.** `worker` services are internal and have no public gateway route.
