UNPKG

4.12 kB Markdown View Raw
1---
2title: HTTP Headers
3---
4
5# HTTP Headers
6
7[MODES: framework]
8
9## Reading request headers
10
11The `request` sent to route handlers is a standard Web Fetch [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request), so you can read headers directly from the [`request.headers`](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers) property:
12
13```tsx filename=some-route.tsx
14export async function loader({
15 request,
16}: Route.LoaderArgs) {
17 // Standard Headers methods are available
18 const userAgent = request.headers.get("User-Agent");
19 const hasCookies = request.headers.has("Cookie");
20
21 // ...
22}
23```
24
25## Setting response headers
26
27Headers are primarily defined with the route module `headers` export. You can also set headers in `entry.server.tsx`.
28
29### From Route Modules
30
31```tsx filename=some-route.tsx
32import { Route } from "./+types/some-route";
33
34export function headers(_: Route.HeadersArgs) {
35 return {
36 "Content-Security-Policy": "default-src 'self'",
37 "X-Frame-Options": "DENY",
38 "X-Content-Type-Options": "nosniff",
39 "Cache-Control": "max-age=3600, s-maxage=86400",
40 };
41}
42```
43
44You can return either a [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) instance or `HeadersInit`.
45
46### From loaders and actions
47
48When the header is dependent on loader data, loaders and actions can also set headers.
49
50**1. Wrap your return value in `data`**
51
52```tsx lines=[1,8]
53import { data } from "react-router";
54
55export async function loader({ params }: LoaderArgs) {
56 let [page, ms] = await fakeTimeCall(
57 await getPage(params.id),
58 );
59
60 return data(page, {
61 headers: {
62 "Server-Timing": `page;dur=${ms};desc="Page query"`,
63 },
64 });
65}
66```
67
68**2. Return from `headers` export**
69
70Headers from loaders and actions are not sent automatically. You must explicitly return them from the `headers` export.
71
72```tsx
73function hasAnyHeaders(headers: Headers): boolean {
74 return [...headers].length > 0;
75}
76
77export function headers({
78 actionHeaders,
79 loaderHeaders,
80}: HeadersArgs) {
81 return hasAnyHeaders(actionHeaders)
82 ? actionHeaders
83 : loaderHeaders;
84}
85```
86
87One notable exception is `Set-Cookie` headers, which are automatically preserved from `headers`, `loader`, and `action` in parent routes, even without exporting `headers` from the child route.
88
89### Merging with parent headers
90
91Consider these nested routes
92
93```ts filename=routes.ts
94route("pages", "pages-layout-with-nav.tsx", [
95 route(":slug", "page.tsx"),
96]);
97```
98
99If both route modules want to set headers, the headers from the deepest matching route will be sent.
100
101When you need to keep both the parent and the child headers, you need to merge them in the child route.
102
103#### Appending
104
105The easiest way is to simply append to the parent headers. This avoids overwriting a header the parent may have set and both are important.
106
107```tsx
108export function headers({ parentHeaders }: HeadersArgs) {
109 parentHeaders.append(
110 "Permissions-Policy",
111 "geolocation=()",
112 );
113 return parentHeaders;
114}
115```
116
117#### Setting
118
119Sometimes it's important to overwrite the parent header. Do this with `set` instead of `append`:
120
121```tsx
122export function headers({ parentHeaders }: HeadersArgs) {
123 parentHeaders.set(
124 "Cache-Control",
125 "max-age=3600, s-maxage=86400",
126 );
127 return parentHeaders;
128}
129```
130
131You can avoid the need to merge headers by only defining headers in "leaf routes" (index routes and child routes without children) and not in parent routes.
132
133### From `entry.server.tsx`
134
135The `handleRequest` export receives the headers from the route module as an argument. You can append global headers here.
136
137```tsx
138export default async function handleRequest(
139 request,
140 responseStatusCode,
141 responseHeaders,
142 routerContext,
143 loadContext,
144) {
145 // set, append global headers
146 responseHeaders.set(
147 "X-App-Version",
148 routerContext.manifest.version,
149 );
150
151 return new Response(await getStream(), {
152 headers: responseHeaders,
153 status: responseStatusCode,
154 });
155}
156```
157
158If you don't have an `entry.server.tsx` run the `reveal` command:
159
160```shellscript nonumber
161react-router reveal
162```
163
\No newline at end of file