動作に支障ない401エラーと向き合ったらNext.jsの仕様にたどり着いた話
投稿日: 2025年11月01日
先日、仕事でずっと気になっていたけど無視しても動作には支障ないからスルーしてきたことと向き合う時が来ました。
デバッグを進めていると「うわぁ〜〜確かに!!」ってなる学びがいくつかあったので共有します。
ログイン、ログアウトまわりで401が頻発していました。
コンソールに以下のようなエラーメッセージが出ている状態です。
GET /api/users/me 401 (Unauthorized)動作自体には支障ない(リロードすれば正常)ものの、リリース前に「警告が出ている状態は避けたい」となり、原因調査と修正を担当することになりました。
結論、バグというよりNext.jsの仕組みによる副作用でした。
ログアウト後に401
ログイン直後に401
ログイン画面でユーザー情報取得APIが呼ばれる
これはserver actionsの仕様の問題でした。
Server Actions内でcookies().delete()を実行すると、Next.jsが自動的に再レンダリングをトリガーします。
一旦どこで401返ってくるようなリクエストしているのか特定すべく、必殺console出力していると、どうやらcookie削除してサインインページへ遷移させた後に、元いたページでユーザー情報の取得APIのリクエストコールされていることが発覚しました。
なんで!!!そのページにいないのに(多分middlewareの影響)!!!と思いつつ、「Next.js server actions 再レンダリング」と検索してみました。
すると、server actionsのレスポンスに再レンダリングするものが含まれているという情報を得ました。。。
「なんだって、、そういうことならログアウトする処理をroute handlerにしたら良さそう。」ということで移行してみたら見事、401エラー消えました!!
下記の記事に行き着いて救われましたありがとうございます!
これは典型的なSSRとCSRのズレ問題でした。
基本データフェッチはSSRで行い、その情報をpropsでクライアントサイド渡してuseSWRのfallbackに渡し描画するというルールなのですが、cookieに認証情報をセットされる前にSSRでデータフェッチされていたので401エラー出て、それが描画されていました。
なのでリロードするとエラー出ないという現象が起きていました。
これは凡ミスみたいな感じでしたが、初期値(fallback)に渡して、swrから取得したデータを表示したら解決です!
type Props = {
data: Response;
}
// 401エラー
const ChildCompnent: React.FC<Props> = ({data}) => {
useMe(data);
return (
{/* cookie設定前の認証ないデータ */}
<div>{data}</div>
);
}
// OK!
const ChildCompnent: React.FC<Props> = ({data}) => {
const { data: freshData } = useMe(data);
return (
<div>{freshData}</div>
);
}
// useMe は /api/users/me を叩いてユーザー情報を取得するSWRフックです。SSRのデータをそのまま表示しているとリロードしないと新鮮なデータにならないのがポイントですね!
他のページでも別のブラウザの更新情報をリロードしないと表示できないというバグあり全く同じパターンでした、、!
これは普通に条件分岐不足でした。
問題はヘッダーコンポーネントだったのですが、pathnameに応じて表示する項目が違うのに、ログインしていなくてもユーザー情報取得APIリクエストしていました・・・
なので条件付きフェッチで解決です。
SWRの仕様で「URLがnullならリクエストをスキップ」できるので、それを活用しました。pathnameに応じてnullを渡すようにしました。
これで401出すことなく安全に認証必要なページも不要なページもエラーなく表示できるようになりました。
今回の401は「エラーのようでエラーじゃない」類のものでした。
Next.js のApp RouterではServer Actions・SSR・Cookieの仕組みが密接に絡むため、
こうした副作用を一度理解しておくと、今後の実装判断がすごく楽になりそうだと思いました!
そしてやはりまず何が起きているかの理解にconsole出力は欠かせないなと思いました!
今回のような思わぬ順序でレンダリングされていることもありますのでw