[@clerk/nextjs v7]: auth.protect() in Next.js 16 proxy redirects to current URL instead of sign-in page
メンテナーはふだん 1 日以内に返信
@jacekradko がすでに取り組んでいます。
2026年5月13日 から。
評価
この issue はまだ評価されていません。
説明
Description
When using auth.protect() inside clerkMiddleware in a Next.js 16 app (proxy/Node.js runtime), unauthenticated users are redirected back to the current page URL instead of the sign-in page. Route protection is effectively bypassed — unauthenticated users can access protected pages.
Root Cause
Clerk resolves the sign-in redirect URL via NEXT_PUBLIC_CLERK_SIGN_IN_URL through process.env in two places:
clerkMiddleware.js:
const SIGN_IN_URL = process.env.NEXT_PUBLIC_CLERK_SIGN_IN_URL || "";
const signInUrl = resolvedParams.signInUrl || SIGN_IN_URL;
utils.js (handleMultiDomainAndProxy):
const signInUrl = (opts?.signInUrl) || SIGN_IN_URL;
return { proxyUrl, isSatellite, domain, signInUrl };
This signInUrl is spread over resolvedOptions in createAuthenticateRequestOptions, so requestState.signInUrl ends up as "" when the env var is unavailable.
In handleControlFlowErrors, when auth.protect() throws a sign-in redirect error, Clerk calls:
createRedirect({ signInUrl: requestState.signInUrl, baseUrl: clerkRequest.clerkUrl, ... })
.redirectToSignIn({ returnBackUrl })
With signInUrl = "", new URL("", "https://example.com/dashboard") resolves to https://example.com/dashboard — the current page — and the user is never redirected to sign-in.
Why NEXT_PUBLIC_CLERK_SIGN_IN_URL is unavailable in the proxy
In Next.js 16, the proxy runs in the Node.js runtime (previously Edge in Next.js ≤15). NEXT_PUBLIC_* variables are inlined at build time into browser bundles, but in the Node.js proxy context they must be available via process.env at runtime. This works in standalone Next.js apps where next dev runs from the project root and loads .env files directly. In monorepo setups (e.g. pnpm workspaces + Turbo), process.env.NEXT_PUBLIC_CLERK_SIGN_IN_URL is not reliably populated in the proxy runtime, so SIGN_IN_URL falls back to "".
This was confirmed by testing with @clerk/nextjs 7.0.8 and 7.0.12 — both versions exhibit the same behavior. It is NOT a version regression.
Environment
@clerk/nextjs: 7.0.8, 7.0.12 (both affected)next: 16.2.2 (proxy/Node.js runtime —proxy.tsreplacesmiddleware.ts)- pnpm workspaces + Turbo monorepo
Steps to Reproduce
- Create a Next.js 16 app inside a pnpm workspace monorepo
- Add
proxy.tswith standard route protection:
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
const isPublicRoute = createRouteMatcher(["/sign-in(.*)", "/sign-up(.*)"]);
export default clerkMiddleware((auth, request) => {
if (!isPublicRoute(request)) {
auth.protect();
}
});
- Set
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-inin.env - Run via Turbo (
turbo run dev) - Open an incognito window and navigate to a protected route
Expected Behavior
Unauthenticated users are redirected to /sign-in.
Actual Behavior
Error: NEXT_REDIRECT
digest: 'NEXT_REDIRECT;replace;https://example.com/dashboard;307;',
clerk_digest: 'CLERK_PROTECT_REDIRECT_TO_SIGN_IN',
returnBackUrl: 'https://example.com/dashboard'
The page renders normally — protected content visible to unauthenticated users.
Workaround
Bypass auth.protect() and redirect manually using NextResponse.redirect():
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
const isPublicRoute = createRouteMatcher(["/sign-in(.*)", "/sign-up(.*)"]);
export default clerkMiddleware(async (auth, request) => {
if (!isPublicRoute(request)) {
const { userId } = await auth();
if (!userId) {
return NextResponse.redirect(new URL("/sign-in", request.url));
}
}
});
- 主要言語
- TypeScript
- スター
- 1.8k
- フォーク
- 473
- 平均マージ
- 2日 13時間
- マージ済み PR(30日)
- 221
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
clerk/javascript のほかの issue
-
難易度 4/5 3〜5日 初心者へのやさしさ 64/100
clerk/javascript#9775 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
clerk/javascript#9770 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 68/100
clerk/javascript#9667 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
needs-triage
難易度 3/5 1〜2日 初心者へのやさしさ 58/100
clerk/javascript#9478 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 56/100
clerk/javascript#9476 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
clerk/javascript の issue をすべて見る
似ている issue
-
triage
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
mermaid-js/mermaid-live-editor#2053 ·
メンテナーはふだん 1 日以内に返信
-
factory
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
jessepollak/home#1455 ·
メンテナーはふだん 1 日以内に返信
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
難易度 1/5 1時間未満 初心者へのやさしさ 95/100
lingdojo/kana-dojo#31227 · コメント 1 件 · リアクション 5 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
appandflow/stim#1838 ·
メンテナーはふだん 1 日以内に返信