2
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?

GETだけ定義したNext.jsのRoute HandlerにHEADを送ると何が起きるか

2
Posted at

結論

  • Next.js(App Router)は、GETを定義するだけでHEADメソッドを自動実装します
  • ただし公式ドキュメントにはこの挙動について記載がありませんでした(OPTIONSの自動実装のみ明記)
  • ソースコード確認と実機検証(Next.js v16.2.6)で動作を確認しました

きっかけ

ネットワークはなぜつながるのか 第2版 を読んでいてHTTPメソッドの整理をしていたとき、Next.jsのRoute HandlerでHEADを定義しなかった場合にどうなるのかが気になりました。

Claude Desktopに聞いたところ「HEADは自動実装されない」と回答されました。公式ドキュメントにも記載がなかったため、本当にそうなのかソースコードと実機で検証しました。

HTTPに詳しい方には当たり前の内容かもしれませんが、自分はまだ学び始めたばかりで気になったので調べてみた記録です。

公式ドキュメントの記載

Next.js公式ドキュメント(v16.2.6時点)のRoute Handlersのページには、以下のようにOPTIONSの自動実装のみが明記されています。

// If `OPTIONS` is not defined, Next.js will automatically implement `OPTIONS`
// and set the appropriate Response `Allow` header depending on the other
// methods defined in the Route Handler.
export async function OPTIONS(request: Request) {}

HEADについては、サポートされるメソッド一覧に export async function HEAD(request: Request) {} が並んでいるだけで、未定義時の挙動には触れられていません。

ソースコードの確認

Next.jsのソースコード auto-implement-methods.ts を確認しました。

const AUTOMATIC_ROUTE_METHODS = ['HEAD', 'OPTIONS'] as const

export function autoImplementMethods(
  handlers: AppRouteHandlers
): Record<HTTP_METHOD, AppRouteHandlerFn> {
  // ...省略...

  for (const method of missing) {
    if (method === 'HEAD') {
      if (handlers.GET) {
        methods.HEAD = handlers.GET   // GETハンドラをそのままHEADに代入
        implemented.add('HEAD')
      }
      continue
    }

    if (method === 'OPTIONS') {
      // ...OPTIONSの自動実装(省略)...
    }
  }

  return methods
}

ポイントは以下の3点です。

  • AUTOMATIC_ROUTE_METHODSHEADOPTIONS の両方が含まれています
  • HEADが未定義かつGETが定義されている場合、methods.HEAD = handlers.GET でGETのハンドラがそのまま代入されます
  • ボディの除去はNext.jsの責務ではなく、HTTPプロトコル層(Node.jsのhttp実装)が行います

実機検証

テストコード

GETのみを定義したRoute Handlerを用意しました。

// app/api/test/route.ts
export async function GET() {
  return Response.json(
    { message: "Hello from GET", timestamp: Date.now() },
    {
      headers: {
        "X-Custom-Header": "test-value",
        "Last-Modified": new Date().toUTCString(),
      },
    }
  );
}

検証結果(Next.js 16.2.6)

# GET — 通常通り200 + ボディ
$ curl -D- http://localhost:3999/api/test
HTTP/1.1 200 OK
content-type: application/json
last-modified: Sat, 23 May 2026 12:08:39 GMT
x-custom-header: test-value

{"message":"Hello from GET","timestamp":1779538119046}

# HEAD — 200 + ヘッダーのみ(ボディなし)
$ curl -D- -X HEAD http://localhost:3999/api/test
HTTP/1.1 200 OK
content-type: application/json
last-modified: Sat, 23 May 2026 12:08:39 GMT
x-custom-header: test-value

# POST — 405(未定義のメソッド)
$ curl -D- -X POST http://localhost:3999/api/test
HTTP/1.1 405 Method Not Allowed

# OPTIONS — 204 + Allowヘッダー(自動実装)
$ curl -D- -X OPTIONS http://localhost:3999/api/test
HTTP/1.1 204 No Content
allow: GET, HEAD, OPTIONS
メソッド ステータス ボディ カスタムヘッダー
GET 200 あり(JSON) あり
HEAD 200 なし(自動除去) あり(GETと同じ)
POST 405 なし
OPTIONS 204 なし Allow: GET, HEAD, OPTIONS

なぜボディが消えるのか

methods.HEAD = handlers.GET なので、HEADリクエストでもGETと同じ処理が実行され、Response.json(...) でボディ付きのレスポンスが生成されます。

HEADリクエストに対するレスポンスにボディを含めてはならないため、Node.jsのHTTPサーバー実装がこの仕様に従い、HEADレスポンスからボディを自動的に除去します。

具体的には、Node.jsの _http_server.jsServerResponse の生成時にHEADかどうかを判定しています。

// node/lib/_http_server.js L203
function ServerResponse(req, options) {
  OutgoingMessage.call(this, options);

  if (req.method === 'HEAD') this._hasBody = false;
  // ...
}

そして _http_outgoing.jswrite()_hasBodyfalse の場合、ボディの書き込みを無視します。

// node/lib/_http_outgoing.js L942
if (!msg._hasBody) {
    if (msg[kRejectNonStandardBodyWrites]) {
      throw new ERR_HTTP_BODY_NOT_ALLOWED();
    } else {
      debug('This type of response MUST NOT have a body. ' +
        'Ignoring write() calls.');
      process.nextTick(callback);
      return true;
    }
  }

つまり、Next.jsはGETのハンドラをそのまま流用するだけで、ボディ除去はフレームワークではなくNode.jsのHTTP層が _hasBody フラグで制御しています。

Honoとの比較

Honoは公式ドキュメントでHEADの自動処理を明記しています。

フレームワーク HEAD自動実装 ドキュメント記載
Next.js(App Router) する(GETから自動実装) なし
Hono する(GETのボディなし版として処理) あり

まとめ

  • Next.jsはHEADをGETから自動実装します。明示的に定義しなくても動きます
  • 実装の根拠は auto-implement-methods.tsmethods.HEAD = handlers.GET です
  • ボディ除去はHTTPプロトコル層(Node.js)の責務であり、Next.jsは関与していません
  • 細かい部分ですが確かめてみると非常に勉強になりました

参考

2
0
1

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
2
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?