Skip to content

Routing ​

Every service with a port gets a route: a path prefix on Portico's port. A request goes to the service with the longest route that matches its path.

At a Glance ​

  • portico routes prints the route table without starting anything.
  • Routes are mounted under proxy.base, which defaults to /.

Routes and Prefixes ​

A service is mounted at /<name> unless route says otherwise:

yaml
services:
  web:
    run: npm run dev -- --port {port}
    route: /
  api:
    port: 3200

Here /api/users goes to api, and every other path goes to web. A path matches a route when it equals the route or continues it after a /, so /apiary goes to web, not api.

Two services can't share a route; Portico refuses to start when they do.

Stripping the Prefix ​

By default the route is removed before the request is forwarded: api sees /users, not /api/users. The original prefix travels in X-Forwarded-Prefix, for apps that build their own links.

Set strip: false for an app that expects the full path, such as one built with a base path:

yaml
services:
  docs:
    run: npm run dev -- --port {port} --base /docs
    route: /docs
    strip: false

A Prefix for Everything ​

proxy.base (or --base) mounts every route under one prefix. With base: /app, web answers on /app and api on /app/api.

Compose Services ​

A compose service expands into one route per compose service that publishes a port, at <route>/<compose service>:

yaml
services:
  stack:
    compose: ./compose.yaml

If compose.yaml has db and queue services that publish ports, they're routed at /stack/db and /stack/queue. A compose service that publishes several ports gets one route per port, such as /stack/queue/5672 and /stack/queue/15672.

Portico reads the ports from compose config, so environment variables, override files and profiles are already applied.

Move individual routes with routes:

yaml
services:
  stack:
    compose: ./compose.yaml
    routes:
      queue: /rabbit

Or route a single compose service with service, which also starts only that one:

yaml
services:
  grafana:
    compose: ./compose.yaml
    service: grafana
    route: /grafana

When that service publishes several ports, port picks the one to route.

What Your Service Sees ​

Portico forwards each request to localhost:<port> and rewrites a few headers:

HeaderValue
Hostlocalhost:<port>, so dev server host checks pass.
X-Forwarded-HostThe host the browser used, or what a gateway in front already set.
X-Forwarded-Protohttp, or what a gateway in front already set.
X-Forwarded-ForThe client address chain.
X-Forwarded-PrefixThe route, when it was stripped.

Apps that compare Origin to Host, like Django's CSRF check, either trust X-Forwarded-Host and X-Forwarded-Proto, or set host: preserve to receive the browser's Host instead.

Websockets and server-sent events pass straight through, and responses stream as they're written.

Next Steps ​

  • Proxy: CORS, error responses and the nginx engine.
  • Containers: image and compose services.

Released under the MIT License