Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/01-app/02-guides/redirecting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export async function createPost(id) {

> **Good to know**:
>
> - `redirect` returns a 307 (Temporary Redirect) status code by default. When used in a Server Action, it returns a 303 (See Other), which is commonly used for redirecting to a success page as a result of a POST request.
> - In a Server Action, `redirect` performs a client-side navigation when JavaScript is available. Without JavaScript, the form submission uses a 303 (See Other). In other contexts, `redirect` uses a 307 (Temporary Redirect).
> - `redirect` throws an error so it should be called **outside** the `try` block when using `try/catch` statements.
> - `redirect` can be called in Client Components during the rendering process but not in event handlers. You can use the [`useRouter` hook](#userouter-hook) instead.
> - `redirect` also accepts absolute URLs and can be used to redirect to external links.
Expand Down Expand Up @@ -132,7 +132,7 @@ export async function updateUsername(username, formData) {

> **Good to know**:
>
> - `permanentRedirect` returns a 308 (permanent redirect) status code by default.
> - In a Server Action, `permanentRedirect` performs a client-side navigation when JavaScript is available. Without JavaScript, the form submission uses a 303 (See Other). In other contexts, `permanentRedirect` uses a 308 (Permanent Redirect).
> - `permanentRedirect` also accepts absolute URLs and can be used to redirect to external links.
> - If you'd like to redirect before the render process, use [`next.config.js`](#redirects-in-nextconfigjs) or [Proxy](#nextresponseredirect-in-proxy).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ related:

The `permanentRedirect` function allows you to redirect the user to another URL. `permanentRedirect` can be used in Server Components, Client Components, [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Functions](/docs/app/getting-started/mutating-data).

When used in a streaming context, this will insert a meta tag to emit the redirect on the client side. When used in a server action, it will serve a 303 HTTP redirect response to the caller. Otherwise, it will serve a 308 (Permanent) HTTP redirect response to the caller.
When used in a streaming context, this will insert a meta tag to emit the redirect on the client side. In a Server Action, `permanentRedirect` performs a client-side navigation when JavaScript is available. For progressive enhancement form submissions, it serves a 303 HTTP redirect response. Otherwise, it serves a 308 (Permanent) HTTP redirect response.

If a resource doesn't exist, you can use the [`notFound` function](/docs/app/api-reference/functions/not-found) instead.

Expand Down
4 changes: 2 additions & 2 deletions docs/01-app/03-api-reference/04-functions/redirect.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ related:

The `redirect` function allows you to redirect the user to another URL. `redirect` can be used while rendering in [Server and Client Components](/docs/app/getting-started/server-and-client-components), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Functions](/docs/app/getting-started/mutating-data).

When used in a [streaming context](/docs/app/getting-started/linking-and-navigating#streaming), this will insert a meta tag to emit the redirect on the client side. When used in a server action, it will serve a 303 HTTP redirect response to the caller. Otherwise, it will serve a 307 HTTP redirect response to the caller.
When used in a [streaming context](/docs/app/getting-started/linking-and-navigating#streaming), this will insert a meta tag to emit the redirect on the client side. In a Server Action, `redirect` performs a client-side navigation when JavaScript is available. For progressive enhancement form submissions, it serves a 303 HTTP redirect response. Otherwise, it serves a 307 HTTP redirect response.

If a resource doesn't exist, you can use the [`notFound` function](/docs/app/api-reference/functions/not-found) instead.

Expand Down Expand Up @@ -212,7 +212,7 @@ The introduction of the `307` status code means that the request method is prese
- `302` - Temporary redirect, will change the request method from `POST` to `GET`
- `307` - Temporary redirect, will preserve the request method as `POST`

The `redirect()` method uses a `307` by default, instead of a `302` temporary redirect, meaning your requests will _always_ be preserved as `POST` requests.
The `redirect()` method uses a `307` by default, instead of a `302` temporary redirect, meaning the request method is preserved. Server Action form submissions are an exception: they use a `303` response so the browser follows the redirect with a `GET` request. When JavaScript is available, Server Actions perform a client-side navigation instead of an HTTP redirect.

[Learn more](https://developer.mozilla.org/docs/Web/HTTP/Redirections) about HTTP Redirects.

Expand Down
6 changes: 4 additions & 2 deletions packages/next/src/client/components/redirect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export function getRedirectError(
* [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
*
* - In a Server Component, this will insert a meta tag to redirect the user to the target page.
* - In a Route Handler or Server Action, it will serve a 307/303 to the caller.
* - In a Route Handler, it will serve a 307 to the caller.
* - In a Server Action, it will perform a client-side navigation when JavaScript is available or serve a 303 for a progressive enhancement form submission.
* - In a Server Action, type defaults to 'push' and 'replace' elsewhere.
*
* Read more: [Next.js Docs: `redirect`](https://nextjs.org/docs/app/api-reference/functions/redirect)
Expand All @@ -46,7 +47,8 @@ export function redirect(
* [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
*
* - In a Server Component, this will insert a meta tag to redirect the user to the target page.
* - In a Route Handler or Server Action, it will serve a 308/303 to the caller.
* - In a Route Handler, it will serve a 308 to the caller.
* - In a Server Action, it will perform a client-side navigation when JavaScript is available or serve a 303 for a progressive enhancement form submission.
*
* Read more: [Next.js Docs: `redirect`](https://nextjs.org/docs/app/api-reference/functions/redirect)
*/
Expand Down
13 changes: 8 additions & 5 deletions packages/next/src/server/app-render/action-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1272,12 +1272,13 @@ export async function handleAction({
const redirectUrl = getURLFromRedirectError(err)
const redirectType = getRedirectTypeFromError(err)

// if it's a fetch action, we'll set the status code for logging/debugging purposes
// but we won't set a Location header, as the redirect will be handled by the client router
res.statusCode = RedirectStatusCode.SeeOther
metadata.statusCode = RedirectStatusCode.SeeOther

if (isFetchAction) {
// Fetch actions communicate redirects through `x-action-redirect` and
// can include the redirect target's Flight response in the body. Since
// this is not an HTTP redirect, keep the response successful.
res.statusCode = 200
metadata.statusCode = 200

return {
type: 'done',
result: await createRedirectRenderResult(
Expand All @@ -1294,6 +1295,8 @@ export async function handleAction({
}

// For an MPA action, the redirect doesn't need a body, just a Location header.
res.statusCode = RedirectStatusCode.SeeOther
metadata.statusCode = RedirectStatusCode.SeeOther
res.setHeader('Location', redirectUrl)
return {
type: 'done',
Expand Down
6 changes: 4 additions & 2 deletions packages/next/src/server/lib/router-utils/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,8 @@ declare module 'next/navigation' {
* [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
*
* - In a Server Component, this will insert a meta tag to redirect the user to the target page.
* - In a Route Handler or Server Action, it will serve a 307/303 to the caller.
* - In a Route Handler, it will serve a 307 to the caller.
* - In a Server Action, it will perform a client-side navigation when JavaScript is available or serve a 303 for a progressive enhancement form submission.
* - In a Server Action, type defaults to 'push' and 'replace' elsewhere.
*
* Read more: [Next.js Docs: redirect](https://nextjs.org/docs/app/api-reference/functions/redirect)
Expand All @@ -396,7 +397,8 @@ declare module 'next/navigation' {
* [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
*
* - In a Server Component, this will insert a meta tag to redirect the user to the target page.
* - In a Route Handler or Server Action, it will serve a 308/303 to the caller.
* - In a Route Handler, it will serve a 308 to the caller.
* - In a Server Action, it will perform a client-side navigation when JavaScript is available or serve a 303 for a progressive enhancement form submission.
*
* Read more: [Next.js Docs: redirect](https://nextjs.org/docs/app/api-reference/functions/redirect)
*/
Expand Down
14 changes: 12 additions & 2 deletions test/e2e/app-dir/actions/app-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1185,7 +1185,12 @@ describe('app-dir action handling', () => {

expect(request.url()).toEqual(`${next.url}${initialPagePath}`)
expect(request.method()).toEqual('POST')
expect(response.status()).toEqual(303)
expect(response.status()).toEqual(200)

const headers = await response.allHeaders()
expect(headers['x-action-redirect']).toBeDefined()
expect(headers.location).toBeUndefined()
expect(headers['content-type']).toContain('text/x-component')
}
)

Expand Down Expand Up @@ -1298,7 +1303,12 @@ describe('app-dir action handling', () => {

expect(request.url()).toEqual(`${next.url}${initialPagePath}`)
expect(request.method()).toEqual('POST')
expect(response.status()).toEqual(303)
expect(response.status()).toEqual(200)

const headers = await response.allHeaders()
expect(headers['x-action-redirect']).toBeDefined()
expect(headers.location).toBeUndefined()
expect(headers['content-type']).toContain('text/x-component')
}
)

Expand Down
12 changes: 10 additions & 2 deletions test/e2e/app-dir/app-basepath/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,12 @@ describe('app dir - basepath', () => {

expect(request.url()).toEqual(`${next.url}${initialPagePath}`)
expect(request.method()).toEqual('POST')
expect(response.status()).toEqual(303)
expect(response.status()).toEqual(200)

const headers = await response.allHeaders()
expect(headers['x-action-redirect']).toBeDefined()
expect(headers.location).toBeUndefined()
expect(headers['content-type']).toContain('text/x-component')
}
)

Expand Down Expand Up @@ -204,7 +209,10 @@ describe('app dir - basepath', () => {
expect(secondRequest.url()).toEqual(`${next.url}${destinationPagePath}`)
expect(secondRequest.method()).toEqual('GET')

expect(firstResponse.status()).toEqual(303)
expect(firstResponse.status()).toEqual(200)
const headers = await firstResponse.allHeaders()
expect(headers['x-action-redirect']).toBeDefined()
expect(headers.location).toBeUndefined()
// Since this is an external request to a resource outside of NextJS
// we expect to see a separate request resolving the external URL.
expect(secondResponse.status()).toEqual(200)
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/app-dir/server-action-logging/app/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export async function multiArgAction(a, b, c) {
return { sum: a + b + c }
}

// Action that redirects (should show 303 status)
// Action that redirects
export async function redirectAction(path) {
redirect(path)
}
Expand Down
Loading