Skip to content

Commit

Permalink
Fix Server Action redirection with absolute internal URL (#60798)
Browse files Browse the repository at this point in the history
Closes NEXT-2141
  • Loading branch information
shuding authored Jan 27, 2024
1 parent 03cd71b commit 93e4bb8
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 4 deletions.
28 changes: 24 additions & 4 deletions packages/next/src/server/app-render/action-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,39 @@ async function addRevalidationHeader(
async function createRedirectRenderResult(
req: IncomingMessage,
res: ServerResponse,
originalHost: Host,
redirectUrl: string,
basePath: string,
staticGenerationStore: StaticGenerationStore
) {
res.setHeader('x-action-redirect', redirectUrl)
// if we're redirecting to a relative path, we'll try to stream the response
if (redirectUrl.startsWith('/')) {

// If we're redirecting to another route of this Next.js application, we'll
// try to stream the response from the other worker path. When that works,
// we can save an extra roundtrip and avoid a full page reload.
// When the redirect URL starts with a `/`, or to the same host as application,
// we treat it as an app-relative redirect.
const parsedRedirectUrl = new URL(redirectUrl, 'http://n')
const isAppRelativeRedirect =
redirectUrl.startsWith('/') ||
(originalHost && originalHost.value === parsedRedirectUrl.host)

if (isAppRelativeRedirect) {
if (!originalHost) {
throw new Error(
'Invariant: Missing `host` header from a forwarded Server Actions request.'
)
}

const forwardedHeaders = getForwardedHeaders(req, res)
forwardedHeaders.set(RSC_HEADER, '1')

const host = req.headers['host']
const host = originalHost.value
const proto =
staticGenerationStore.incrementalCache?.requestProtocol || 'https'
const fetchUrl = new URL(`${proto}://${host}${basePath}${redirectUrl}`)
const fetchUrl = new URL(
`${proto}://${host}${basePath}${parsedRedirectUrl.pathname}`
)

if (staticGenerationStore.revalidatedTags) {
forwardedHeaders.set(
Expand Down Expand Up @@ -634,6 +653,7 @@ To configure the body size limit for Server Actions, see: https://nextjs.org/doc
result: await createRedirectRenderResult(
req,
res,
host,
redirectUrl,
ctx.renderOpts.basePath,
staticGenerationStore
Expand Down
10 changes: 10 additions & 0 deletions test/e2e/app-dir/actions/app/client/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ export default function Counter() {
redirect external
</button>
</form>
<form>
<button
id="redirect-absolute"
formAction={() =>
redirectAction(location.origin + '/redirect-target')
}
>
redirect internal with domain
</button>
</form>
<form action={getHeaders}>
<button type="submit" id="get-header">
submit
Expand Down

0 comments on commit 93e4bb8

Please sign in to comment.