Skip to content

Commit

Permalink
remove next-action header when following a redirect (#65615)
Browse files Browse the repository at this point in the history
When a server action performs a redirect, we send an RSC request to the
redirect URL so that everything can be handled in a single roundtrip.

However, we forward the `next-action` header to that request. This means
that the intra-app RSC request will be incorrectly associated with an
action, and any rewrites we do for `next-action` requests (such as the
work in the Next.js builder to ensure actions are routed to streaming
outputs) won't be handled correctly.

<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Adding or Updating Examples

- The "examples guidelines" are followed from our contributing doc
https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See
https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md


## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->
  • Loading branch information
ztanner committed May 10, 2024
1 parent 26ad61a commit 85162c8
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 2 deletions.
7 changes: 6 additions & 1 deletion packages/next/src/server/app-render/action-handler.ts
Expand Up @@ -8,6 +8,8 @@ import type { BaseNextRequest, BaseNextResponse } from '../base-http'
import {
RSC_HEADER,
RSC_CONTENT_TYPE_HEADER,
NEXT_ROUTER_STATE_TREE,
ACTION,
} from '../../client/components/app-router-headers'
import { isNotFoundError } from '../../client/components/not-found'
import {
Expand Down Expand Up @@ -292,7 +294,10 @@ async function createRedirectRenderResult(
}

// Ensures that when the path was revalidated we don't return a partial response on redirects
forwardedHeaders.delete('next-router-state-tree')
forwardedHeaders.delete(NEXT_ROUTER_STATE_TREE)
// When an action follows a redirect, it's no longer handling an action: it's just a normal RSC request
// to the requested URL. We should remove the `next-action` header so that it's not treated as an action
forwardedHeaders.delete(ACTION)

try {
const response = await fetch(fetchUrl, {
Expand Down
18 changes: 18 additions & 0 deletions test/e2e/app-dir/actions/app-action.test.ts
Expand Up @@ -1310,6 +1310,24 @@ describe('app-dir action handling', () => {
expect(await browser.elementByCss('h1').text()).toBe('foo=; bar=2')
})

it('should not forward next-action header to a redirected RSC request', async () => {
const browser = await next.browser('/redirects/action-redirect')

await browser.elementById('redirect-with-search-params').click()
await retry(async () => {
expect(await browser.url()).toMatch(
/\/redirects\/action-redirect\/redirect-target\?baz=1/
)
})
// verify that the search params was set correctly
expect(await browser.elementByCss('h2').text()).toBe('baz=1')

// we should not have the next-action header in the redirected request
expect(next.cliOutput).not.toContain(
'Action header should not be present'
)
})

it.each(['307', '308'])(
`redirects properly when server action handler redirects with a %s status code`,
async (statusCode) => {
Expand Down
@@ -1,8 +1,12 @@
import { cookies } from 'next/headers'
import { cookies, headers } from 'next/headers'

export default function Page({ searchParams }) {
const foo = cookies().get('foo')
const bar = cookies().get('bar')
const actionHeader = headers().get('next-action')
if (actionHeader) {
throw new Error('Action header should not be present')
}
return (
<div>
<h1>
Expand Down

0 comments on commit 85162c8

Please sign in to comment.