ファランクスブログ

© 2026 all rights reserved.
  1. blog
  2. next-api-route-protection
  • CORS
  • Access-Control-Allow-Origin
  • 認証ヘッダー
  • RateLimit

Nextjs:APIルートの保護 / CORSについて

2026年4月19日

CORS

  • CORS(Cross-Origin Resource Sharing)は「保護」というよりも「許可」の為のブラウザの仕組み

  • CORSはブラウザの機能なのでサーバー側からのリクエストには効果が無い。

  • ブラウザ(クライアント側)はセキュリティ上の理由から異なるオリジン(他のサイト)へのリクエスト自体は許可するが、レスポンスの読み取りはCORSにより制限されている。

  • APIをfetchさせたい側はHTTPヘッダーのCORS設定で任意のドメインを許可することで、そのドメインがクライアントサイドからfetchできるようにさせる。

  • サブドメインはcross-origin に該当するので、APIを提供する場合はCORSによる許可が必要

サーバー側から異なるオリジンへのfetch:

  const res = await fetch("https://xxx.com/api/test");
  const data = await res.json();
  console.log(data, "data");
  • ターミナルにデータが表示される。

クライアント側から異なるオリジンへのfetch:

  useEffect(() => {
    fetch("https://xxx.com/api/test")
      .then((res) => res.json())
      .then((data) => {
        console.log(data, "data");
      });
  }, []);

has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

  • ブラウザの検証ツールにCORSでブロックされた旨の表示が出る。

Access-Control-Allow-Origin

next.config.ts :

const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        source: "/api/:path*",
        headers: [
          {
            key: "Access-Control-Allow-Origin",
            value: 許可するオリジン,
          },
        ],
      },
    ];
  },
};

export default nextConfig;
  • middeware(proxy.ts)やAPI(route.ts)のレスポンスヘッダーでも設定できる。

// API

    return new Response(JSON.stringify(data), {
      status: 200,
      headers: {
        "Access-Control-Allow-Origin": allowedOrigin,
        "Access-Control-Allow-Methods": "GET",
      },
    });

認証ヘッダー

Authorization: Bearer:

  • Authorization: <type> <credentials> という形式。

  • HTTP標準のスキーム

api-key:

  • x-api-key: <credentials>

  • カスタムヘッダー、任意の名前でいい

使い方:

  • 環境変数(.env)にユニークなキー(credentials)を設定し、APIへのrequest時にheaderにそのキーを含める。

  • API側はキーが合っているか照合し処理の分岐を行う。

例:

.env:

PROTECTION_TEST_KEY="test1234"

page.tsx:

export const Page = async () => {
    const response = await fetch("/api/protect", {
      headers: {
        Authorization: `Bearer ${process.env.PROTECTION_TEST_KEY}`,
      },
    });

    if (!response.ok) {
      throw new Error("Unauthorized or error");
    }

    const data = await response.json();
    console.log(data);

route.ts:

const AUTH_SCHEME = "Bearer";

export async function GET(req: NextRequest) {
  const authorization = req.headers.get("authorization");
  if (!authorization) {
    return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
  }

  const [scheme, token] = authorization.split(" ");

  if (scheme !== AUTH_SCHEME || !token) {
    return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
  }

  if (token !== process.env.PROTECTION_TEST_KEY) {
    return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
  }

  // DBや認証が関わる処理
}

- ここまで行うとサーバー側( Postmanやcurl )からAPIにアクセスすると Unauthorized が返ってくる。つまりサーバー側も保護出来ている。

api-keyのようなカスタムヘッダーの場合もほとんど同じやり方

// route.ts
export async function GET(req: NextRequest) {
  if (req.headers.get("api-key") !== process.env.PROTECTION_TEST_KEY) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  return NextResponse.json({ data: "protectData" });
}

RateLimit

RateLimitも認証ヘッダーと同じでAPI側に保護ロジックを書くのでサーバー側からも保護できる。

import { headers } from "next/headers";
import { RateLimiterMemory } from "rate-limiter-flexible";

export const getUserIp = async () => {
  const headersList = await headers();
  const ip =
    headersList.get("x-forwarded-for")?.split(",")[0].trim() ||
    headersList.get("x-real-ip") ||
    headersList.get("cf-connecting-ip") ||
    "unknown";
  return ip;
};

export const rateLimiter = new RateLimiterMemory({
  points: 5,
  duration: 60, // 5req/min
});

route.ts:

export async function GET(req: NextRequest) {
  const userIp = await getUserIp();

  try {
    await rateLimiter.consume(`routeA:${userIp}`, 1);

    return NextResponse.json({
      message: "Success",
    });

  } catch {
    return NextResponse.json(
      { error: "Too Many Requests" },
      { status: 429 }
    );
  }
}
  • rate-limiter-flexible というライブラリを使った例

    • https://github.com/animir/node-rate-limiter-flexible

  • *注意点:consume()に渡すキーをroute毎にユニークにしないとlimitの値が共有される。

  • 基本的には(upstash)Redisを使った方が良い

nextjs
/
security
  • CORS
  • Access-Control-Allow-Origin
  • 認証ヘッダー
  • RateLimit