This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

Server headers

Edit page

Learn how to set global and path-specific HTTP headers in Expo Router.


Server headers in Expo Router allow you to set custom HTTP headers for security, caching, cookies, and custom metadata on route responses. Global headers apply to HTML, API route, and data loader responses. In SDK 58 and later, use pageHeaders to configure headers for specific HTML pages. These options do not apply to static assets such as images, fonts, or JavaScript bundles.

Setup

1

Configure headers in the expo-router plugin in your app config:

app.json
{ "expo": { "plugins": [ [ "expo-router", { "headers": { "X-Frame-Options": "DENY" } } ] ] } }

2

Start the development server or export for production:

Terminal
- npx expo start

# Or export for production
- npx expo export -p web
- yarn expo start

# Or export for production
- yarn expo export -p web
- pnpm expo start

# Or export for production
- pnpm expo export -p web
- bun expo start

# Or export for production
- bun expo export -p web

The server automatically applies these headers to HTML, API route, and data loader responses.

Configuration

Headers are configured as an object where keys are header names and values are either strings or arrays of strings.

app.json
{ "expo": { "plugins": [ [ "expo-router", { "headers": { "X-Frame-Options": "DENY", "X-Content-Type-Options": "nosniff", "Set-Cookie": ["session=abc123; HttpOnly", "preference=dark; Path=/"] } } ] ] } }

Path-specific headers

In SDK 58 and later, use pageHeaders in the expo-router config plugin to apply headers to matching HTML pages:

app.json
{ "expo": { "plugins": [ [ "expo-router", { "headers": { "X-Frame-Options": "DENY" }, "pageHeaders": [ { "source": "/blog/[slug]", "headers": { "Cache-Control": "public, max-age=60" } }, { "source": "/account/[...path]", "headers": { "Cache-Control": "no-store" } } ] } ] ] } }

The source uses Expo Router path syntax: /blog/[slug] matches a single segment, and /account/[...path] matches nested paths. An exact path such as /about applies only to that page.

pageHeaders does not apply to API routes. Use global headers or set headers in the API route's response instead.

Examples

Security headers

Add common security headers to protect your application:

app.json
{ "expo": { "plugins": [ [ "expo-router", { "headers": { "X-Frame-Options": "DENY", "X-Content-Type-Options": "nosniff", "Referrer-Policy": "strict-origin-when-cross-origin", "X-XSS-Protection": "1; mode=block" } } ] ] } }
Cross-Origin headers for SharedArrayBuffer

Some web APIs like SharedArrayBuffer require specific Cross-Origin headers. This is required for features like expo-sqlite on web.

app.json
{ "expo": { "plugins": [ [ "expo-router", { "headers": { "Cross-Origin-Embedder-Policy": "credentialless", "Cross-Origin-Opener-Policy": "same-origin" } } ] ] } }
Cache-Control headers

Set caching policies for your responses:

app.json
{ "expo": { "plugins": [ [ "expo-router", { "headers": { "Cache-Control": "public, max-age=3600, s-maxage=86400" } } ] ] } }
Custom headers

Add custom headers with metadata about your app:

app.json
{ "expo": { "plugins": [ [ "expo-router", { "headers": { "X-App-Version": "1.0.0", "X-Environment": "production" } } ] ] } }

How it works

Output modes

Server headers work with both output modes configured in your app config:

  • static: expo-server applies headers when serving pre-rendered HTML and loader data files
  • server: The server applies headers to dynamically rendered HTML, API route, and data loader responses

The development server and npx expo serve support global and path-specific headers. For production deployments, use expo-server or EAS Hosting to apply these headers. Other static hosts need their own header configuration. For loader caching defaults and development behavior, see Data loaders: Cache defaults.

Header precedence

For a header with a string value, the following order applies, from highest to lowest priority:

  1. Headers set by the route response.
  2. Matching pageHeaders rules, for HTML pages.
  3. Global headers.

When multiple pageHeaders rules match, later rules override earlier rules for the same header. Header names are case-insensitive. The server appends array values, allowing multiple values such as Set-Cookie headers.

For example, suppose global headers sets Cache-Control: public, max-age=3600. A matching pageHeaders rule can override it with private, max-age=60. If the page response sets no-store, that value takes precedence over both settings.

Known limitations

  • Redirects: Headers do not apply to redirect responses
  • Static assets: These options do not apply to images, fonts, or JavaScript bundles
API Routes

Learn how to create server endpoints with Expo Router.

Server middleware

Learn how to create middleware that runs for every request to the server.