Sending requests out
Every request your app sends to the internet goes through our outbound proxy. That is where secrets are added, and where you can hold an app to a list of hosts. Both are on every plan.
See where an app sends requests
Section titled “See where an app sends requests”An app’s Activity tab has Sends requests to: the hosts the app sent requests to in the last 30 days, busiest first, with refused requests in red: ones its list didn’t allow, and ones to an AI provider the company key doesn’t cover. The app’s editors and owners can read the same with jiayang egress usage acme/payroll, or through the API at GET /v1/workspaces/<workspace>/apps/<app>/egress/usage, along with the workspace’s owners and admins. Owners and admins also see each app’s busiest hosts in the workspace’s list of apps and its CSV, and the workspace export carries each app’s list and AI grants and which AI keys the workspace has, never a key.
Counts are kept by the hour and can take a few minutes to show. A request is counted as sent once the host has answered, so a name that doesn’t resolve isn’t counted. An app that sends requests to more than 2,000 different hosts in an hour has the rest counted together as other hosts, with how many hosts went into that line, and so does a name no real host could have. Counts are kept for 90 days. We count only the host: never the path, the headers or the body.
Allow only some hosts
Section titled “Allow only some hosts”By default an app can send requests to any host except our own, and except a private address like 10.0.0.1, 127.0.0.1 or 169.254.169.254, which no public host has. A request to one is refused with 403 and Refused: apps can't send requests to a private address., list or no list, so no app can reach another app’s container, or anything else on our network, by its address. To allow only the hosts you name, add them to the app’s list and turn the list on:
jiayang egress allow acme/payroll api.stripe.comjiayang egress allow acme/payroll "*.googleapis.com"jiayang egress on acme/payrollacme/payroll can send requests only to these hosts:*.googleapis.comapi.stripe.comA request to any other host is refused with a 403 that says Refused: example.org isn't on this app's list of hosts it can send requests to., and nothing is sent. To an AI provider’s host it comes in that provider’s error shape, so its SDK shows it. The refusal carries an x-jiayang-egress-list header with the version of the list that refused it, which goes up with every change.
jiayang egress list acme/payroll shows the list and whether it is on. jiayang egress deny acme/payroll api.stripe.com takes a host off, and jiayang egress off acme/payroll lets the app reach any host again. Changes apply within 10 seconds.
In the dashboard, the list is under the app’s Settings, in Sending requests out.
Only the workspace’s owners and admins can let an app reach more: add a host, or turn the list off. The app’s editors and owners can hold it in further: turn the list on, or take a host off. Every change is in the workspace’s audit log as egress.allow, egress.deny, egress.restrict or egress.unrestrict.
What a host can be
Section titled “What a host can be”- An exact host, like
api.stripe.com. It matches that host only, notx.api.stripe.com. *.and a domain, like*.stripe.com. It matches every host understripe.com, at any depth, but notstripe.comitself. Add both if you need both. A suffix anyone can register a name under, like*.github.io,*.workers.dev,*.amazonaws.comor*.co.uk, can’t be listed: it would let the app reach hosts anybody can make. We refuse the ones on the Public Suffix List, and some common ones that aren’t, so still check that a pattern covers only hosts you trust.- Not one of ours, like
*.jiayang-apps.cloud: an app’s requests to our own hosts are always refused, so listing one would only mislead. - Not a name nobody can register, like
foo.internalorprinter.local: no request could reach it.
Hosts are matched without regard to case, a trailing dot or the port: listing api.stripe.com allows it on any port. An international name can be given in either form, bücher.de or xn--bcher-kva.de, and is kept in the second. An IP address can’t be listed, so with the list on, a request to an address is always refused. A list holds up to 200 hosts. A service on your private network that an owner or admin has granted the app is reached whatever its list says, by name or address: the grant is the allow. So the list doesn’t show those services. The Private network page and jiayang network list show which apps each one is granted to.
With the list on and nothing on it, the app can’t send requests anywhere. The key set your app verifies sign-ins with is the one exception: it is always reachable, so the SDKs keep working.
How it’s checked
Section titled “How it’s checked”The proxy checks the host in the URL it is about to fetch, after the URL is parsed. So:
- Credentials in a URL don’t count:
https://api.stripe.com@evil.example/goes toevil.example, and is refused. - A
Hostheader your app sets is dropped. The URL decides where a request goes. - The proxy never follows a redirect. Your app gets the redirect back, and when it follows one, that request is checked like any other. A listed host can’t send your app on to one that isn’t listed.
CONNECTis refused, so nothing can open a tunnel to a host that isn’t listed.
The same holds for Workers apps and container apps, with one difference: a container app can’t send an HTTPS request to an IP address at all. The connection fails with a TLS error before anything is sent, because a connection to an address carries no host name for our proxy to answer as. Use the host’s name. Over http, a request to an address is checked like any other.
What a list can’t stop
Section titled “What a list can’t stop”The list says which hosts your app’s requests go to, not what those hosts do with them. A listed API that fetches a URL you give it, sends a webhook or a message, or stores what you send, can pass data on to places the list doesn’t name. List the hosts you need and no more, and know what each of them can do on your behalf.