# Server-Side Auth Redirects

**MUST** · **ID:** `design-server-auth-redirect` · **Category:** design
**Source:** [Rauno](https://interfaces.rauno.me/)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/design-server-auth-redirect

> MUST: Redirect unauthenticated users on the server (302 before any HTML ships), not from a client-side effect — client redirects flash the protected page and jank the URL.

Authentication redirects should happen on the server to avoid janky client-side URL changes

Client-side auth checks load the protected page first, then redirect to login after JavaScript executes — causing a visible flash of the wrong page and a jarring URL change. Server-side redirects (302) send users directly to the login page before any content loads.

## Bad — do not do this

`design-server-auth-redirect-bad`

```tsx
export function ServerAuthRedirectBad() {
  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4 space-y-3">
        <div className="space-y-2">
          <div className="flex items-center gap-2">
            <span className="text-xs bg-muted px-2 py-0.5 rounded text-muted-foreground font-mono">1</span>
            <span className="text-sm text-foreground">Browser loads /dashboard</span>
          </div>
          <div className="flex items-center gap-2">
            <span className="text-xs bg-muted px-2 py-0.5 rounded text-muted-foreground font-mono">2</span>
            <span className="text-sm text-foreground">JS bundle downloads & parses</span>
          </div>
          <div className="flex items-center gap-2">
            <span className="text-xs bg-muted px-2 py-0.5 rounded text-muted-foreground font-mono">3</span>
            <span className="text-sm text-foreground">React hydrates, checks auth</span>
          </div>
          <div className="flex items-center gap-2">
            <span className="text-xs bg-destructive/20 px-2 py-0.5 rounded text-destructive font-mono">4</span>
            <span className="text-sm text-destructive">Client redirect → /login (flash!)</span>
          </div>
        </div>
        <div className="bg-muted rounded p-2 text-xs text-foreground font-mono">/dashboard → flash → /login</div>
      </div>
      <p className="text-xs text-error">Client-side auth redirect — URL flash and layout shift</p>
    </div>
  );
}
```

## Good — do this

`design-server-auth-redirect-good`

```tsx
export function ServerAuthRedirectGood() {
  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4 space-y-3">
        <div className="space-y-2">
          <div className="flex items-center gap-2">
            <span className="text-xs bg-muted px-2 py-0.5 rounded text-muted-foreground font-mono">1</span>
            <span className="text-sm text-foreground">Browser requests /dashboard</span>
          </div>
          <div className="flex items-center gap-2">
            <span className="text-xs bg-primary/20 px-2 py-0.5 rounded text-primary font-mono">2</span>
            <span className="text-sm text-primary">Server checks auth → 302 /login</span>
          </div>
          <div className="flex items-center gap-2">
            <span className="text-xs bg-muted px-2 py-0.5 rounded text-muted-foreground font-mono">3</span>
            <span className="text-sm text-foreground">Browser loads /login directly</span>
          </div>
        </div>
        <div className="bg-muted rounded p-2 text-xs text-foreground font-mono">/dashboard → 302 → /login (seamless)</div>
      </div>
      <p className="text-xs text-success">Server-side redirect — no flash, clean URL transition</p>
    </div>
  );
}
```

## References

- [interfaces.rauno.me](https://interfaces.rauno.me/)
