Proxying Expo Web Requests Through Nginx
Expo Web introduces an interesting challenge, getting around CORS which isn't an issue on mobile devices. Native Expo apps skip CORS entirely, but Expo Web runs in the browser and must respect the proxy stack. However we can use Metro’s (depreciated) enhanceMiddleware hook to forward requests to Nginx (or any reverse proxy) while keeping native builds untouched.
When You Need a Proxy
Local iOS/Android clients can reach https://127.0.0.1 APIs directly, but the web bundle hits the browser CORS wall. Rather than rewriting every fetch call, tunnel the requests through Metro so they inherit the same cookies and headers your proxy provides.
Install Dependencies
Add http-proxy-middleware to your Expo workspace:
yarn add -D http-proxy-middleware
Add enhanceMiddleware
Modify metro.config.js to point the proxy to your local nginx instance.
// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const { createProxyMiddleware } = require("http-proxy-middleware");
const connect = require("connect");
const defaultConfig = getDefaultConfig(__dirname);
module.exports = {
...defaultConfig,
server: {
...defaultConfig.server,
enhanceMiddleware(middleware) {
const app = connect();
app.use(middleware);
app.use(
"/api",
createProxyMiddleware({
target: "https://127.0.0.1:8443/api",
changeOrigin: false,
secure: false
})
);
app.use(
"/auth",
createProxyMiddleware({
target: "https://127.0.0.1:8443/auth",
changeOrigin: false,
secure: false
})
);
return app;
}
}
};
Key points:
- Order matters: call
app.use(middleware)first so Metro can serve bundles before the proxy runs. - SSL to localhost:
secure: falselets you hit self-signed local certs. - One place for routes: map each upstream prefix (
/api,/neo, etc.) once instead of touching client code.
Testing the Proxy
- Start your reverse proxy (Nginx, Traefik, Caddy) on
8443or any port. - Run
npx expo start --weband confirm network requests flow through Metro to the proxy. - Switch back to
--ios/--android: native builds ignoreenhanceMiddlewareand continue hitting upstream services directly.
Because Metro only wraps the web dev server, you don’t need environment-specific fetch logic, native builds stay blissfully unaware of the proxy.