@hono/node-server/vercel: POST requests hang (no response) on Vercel Node runtime while GET works · Issue #306 · honojs/node-server · GitHub
Skip to content

@hono/node-server/vercel: POST requests hang (no response) on Vercel Node runtime while GET works #306

Open

Description

@leohahn

What version of Hono?

  • hono@4.11.9
  • @hono/node-server@1.19.9

What version of Node.js?

  • Vercel function runtime nodejs24.x

What happened?

Using the official Vercel adapter from @hono/node-server/vercel:

import { handle } from '@hono/node-server/vercel'
import app from '../apps/api/src/server.js'

export default handle(app)

in a Vercel function (api/server.ts) with rewrites (/api/* and /rpc/* -> /api/server), GET requests work, but POST requests never return a response (client times out).

Repro matrix from Preview deployment:

Case Method Path Result
health GET /api/health (separate function) 200
slack-url-verification POST /api/slack timeout (curl exit 28 after 25s)
slack-interactivity-invalid-signature POST /api/slack/interactivity timeout (curl exit 28 after 25s)
rpc-ping-no-csrf POST /rpc/ping timeout (curl exit 28 after 25s)
rpc-ping-csrf-no-auth POST /rpc/ping timeout (curl exit 28 after 25s)

When we switch back to a custom Node IncomingMessage/ServerResponse bridge that buffers the request body and calls app.fetch(request), the same POST routes return immediately with expected app-level responses.

Minimal reproduction steps

  1. Deploy a Vite + Hono app on Vercel with:
    • API function at api/server.ts
    • rewrites /api/:path((?!health$|server$).*) -> /api/server and /rpc/(.*) -> /api/server
  2. Use @hono/node-server/vercel handle(app) as above
  3. Call POST route (JSON or form-encoded) via Preview URL
  4. Observe no response body/status until client timeout

Expected

POST routes should behave like GET routes and return app responses.

Actual

POST routes hang indefinitely on Vercel Node runtime.

Additional context

  • Using hono/vercel in this same Node function setup also hangs, but that appears to be a signature mismatch (Request handler in Node req/res runtime).
  • The issue here is specifically with the Node-targeted Vercel adapter from @hono/node-server/vercel.

Activity

  1. venarius commented on Apr 10, 2026

    @venarius

    +1 Same Issue here

    My solution:

    // @ts-nocheck — pre-built backend bundle has no type declarations
    import type { IncomingMessage, ServerResponse } from 'http';
    
    import app from '../apps/backend/dist/app.js';
    
    export default async function handler(req: IncomingMessage, res: ServerResponse) {
      const proto = req.headers['x-forwarded-proto'] || 'https';
      const host = req.headers['x-forwarded-host'] || req.headers.host;
      const url = new URL(req.url!, `${proto}://${host}`);
    
      const headers = new Headers();
      for (const [key, value] of Object.entries(req.headers)) {
        if (value) headers.set(key, Array.isArray(value) ? value.join(', ') : value);
      }
    
      const init: RequestInit = { method: req.method, headers };
    
      if (req.method !== 'GET' && req.method !== 'HEAD') {
        init.body = await readBody(req);
      }
    
      const request = new Request(url.toString(), init);
      const response = await app.fetch(request);
    
      const respHeaders: Record<string, string[] | string> = {};
      response.headers.forEach((value, key) => {
        const existing = respHeaders[key];
        if (existing) {
          respHeaders[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
        } else {
          respHeaders[key] = value;
        }
      });
    
      res.writeHead(response.status, respHeaders);
    
      if (response.body) {
        const reader = response.body.getReader();
        let chunk = await reader.read();
        while (!chunk.done) {
          res.write(chunk.value);
          chunk = await reader.read();
        }
      }
    
      res.end();
    }
    
    function readBody(req: IncomingMessage): Promise<Buffer> {
      return new Promise((resolve, reject) => {
        const chunks: Buffer[] = [];
        req.on('data', (chunk: Buffer) => chunks.push(chunk));
        req.on('end', () => resolve(Buffer.concat(chunks)));
        req.on('error', reject);
      });
    }
    
    export const config = {
      api: {
        bodyParser: false,
      },
    };
  2. yusukebe commented on Apr 12, 2026

    @yusukebe
    Member

    Hi @leohahn @venarius

    Honestly, I'm not familiar with Vercel recently, but is there any case to use @hono/node-server/vercel when Vercel already supports Hono apps natively? https://vercel.com/docs/frameworks/backend/hono

  3. abhishiv commented on Jun 25, 2026

    @abhishiv

    Honestly, I'm not familiar with Vercel recently, but is there any case to use @hono/node-server/vercel when Vercel already supports Hono apps natively? https://vercel.com/docs/frameworks/backend/hono

    Because otherwise you get following error

    ok now i get different error
    onError TypeError: c2.req.raw.headers.get is not a function
        at getSignedCookie (file:///var/task/index.js:255800:37)
        at getUserCookieV2 (file:///var/task/index.js:255897:24)
        at Object.handler (file:///var/task/index.js:285028:26)
    
  4. satyam-bhosale commented on Jun 25, 2026

    @satyam-bhosale

    I'm currently working with the same setup, and you don't have to use the node-server from Vercel, just do the export default app, it'll run on node js runtime only. Just add the following env variables: NODEJS_HELPERS=0 and VERCEL_EXPERIMENTAL_BACKENDS=1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions