How to Fix: Calling `redirect(“./”)` from server action updates route to `./` but with page content from `/`

5 min read

Relative redirects inside a Next.js server action can corrupt the visible URL: calling redirect(“./”) from a nested route like /hello/world may leave the browser at ./ while rendering content that actually belongs to /. The result is a route/content mismatch that feels random until you look at how App Router, server actions, and redirect resolution interact.

Problem Overview

In the reported case, a form or event triggers a server action from a nested route. Inside that action, the code calls redirect(“./”). Instead of navigating cleanly to the expected route, Next.js updates the address bar to a relative-looking location while the rendered page content comes from the application root.

This usually shows up when testing a reproduction like the one linked in the issue repository: a nested route loads, a server action runs, and the redirect produces a mismatch between the browser URL and the server-rendered payload.

The practical fix is simple: do not use relative redirect targets in server actions for App Router navigation. Use an absolute app path such as redirect(“/”) or redirect(“/hello”) instead.

Understanding the Root Cause

At a technical level, this happens because redirect() in a Next.js server action is not the same thing as a browser-side relative link resolution. A server action executes on the server, and the redirect instruction is serialized into a framework-level response that the client router interprets.

The key issue is that “./” is a relative path, but the redirect handling path in this flow is not guaranteed to resolve it relative to the current nested route in the same way the browser would for a normal anchor element. In affected behavior, Next.js effectively normalizes the redirect in a way that causes the content fetch to align with /, while the displayed route string can remain inconsistent.

Why this matters:

  • Server actions run in a framework transport layer, not as ordinary client-side navigation code.
  • Relative path semantics are ambiguous unless explicitly resolved against a known canonical pathname.
  • The App Router expects stable, canonical route targets for redirects.
  • A relative redirect like “./” may not map cleanly once the action response is processed by the client runtime.

In short, the root cause is a relative redirect target being interpreted outside normal browser link resolution rules. That is why the safe pattern is to always redirect to an absolute pathname from a server action.

Step-by-Step Solution

The fix is to replace relative redirect targets with explicit absolute paths.

1. Find the server action using redirect(“./”)

'use server'

import { redirect } from 'next/navigation'

export async function submitAction() {
  redirect('./')
}

2. Replace it with an absolute route

If the intended destination is the home page:

'use server'

import { redirect } from 'next/navigation'

export async function submitAction() {
  redirect('/')
}

If the intended destination is the parent or a specific nested route, redirect directly to that exact pathname:

'use server'

import { redirect } from 'next/navigation'

export async function submitAction() {
  redirect('/hello')
}

3. If you need dynamic routing, build the full pathname explicitly

For example, if the current route is derived from params and you want to redirect to a known target, construct it deterministically:

'use server'

import { redirect } from 'next/navigation'

export async function submitAction(slug) {
  redirect(`/hello/${slug}`)
}

4. Avoid assuming server actions know the current relative route context

Unlike a browser link, a server action should not rely on implicit relative navigation behavior. Pass the destination you want, or compute it from trusted route data.

5. Re-test the reproduction flow

  1. Start the app in development mode.
  2. Open the nested route from the issue reproduction.
  3. Trigger the server action.
  4. Confirm that both the URL and the rendered content now match.
'use server'

import { redirect } from 'next/navigation'

export async function action() {
  // Good: explicit absolute path
  redirect('/')
}

If you need to preserve query strings or route segments, build them explicitly instead of using ./ or ../.

Common Edge Cases

1. Redirecting to a parent route

Developers often try redirect(“../”) expecting filesystem-like behavior. This can be just as fragile in a server action. Prefer the concrete absolute path, such as redirect(“/dashboard”).

2. Base path or internationalized routing

If your app uses a basePath or locale-prefixed routes, hardcoding the wrong absolute path can still break navigation. Make sure the redirect target matches the actual public route structure used by the app.

3. Dynamic segments

If the user is inside a route like /posts/[id], do not assume ./ will preserve the segment correctly. Build the target using the actual id or intended destination.

4. Query parameters disappearing

Switching from a relative redirect to redirect(“/”) may unintentionally drop search params. If those params matter, append them explicitly:

redirect(`/search?q=${encodeURIComponent(query)}`)

5. Mixing client navigation and server redirects

If part of the flow uses router.push() on the client and another part uses redirect() on the server, inconsistent path construction can create hard-to-debug behavior. Keep route generation centralized where possible.

6. Temporary framework-version-specific behavior

This issue can be influenced by the exact Next.js version. Even if a future release improves relative redirect handling, using absolute paths remains the most robust and readable approach.

FAQ

Should redirect(“./”) ever be used in a Next.js server action?

It is best avoided. In a server action, use absolute application paths so the redirect target is unambiguous to the framework runtime.

Why does the page content come from / while the URL looks different?

Because the redirect response and the client router state can become misaligned when a relative target is resolved inconsistently. The framework ends up fetching or rendering content for / even though the visible URL string is not a clean canonical root path.

What is the safest long-term fix for this bug?

Always call redirect() with a fully specified pathname such as /, /hello, or another explicit route. If the route is dynamic, compute the full path before redirecting.

For production apps, the takeaway is straightforward: treat server action redirects as framework-level navigation commands, not as browser-relative links. Once you switch from redirect(“./”) to a canonical absolute path, the route and rendered content stay in sync and the bug disappears.

Reference materials: issue reproduction repository and Next.js redirect documentation.

Leave a Reply

Your email address will not be published. Required fields are marked *