0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.js + Railway + Auth.jsで自社開発サービスを作る際にハマったポイントまとめ

0
Last updated at Posted at 2026-02-17

はじめに

OAuth認証で全リンクの本人確認を行うプロフィールリンクサービス「myna.me」を自社開発しました。Next.js 16 (App Router) + Auth.js v5 + Drizzle ORM + PostgreSQLの構成で、Railwayにデプロイしています。

本記事では、開発中にハマったポイントとその解決策を共有します。

技術構成

カテゴリ 技術
フレームワーク Next.js 16 (App Router)
言語 TypeScript
ORM Drizzle ORM
DB PostgreSQL
認証 Auth.js v5 (NextAuth)
スタイリング Tailwind CSS v4
ホスティング Railway

1. RailwayでSet-Cookieヘッダーが消える問題

症状

Auth.jsのマジックリンク認証で、ユーザーがメールのリンクをクリックした後、セッションcookieがセットされずにログインできない。

原因

Railwayのedge proxyが、307リダイレクトレスポンスからSet-Cookieヘッダーを剥がす場合があります。NextResponse.redirect() はデフォルトで307を返すため、cookieをセットしてからリダイレクトする一般的なパターンが動作しません。

解決策

HTMLレスポンス(ステータス200)でSet-Cookieヘッダーを返し、JavaScript + meta-refreshでリダイレクトするパターンに変更しました。

return new Response(
  `<html><head>
    <meta http-equiv="refresh" content="0;url=/dashboard">
  </head><body>
    <script>window.location.href='/dashboard'</script>
  </body></html>`,
  {
    status: 200,
    headers: {
      'Content-Type': 'text/html',
      'Set-Cookie': sessionCookie,
    },
  }
);

2. Railway上で req.url が内部URLを返す

症状

req.url を使ってリダイレクトURLを構築すると、http://0.0.0.0:8080/... のような内部URLになってしまう。

原因

Railway上ではNext.jsが内部ポートでリッスンしており、req.url はそのままの内部URLを返します。req.url.startsWith("https://") による本番判定も常に false になります。

解決策

リダイレクトURLには必ず環境変数 NEXT_PUBLIC_APP_URL を使用します。

// NG
const redirectUrl = new URL('/dashboard', req.url);

// OK
const redirectUrl = `${process.env.NEXT_PUBLIC_APP_URL}/dashboard`;

3. Auth.js v5のJWE構造の理解

症状

ミドルウェアでJWTトークンのバリデーションが失敗する。

原因

Auth.js v5はJWS(署名JWT)ではなく**JWE(暗号化JWT)**を使用します。JWEは5つのパーツ(header..iv.ciphertext.tag)で構成され、2番目のパーツ(encrypted key)が常に空文字列になります。

parts.some(p => p.length === 0) のようなバリデーションでは必ず失敗します。

解決策

JWEの検証には decode() from next-auth/jwt を使用し、鍵導出にはcookie名をsaltとして指定します。

import { decode } from 'next-auth/jwt';

const token = await decode({
  token: sessionToken,
  secret: process.env.AUTH_SECRET,
  salt: cookieName, // "__Secure-authjs.session-token"
});

4. Server Actionを使うページにはforce-dynamicが必須

症状

本番ビルド後にServer Actionを呼び出すと503エラーが返る。

原因

Next.jsの静的生成(マーク)されたページはPOSTリクエストを処理できません。Server Actionは内部的にPOSTを使うため、静的ページでは動作しません。

解決策

Server Actionを使用するページには force-dynamic を追加します。

// app/dashboard/page.tsx
export const dynamic = "force-dynamic";

5. DBマイグレーションはビルド時に実行できない

症状

Railway上でビルドコマンドにDBマイグレーションを含めると、データベースに接続できずビルドが失敗する。

原因

Railwayのビルドフェーズでは内部ネットワーク(例: postgres.railway.internal)にアクセスできません。

解決策

マイグレーションはビルドコマンドではなく、Pre-deployコマンドで実行します。

# Railway設定
Build Command: npm run build
Pre-deploy Command: npx drizzle-kit migrate

まとめ

Railway + Next.js + Auth.jsの組み合わせは強力ですが、各技術のエッジケースにハマりやすいです。特にRailwayのedge proxyの挙動は公式ドキュメントにも明記されていない部分があるため、Set-Cookieが消える問題には注意が必要です。

この記事が同じ構成で開発している方の参考になれば幸いです。

myna.meは現在公開中です:https://myna.me

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?