---
title: 'Incremental EAS Hosting changes: May 2025'
authors: Phil Pluckthun
published: May 27, 2025
---

EAS Hosting’s updates ship incrementally without versioning and are backwards compatible. Runtime changes will require you to re-deploy with `eas deploy` while network and CDN changes apply automatically.

### Wildcard custom domains

We’ve received several requests to add support for subdomains on EAS Hosting’s custom domains, and support for `www` subdomains specifically. We’re happy to report that this has now been implemented.

Once you’ve clicked “Refresh” in the EAS Hosting settings’ “Custom domains” section, you’ll now be able to set up subdomain records for your custom domain, for example:

- `CNAME <alias>.<yourdomain> origin.expo.app` sets up a subdomain for a named alias, so you can for example link `staging.yourdomain.com` directly into EAS Hosting
- `CNAME www.<yourdomain> origin.expo.app` sets up a redirect to your custom domain automatically (as long as no alias named `www` exists)
- `CNAME *.<yourdomain> origin.expo.app` sets up a wildcard for all aliases in your project

Note that we still only support one custom domain per project. But we hope that the support for wildcard subdomains on this custom domain will alleviate the need to set up multiple custom domains per project.

Learn more about how to set up the new DNS records in our docs: [https://docs.expo.dev/eas/hosting/custom-domain/#alias-and-wildcard-subdomains](https://docs.expo.dev/eas/hosting/custom-domain/#alias-and-wildcard-subdomains)

### Notice: Custom Domain CNAMEs are changing

Instead of `fallback.expo.app` we now recommend you to set up CNAMEs for your custom domains with the `origin.expo.app` value instead.

[`origin.expo.app`](http://origin.expo.app) itself is a CNAME that links to `fallback.expo.app` . This is mainly an internal change that allows us to change what `origin.expo.app` points to in case we need to make changes to our internal CDN configuration.

**No changes are required immediately if you’ve already set up a custom domain. `fallback.expo.app` will continue to work for the foreseeable future, and we recommend you to switch over this value within the next 6 months.**

If we decide to shut down `fallback.expo.app` as a valid value, we’ll send out repeated notices before doing so, but we don’t have any immediate plans to force a switchover yet.

### Worker Deployment File Browsers

The dashboard’s worker deployments details page now displays file trees both for your deployments’ server code and client assets. We felt that having a file browser to quickly see what past deployments contain is crucial and we’ll be adding more details and information to this page in the future.

![Worker Deployment File Browsers](https://cdn.sanity.io/images/9r24npb8/production/c48adbe7155bbf154cb276cd89062da4a276ffe5-1953x894.png)

![Worker Deployment File Browsers](https://cdn.sanity.io/images/9r24npb8/production/af00e0c42ad2ea2753a3dfda15518e334816f29c-1947x633.png)

### Request logs record Expo routes and pathnames

Previously, our request logging didn’t record both pathnames and Expo Router route names. Instead, it could only record and display one at a time.

This was especially obvious with React Server Component requests always displaying `/_flight/[...rsc]` for many requests and entries.

Instead, our logging now records route parameters as well, when we’ve detected that a request is being made to an Expo Router API route, which means that the dashboard now displays both the full original path as well as the Expo Router route.

![Request logs record Expo routes and pathnames](https://cdn.sanity.io/images/9r24npb8/production/53397487db640c5723250da4ceba93b40f6c73bf-1955x1334.png)

Here we see a request to the exported RSC route, `/_flight/[...rsc]`, is being expanded to show the request’s deep route parameter, `/web/ACTION_./actions/render-info.tsx/default.txt`.

## New Hosting Overview

An improved at-a-glance dashboard for how your project is doing.

This also enables new request filters: by platform and country.

![New Hosting Overview](https://cdn.sanity.io/images/9r24npb8/production/d3b41e0badb7e6af09d4aa4af4d5fb0b3e6c80c2-2482x2376.png)

## Improved request header URLs

When a complex app is deployed with many different aliases, a custom domain, and environments, it was previously tricky to write code that reacts to these changing request origins.

For example, previously, when a request is made to a worker deployment `https://myapp--staging.expo.app/api/route`, the API route would mostly only see its own URL even if the client made a request to an alias URL or a custom domain:

```console
$ GET https://myapp--id.expo.app/api/route

Host: myapp--id.expo.app
Forwarded: for=1.2.3.4;proto=https
X-Forwarded-For: 1.2.3.4
X-Forwarded-Proto: https
X-Real-IP: 1.2.3.4
```

In the previous request headers, no headers or hints were available for the incoming request’s URL and only the outgoing one. Hence, it was impossible to write conditional logic for requests being made to custom domains, for instance.

We’ve updated these headers and they now reflect both the incoming origin URL and the target URL separately:

```console
$ GET https://myapp--id.expo.app/api/route

Host: myapp--id.expo.app
Origin: https://myapp--staging.expo.app
Forwarded: for=1.2.3.4;host="myapp--staging.expo.app";proto=https
X-Forwarded-Host: myapp--staging.expo.app
X-Forwarded-For: 1.2.3.4
X-Forwarded-Proto: https
X-Real-IP: 1.2.3.4
```

As seen above, although the request to `myapp--staging.expo.app` resolves to `myapp--id.expo.app`, the `Origin`, `Forwarded`, and `X-Forwarded-Host` headers now reflect the incoming URL’s hostname instead.

## New `origin` global

To complement this change, the `origin` global was updated to output the current request URL’s `Origin` per request. Previously, this property would always be set to `"null"`.

```javascript
export async function GET(request) {
	console.log(origin); // 'https://myapp--staging.expo.app'
}
```

It now outputs the current request URL’s origin, depending on the ongoing request, dynamically, and should always be identical to `request.headers.get('Origin')`.

> ⚠️ This global is only available in EAS Hosting worker deployments. `expo start` and `expo serve` currently stick to a plain Node.js environment and won’t emulate it.

## Support for `404.html` for static deployments

Not all static site generators follow the same output conventions. While Expo Router’s `static` output generates `+not-found.html` files for “404 Not Found” pages, other static-site generators prefers to output `404.html` files.

To help with making EAS Hosting useful for static sites that aren’t generated with Expo Router, we now support the `404.html` output variants as well.

A static deployment is only expected to either use `+not-found.html` pages or `404.html` pages for not-found pages, but not both at the same time. While deploying, EAS Hosting will detect and decide which one it’ll use ahead of time.

## Fix for `HEAD` requests to assets

We’ve noticed that `HEAD` requests made to assets weren’t resolving correctly, and would instead result in 404 responses, even if the path matches an asset’s path.

This is now fixed and working as intended.

## Fix for the `agentkeepalive` package

Previously, due to `options: AgentOptions;` not being present on `node:http`'s `Agent` class, the `agentkeepalive` package wasn’t functional and would throw a runtime error.

This has now been fixed and the missing internal property has been added to our layer of Node polyfills.

## Streamed worker deployments

The size limit for worker deployments is generally 10MiB (gzip compressed) for all server files combined. When this size is exceeded by your upload, the worker deployment cannot be created and an error will be displayed by `eas-cli`.

However, this wasn’t always the case,

- some worker deployments could cause our upload API to run out of memory, which prevented us from completing the deployment successfully
- worker deployments exceeding 10MiB (gzipped) could cause our upload API to run out of memory as well, which prevented us from displaying an error message of what had happened

This has now been addressed by rewriting our upload API to be streamed. Instead of unpacking and handling your worker deployments in memory, it will now stream each part of the upload where it needs to go and prevent itself from running out of memory.

This has also made uploads more reliable and a little quicker.

## Internal caching changes

All caching in EAS Hosting is namespaced per worker deployment. However, we also have layers of caching that apply to routing. For example, EAS Hosting will cache where a request to an alias (e.g. `myapp--staging` → `myapp--id`) needs to go.

This meant that changing aliases, deleting aliases, deleting worker deployments, and promoting worker deployments to production was taking longer than expected.

We’re now proactively purging caches to prevent this, which should speed up all mentioned operations to complete much sooner. Often in a few seconds, and at most in a few minutes.

## Added missing `process.stdout` and `process.stderr` normalization

In EAS Hosting, any library that writes to `process.stdout` and `process.stderr` is supported and their output will be redirected to the Console API. This means that common debugging packages outputs’ (such as `debug`'s output) isn’t lost.

However, previously we weren’t splitting this terminal output into separate lines and ANSI escape codes weren’t removed. This could result in confusing logs being displayed in the EAS Hosting dashboard.

We’re now normalizing this output and it should be more readable now.

## Cloudflare Worker runtime updates

The Cloudflare team has been hard at work to update their Node compatibility and to add more features to their runtime:

- EAS Hosting’s `WeakRef` shim has now been replaced with native support for `WeakRef` and `FinalizationRegistry`
- `node:tls` support has been added (for client requests only) and replaces the previous, non-functional EAS Hosting shim
- The `URLPattern` web standard global is now supported

For more information on the latest runtime changes, check out [Cloudflare’s workerd repository](https://github.com/cloudflare/workerd)’s release notes.