File: override.md | Updated: 11/15/2025
Hide navigation
Search
Ctrl K
Home Guides EAS Reference Learn
Archive Expo Snack Discord and Forums Newsletter
Copy page
Learn how to override the update URL and request headers at runtime to control which update is loaded on the client side.
Copy page
The typical way to use EAS Update is to have a single update URL and a set of request headers (such as update channel name) embedded in a build of your app. To control which update is loaded you make changes on the server through the eas update command or the EAS dashboard. For example, you publish a new update to a channel that your build is pointing to, then the build fetches that update on the next launch. Updates published to a channel different from the one your build is pointing to will not be downloaded with this approach.
This guide explains how you can change the update URL and request headers at runtime, making it possible to load a specific update by ID or change the channel that updates are pulled from without creating and installing a new build.
The feature described in this section is available in Expo SDK 54 with the
expo-updatesversion 0.29.0 and later.
The primary use case for this feature is to enable switching between update channels in a production build. This is useful to enable non-technical stakeholders to test and validate updates of work-in-progress (such as from a pull request or different feature branches) without having to use a development build or create a separate preview build for each change.
For example, if you have a default channel for production updates and a preview channel for preview updates, you can override the expo-channel-name request header to point to the preview channel. This enables you to test preview updates in your current production builds.
Another potential use case is to provide different updates to different users, for example, so that a group of internal users (such as employees) receive updates before end-users.
You can override the expo-channel-name request header by calling Updates.setUpdateRequestHeadersOverride
. This will override update requests to fetch updates from the specified channel.
Somewhere in your app, you would provide a way for users to trigger the change to request headers. This may be in a hidden menu that only trusted users have access to, or some other mechanism, depending on your use case. After the parameters are changed, you can call fetchUpdateAsync()
to fetch the update, and then reloadAsync()
to reload the app. Or you can wait for the next launch, which will automatically fetch and install the update.
import * as Updates from 'expo-updates'; // Where you call this method depends on your use case - it may make sense to // have a menu in your preview builds that allows testers to pick from available channels, // for example: Updates.setUpdateRequestHeadersOverride({ 'expo-channel-name': 'preview' }); // You can fetch and reload the update immediately, or wait for the next launch await Updates.fetchUpdateAsync(); await Updates.reloadAsync();
Override both update URL and request headers
The feature described in this section is available in Expo SDK 52 with the
expo-updatesversion 0.27.0 and later. Using thedisableAntiBrickingMeasuresoption is not recommended for production apps, it is currently primarily intended for preview environments.
Similar to override request headers
, if you want to further override the update URL to a specific update, you can use the Updates.setUpdateURLAndRequestHeadersOverride
method. This allows you to load a specific update by ID, even if the update is published before the current build is created.
It is important to be familiar with the security considerations before deciding to use this feature in production. In the future, we may add support for a more restricted version of the feature that would be more suitable for this use case.
There are two relevant APIs:
Updates.setUpdateURLAndRequestHeadersOverride({ url: string, requestHeaders: Object }) - this method overrides the update URL and the request headers that are specified in app.json / Expo.plist / AndroidManifest.xml, such as the expo-channel-name header.disableAntiBrickingMeasures - this field in the app config disables anti-bricking measures built-in to expo-updates which ensure subsequent updates can always be published to fix issues in previously-installed update. When you change this value, you will need to create a new build for it to take effect. Do not enable this in your production builds. The reason for this name is to clearly indicate that when you override the update URL/headers, we're no longer able to safely rollback to the previous update that was loaded. So, if the new update you have loaded causes the app to crash then expo-updatescannot automatically recover, because this field in conjunction with setUpdateURLAndRequestHeadersOverride will disable embedded updates and therefore there will not be any update to rollback to. The user would need to uninstall and reinstall the app. You should only use this feature in preview builds.How to use these APIs:
expo-updates library, with methods like checkForUpdateAsync(), will not use the new overridden URL and request headers until the app is closed and reopened.The anti-bricking measures that can be disabled with disableAntiBrickingMeasures ensure that, no matter what update is published, you can always publish another update afterwards that will be applied. By disabling the anti-bricking measures, certain categories of attacks and exploits become possible, especially around in-house (compromised employee) publishing of malicious updates. For example, an employee with the ability to publish updates could publish a malicious update that changes the update URL and request headers to point to their own server, and take over installations of the app. This risk can be mitigated, but not eliminated, by using code signing
for production updates and limiting access to the key.
Did similar usage of CodePush carry the same risk?
Yes. CodePush allowed developers to swap deployment keys with sync({ deploymentKey: string }) which could be used maliciously take over an app installation in this same way.
Here's an example of how you might use these APIs:
import * as Updates from 'expo-updates'; // Where you call this method depends on your use case - it may make sense to // have a menu in your preview builds that allows testers to pick from available // pull requests, for example. function overrideUpdateURLAndHeaders() { Updates.setUpdateURLAndRequestHeadersOverride({ url: 'https://u.expo.dev/{updateId}/group/{groupId}', requestHeaders: {}, }); alert('Close and re-open the app to load the latest version.'); }
{ "expo": { "updates": { // We recommend only enabling this in preview builds. // You can use app.config.js to configure it dynamically. "disableAntiBrickingMeasures": true // etc.. } } }