Server action redirect() not working
Answered
Donskoy posted this in #help-forum
DonskoyOP
Hello, I have made a basic authentication setup and everything is working server-side, the only thing that isn't working is the redirect at the end.
Basically, the user submits a form, it calls a server action, it validates the data server-side and then at the end if everything data-wise is fine, the
server action ends with a redirect("/dashboard") call, which doesn't work.
It sends the actual HTTP 303 Redirect response, but the browser doesn't do anything. I've tested it on Firefox and Chrome so it's not a client issue.
The weird thing is that, on the same /login page (it's a server component) I have a check for if the user is already logged in, that if true it redirects them to /dashboard directly, and that works, but the server action redirect doesn't.
The only other thing I've noticed is that the redirect() on the server component uses a HTTP 307 redirect and the server action uses a HTTP 303 redirect, but I don't know if that should matter.
Basically, the user submits a form, it calls a server action, it validates the data server-side and then at the end if everything data-wise is fine, the
server action ends with a redirect("/dashboard") call, which doesn't work.
It sends the actual HTTP 303 Redirect response, but the browser doesn't do anything. I've tested it on Firefox and Chrome so it's not a client issue.
The weird thing is that, on the same /login page (it's a server component) I have a check for if the user is already logged in, that if true it redirects them to /dashboard directly, and that works, but the server action redirect doesn't.
The only other thing I've noticed is that the redirect() on the server component uses a HTTP 307 redirect and the server action uses a HTTP 303 redirect, but I don't know if that should matter.
35 Replies
Sun bear
Hi, I would say its expected behavior.
e.g. in serverside page.tsx you can redirect the user because the page is about to load and you can redirect the user.
In a server action (executed from client side) the page is already loaded and you are making more or less a post request.
From my perspective you have to redirect after the serveraction.
E.g serveraction returns true und then you do router.push("/dashboard")
e.g. in serverside page.tsx you can redirect the user because the page is about to load and you can redirect the user.
In a server action (executed from client side) the page is already loaded and you are making more or less a post request.
From my perspective you have to redirect after the serveraction.
E.g serveraction returns true und then you do router.push("/dashboard")
Madeiran sardinella
Hi, can you show your server action code?
DonskoyOP
my server action login.ts is basically this:
"use server";
import { redirect } from "next/navigation";
export async function login(formData: FormData) {
// ... formData validation and db interaction
return redirect("/dashboard");
}Madeiran sardinella
Ok, try removing that return before redirect
DonskoyOP
already did, no difference
Madeiran sardinella
Weird... Can you show the page or the code that invoques this server action?
Sun bear
https://nextjs.org/docs/app/api-reference/functions/redirect#client-component
Just make sure it is like here used in form action.
Just make sure it is like here used in form action.
DonskoyOP
In a "use client" component I have:
And then I have in my "use server" server action login.ts at the end:
<form className="space-y-6" action={login}>And then I have in my "use server" server action login.ts at the end:
redirect("/dashboard")I have confirmed that the server action doesn't exit early because it sets the session cookie correctly
it just doesn't redirect
the only difference I've noticed is that when I call redirect from within the component it sends a HTTP 307 Redirect
but the server action sends a HTTP 303 Redirect
no matter if the component is "use server" or "use client"
is there a way just to specify that I want redirect() to send a 307 redirect and not a 303 one?
Madeiran sardinella
In server actions redirect uses 307 by default
DonskoyOP
because in my server componenet where I check for a session already i have this code:
and THAT redirect returns a http header like this:
const { session } = await validateRequest();
if (session) {
redirect("/dashboard");
}and THAT redirect returns a http header like this:
Request URL: http://localhost:3000/login
Request Method: GET
Status Code: 307 Temporary RedirectMadeiran sardinella
That's ok
@Madeiran sardinella In server actions redirect uses 307 by default
DonskoyOP
it doesn't, that's literally the third sentence in the docs
"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 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."
Madeiran sardinella
Yeah, my bad, I meant that by default redirect uses 307 by default but in a server action it will send a 303 and you I think you can't change it
I'll try to test the action only with that redirection, comment all other statements and see what happens
DonskoyOP
I solved it in a strange way, but it works now
I created a route handler
for a post request that takes the FormData body
basically I had
/login/page.tsx
DonskoyOP
now I have
Answer
DonskoyOP
/login/page.tsx
+
/login/handler/route.ts
+
/login/handler/route.ts
and i changed the form action to just a string "/login/handler"
and now redirection works
thanks for your time anyway
@Donskoy thanks for your time anyway
Hey @Donskoy im just curious at this point and if you have a min id like to try and help you wiht this issue.
'use client';
import { dothing } from './actions'
export default function Page(props) {
const click = async () => {
await dothing();
}
return (
<>
<button onClick={() => click()}>Click me to get redirected</button>
</>
);
}'use server';
import { redirect } from 'next/navigation'
export async function dothing(){
redirect('/');
}This works as expected, im really just genuinely curious whats causing your issue.
Are you trying to do a redirect in a try catch block by chance? By its nature a redirect is an error kind of, so it has to either be outside the try catch block or you have to double redirect lol
DonskoyOP
@Jboncz Sorry, for the extremely late reply, but I'm coming back to this issue, because it got even weirder.
First I'm not using try catch anywhere in my code.
Second, the weird thing is that I have a /dashboard/events/create route, where I do the same stuff as the login page, but there the redirect works.
I have a client component and a server action for creating the new event and then at the end I have a redirect("/dashboard/events") and it works.
But the original login page (also a client component), which does the exact same thing doesn't work.
I've been analyzing the HTTP headers for a few hours now and the only difference I managed to see is that the X-Action-Revalidated header is changed from:
this
To this
I'm not sure what this means, I'm pretty sure its some header injected by next.js, but maybe you can help me
First I'm not using try catch anywhere in my code.
Second, the weird thing is that I have a /dashboard/events/create route, where I do the same stuff as the login page, but there the redirect works.
I have a client component and a server action for creating the new event and then at the end I have a redirect("/dashboard/events") and it works.
But the original login page (also a client component), which does the exact same thing doesn't work.
I've been analyzing the HTTP headers for a few hours now and the only difference I managed to see is that the X-Action-Revalidated header is changed from:
this
[[],0,0] on the redirect that does work.To this
[[],0,1] on the redirect on login that doesn't workI'm not sure what this means, I'm pretty sure its some header injected by next.js, but maybe you can help me
Madeiran sardinella
Maybe something into your auth logic is rewriting headers?