[GH-ISSUE #6530] Bun + Hono + Prisma backend auth.api.getSession() returns null on nextjs frontend #10543

Closed
opened 2026-04-13 06:45:27 -05:00 by GiteaMirror · 8 comments
Owner

Originally created by @vCiKv on GitHub (Dec 4, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6530

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

going to route api/auth/get-session always returns null

server(bun+hono) setup runs on localhost:4000

type HonoEnv = {
  Variables: {
    user: typeof auth.$Infer.Session.user | null;
    session: typeof auth.$Infer.Session.session | null;
  };
};
export const ALLOWED_ORIGINS = [
  'http://localhost:3000',
];
const app = new Hono<HonoEnv>()
app.use("*", cors({
  origin: ALLOWED_ORIGINS,
  allowMethods: ["POST", "GET", "PUT", "DELETE", "OPTIONS"],
  allowHeaders: ["Content-Type", "Authorization"],
  exposeHeaders: ["Content-Length"],
  maxAge: 600,
  credentials: true,
}))
app.use("*", async (c, next) => {
  console.log('Request headers:', c.req.raw.headers)
  console.log('Cookie header:', c.req.header('cookie'))
  const session = await auth.api.getSession({ headers: c.req.raw.headers });
  console.log('Retrieved session:', session)
  if (!session) {
    c.set("user", null);
    c.set("session", null);
    await next();
    return;
  }

  c.set("user", session.user);
  c.set("session", session.session);
  await next();
});
app.on(["POST", "GET"], "api/**", (c) => auth.handler(c.req.raw))
app.route("api/auth", routeAuth)

export default {
  port,
  fetch: app.fetch,
} 

routeAuth

const routeAuth = new Hono<HonoEnv>()
routeAuth.get("/get-session", async (c) => {
  // const session = c.get("session")
  // const user = c.get("user")
  const session = await auth.api.getSession({ headers: c.req.raw.headers });
  const user = session?.user
  if (!user) return c.body(null, 401);

  return c.json({
    session: session.session,
    user
  });
});

clientBetterAuth on Nextjs(localhost:3000)

import { createAuthClient } from "better-auth/react"
export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_API_URL, //same as localhost:4000
  fetchOptions: {
    credentials: "include",
  }
})

Current vs. Expected behavior

on successful sign-in route localhost:4000/api/auth/get-session should return a session instead i always get null unauthorized
the database is setup and it works that includes sign-in and sign-up routes

running

fronend
nextjs : 16.0.3
react: 19.2.0,
better-auth: 1.4.5,

backend
hono: 4.10.7,
@prisma/client: 7.0.1,
better-auth: 1.4.5,
bun: 1.2.23

What version of Better Auth are you using?

1.4.5

System info

see additional context

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

const auth = betterAuth({
  database: prismaAdapter(db, {
    provider: "postgresql",
  }),
  baseURL: process.env.CLIENT_URL, //same as localhost:3000
  trustedOrigins: [process.env.CLIENT_URL],

  emailAndPassword: {
    enabled: true,
  },
  advanced: {
    crossSubDomainCookies: {
      enabled: true
    },
    defaultCookieAttributes: {
      sameSite: "none",
      secure: true,
      httpOnly: true,
      // httpOnly: true,
      // path: "/",
      // secure: process.env.NODE_ENV === "production",
      // sameSite: process.env.NODE_ENV === "production" ? "none" : "lax",
      // partitioned: true
    }
  }
});

Additional context

i had seen similar issues here but none of the solutions worked for me, i would really appreciate some help or insight into why it does not work.
i want to use nextjs client only none to minial
i used some jwt tokens before setting up better auth and those worked after cors and cookie setup.
all the comments are things i tried that did not work.
i could not get the cli to work no matter what i did hopeful that has no effect.
thank you in advance

Originally created by @vCiKv on GitHub (Dec 4, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6530 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce going to route api/auth/get-session always returns null server(bun+hono) setup runs on localhost:4000 ```ts type HonoEnv = { Variables: { user: typeof auth.$Infer.Session.user | null; session: typeof auth.$Infer.Session.session | null; }; }; export const ALLOWED_ORIGINS = [ 'http://localhost:3000', ]; const app = new Hono<HonoEnv>() app.use("*", cors({ origin: ALLOWED_ORIGINS, allowMethods: ["POST", "GET", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["Content-Type", "Authorization"], exposeHeaders: ["Content-Length"], maxAge: 600, credentials: true, })) app.use("*", async (c, next) => { console.log('Request headers:', c.req.raw.headers) console.log('Cookie header:', c.req.header('cookie')) const session = await auth.api.getSession({ headers: c.req.raw.headers }); console.log('Retrieved session:', session) if (!session) { c.set("user", null); c.set("session", null); await next(); return; } c.set("user", session.user); c.set("session", session.session); await next(); }); app.on(["POST", "GET"], "api/**", (c) => auth.handler(c.req.raw)) app.route("api/auth", routeAuth) export default { port, fetch: app.fetch, } ``` routeAuth ```ts const routeAuth = new Hono<HonoEnv>() routeAuth.get("/get-session", async (c) => { // const session = c.get("session") // const user = c.get("user") const session = await auth.api.getSession({ headers: c.req.raw.headers }); const user = session?.user if (!user) return c.body(null, 401); return c.json({ session: session.session, user }); }); ``` clientBetterAuth on Nextjs(localhost:3000) ```ts import { createAuthClient } from "better-auth/react" export const authClient = createAuthClient({ baseURL: process.env.NEXT_PUBLIC_API_URL, //same as localhost:4000 fetchOptions: { credentials: "include", } }) ``` ### Current vs. Expected behavior on successful sign-in route localhost:4000/api/auth/get-session should return a session instead i always get null unauthorized the database is setup and it works that includes sign-in and sign-up routes running fronend nextjs : 16.0.3 react: 19.2.0, better-auth: 1.4.5, backend hono: 4.10.7, @prisma/client: 7.0.1, better-auth: 1.4.5, bun: 1.2.23 ### What version of Better Auth are you using? 1.4.5 ### System info ```bash see additional context ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript const auth = betterAuth({ database: prismaAdapter(db, { provider: "postgresql", }), baseURL: process.env.CLIENT_URL, //same as localhost:3000 trustedOrigins: [process.env.CLIENT_URL], emailAndPassword: { enabled: true, }, advanced: { crossSubDomainCookies: { enabled: true }, defaultCookieAttributes: { sameSite: "none", secure: true, httpOnly: true, // httpOnly: true, // path: "/", // secure: process.env.NODE_ENV === "production", // sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", // partitioned: true } } }); ``` ### Additional context i had seen similar issues here but none of the solutions worked for me, i would really appreciate some help or insight into why it does not work. i want to use nextjs client only none to minial i used some jwt tokens before setting up better auth and those worked after cors and cookie setup. all the comments are things i tried that did not work. i could not get the cli to work no matter what i did hopeful that has no effect. thank you in advance
GiteaMirror added the locked label 2026-04-13 06:45:27 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Dec 4, 2025):

Hi @vCiKv! I'm Dosu and I’m helping the better-auth team.

The most common reason auth.api.getSession() returns null in your setup is that the session cookie is not being sent or accepted due to cross-origin cookie and CORS restrictions, especially on localhost. Here’s what you should check and adjust:

  1. Cookie attributes for local development: With secure: true and sameSite: 'none', browsers will only send cookies over HTTPS. On localhost (HTTP), these cookies are blocked, so getSession() always returns null. For local dev, set secure: false and allowLocalhostUnsecure: true in your Better Auth config’s defaultCookieAttributes. Example:

    advanced: {
      defaultCookieAttributes: {
        sameSite: "none",
        secure: false,
        allowLocalhostUnsecure: true,
        httpOnly: true,
      }
    }
    

    Reference

  2. CORS headers: Your backend must send Access-Control-Allow-Origin (matching exactly the frontend origin, including protocol and port) and Access-Control-Allow-Credentials: true on every response, including OPTIONS preflight. If these are missing or incorrect, the browser will silently block cookies and session retrieval will fail. Reference

  3. crossSubDomainCookies config: For true cross-origin (localhost:3000 → localhost:4000), do not set a Domain attribute. If you enable crossSubDomainCookies, set the domain to your root domain (e.g., .example.com) only if you are sharing cookies across subdomains, not different ports. For localhost, you should disable crossSubDomainCookies or remove the domain. Reference

  4. Check the browser’s network tab: After login, inspect the Set-Cookie header and confirm the session cookie (usually better-auth.session_token or __Secure-better-auth.session_token) is present and sent on requests to your backend. If it’s missing, the browser is blocking it due to the reasons above. Reference

  5. Frontend fetch: Always use credentials: "include" in your fetch/auth client, which you are already doing.

  6. Trusted origins: Make sure every possible frontend origin (including protocol and port) is listed in trustedOrigins in your Better Auth config.

  7. Middleware conflicts: Some Hono middlewares (like hono/language with caching) can overwrite cookies after sign-in. If you use such middleware, try disabling its cache. Reference

  8. Session creation: If the session is created without a valid user id, Better Auth will erase the cookie immediately after login, causing getSession() to return null. Double-check your DB and logs if you suspect this. Reference

Summary: For local development, set secure: false and allowLocalhostUnsecure: true, disable crossSubDomainCookies, and ensure CORS headers are correct. Inspect the browser’s network tab to confirm cookies are set and sent. If you deploy to production, switch back to secure: true, use HTTPS, and set the domain only for subdomain sharing.

If you follow these steps and still get null, share the Set-Cookie header from your backend and the request/response headers from your browser’s network tab for more targeted help.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3613012730 --> @dosubot[bot] commented on GitHub (Dec 4, 2025): <!-- Greeting --> Hi @vCiKv! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The most common reason auth.api.getSession() returns null in your setup is that the session cookie is not being sent or accepted due to cross-origin cookie and CORS restrictions, especially on localhost. Here’s what you should check and adjust: 1. **Cookie attributes for local development:** With secure: true and sameSite: 'none', browsers will only send cookies over HTTPS. On localhost (HTTP), these cookies are blocked, so getSession() always returns null. For local dev, set `secure: false` and `allowLocalhostUnsecure: true` in your Better Auth config’s defaultCookieAttributes. Example: ```js advanced: { defaultCookieAttributes: { sameSite: "none", secure: false, allowLocalhostUnsecure: true, httpOnly: true, } } ``` [Reference](https://github.com/better-auth/better-auth/issues/4720) 2. **CORS headers:** Your backend must send `Access-Control-Allow-Origin` (matching exactly the frontend origin, including protocol and port) and `Access-Control-Allow-Credentials: true` on every response, including OPTIONS preflight. If these are missing or incorrect, the browser will silently block cookies and session retrieval will fail. [Reference](https://github.com/better-auth/better-auth/issues/4720) 3. **crossSubDomainCookies config:** For true cross-origin (localhost:3000 → localhost:4000), do not set a Domain attribute. If you enable crossSubDomainCookies, set the domain to your root domain (e.g., `.example.com`) only if you are sharing cookies across subdomains, not different ports. For localhost, you should disable crossSubDomainCookies or remove the domain. [Reference](https://github.com/better-auth/better-auth/issues/5611) 4. **Check the browser’s network tab:** After login, inspect the Set-Cookie header and confirm the session cookie (usually `better-auth.session_token` or `__Secure-better-auth.session_token`) is present and sent on requests to your backend. If it’s missing, the browser is blocking it due to the reasons above. [Reference](https://github.com/better-auth/better-auth/issues/4555) 5. **Frontend fetch:** Always use `credentials: "include"` in your fetch/auth client, which you are already doing. 6. **Trusted origins:** Make sure every possible frontend origin (including protocol and port) is listed in trustedOrigins in your Better Auth config. 7. **Middleware conflicts:** Some Hono middlewares (like hono/language with caching) can overwrite cookies after sign-in. If you use such middleware, try disabling its cache. [Reference](https://github.com/better-auth/better-auth/issues/2055) 8. **Session creation:** If the session is created without a valid user id, Better Auth will erase the cookie immediately after login, causing getSession() to return null. Double-check your DB and logs if you suspect this. [Reference](https://github.com/better-auth/better-auth/issues/5611) **Summary:** For local development, set `secure: false` and `allowLocalhostUnsecure: true`, disable crossSubDomainCookies, and ensure CORS headers are correct. Inspect the browser’s network tab to confirm cookies are set and sent. If you deploy to production, switch back to `secure: true`, use HTTPS, and set the domain only for subdomain sharing. If you follow these steps and still get null, share the Set-Cookie header from your backend and the request/response headers from your browser’s network tab for more targeted help. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/43380a23-e849-4c15-8ee6-10f78c75dda0?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/43380a23-e849-4c15-8ee6-10f78c75dda0?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/43380a23-e849-4c15-8ee6-10f78c75dda0?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/43380a23-e849-4c15-8ee6-10f78c75dda0?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/43380a23-e849-4c15-8ee6-10f78c75dda0?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/43380a23-e849-4c15-8ee6-10f78c75dda0?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/43380a23-e849-4c15-8ee6-10f78c75dda0?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=The%20most%20common%20reason%20auth.api.getSession%28%29%20returns%20null%20in%20your%20setup%20is%20that%20the%20session%20cookie%20is%20not%20being%20sent%20or%20accepted%20due%20to%20cross-origin%20cookie%20and%20CORS%20restrictions%2C%20especially%20on%20localhost.%20Here%E2%80%99s%20what%20you%20should%20check%20and%20adjust%3A%0A%0A1.%20%2A%2ACookie%20attributes%20for%20local%20development%3A%2A%2A%20With%20secure%3A%20true%20and%20sameSite%3A%20%27none%27%2C%20browsers%20will%20only%20send%20cookies%20over%20HTTPS.%20On%20localhost%20%28HTTP%29%2C%20these%20cookies%20are%20blocked%2C%20so%20getSession%28%29%20always%20returns%20null.%20For%20local%20dev%2C%20set%20%60secure%3A%20false%60%20and%20%60allowLocalhostUnsecure%3A%20true%60%20in%20your%20Better%20Auth%20config%E2%80%99s%20defaultCookieAttributes.%20Example%3A%0A%20%20%20%60%60%60js%0A%20%20%20advanced%3A%20%7B%0A%20%20%20%20%20defaultCookieAttributes%3A%20%7B%0A%20%20%20%20%20%20%20sameSite%3A%20%22none%22%2C%0A%20%20%20%20%20%20%20secure%3A%20false%2C%0A%20%20%20%20%20%20%20allowLocalhostUnsecure%3A%20true%2C%0A%20%20%20%20%20%20%20httpOnly%3A%20true%2C%0A%20%20%20%20%20%7D%0A%20%20%20%7D%0A%20%20%20%60%60%60%0A%20%20%20%5BReference%5D%28https%3A//github.com/better-auth/better-auth/issues/4720%29%0A%0A2.%20%2A%2ACORS%20headers%3A%2A%2A%20Your%20backend%20must%20send%20%60Access-Control-Allow-Origin%60%20%28matching%20exactly%20the%20frontend%20origin%2C%20including%20protocol%20and%20port%29%20and%20%60Access-Control-Allow-Credentials%3A%20true%60%20on%20every%20response%2C%20including%20OPTIONS%20preflight.%20If%20these%20are%20missing%20or%20incorrect%2C%20the%20browser%20will%20silently%20block%20cookies%20and%20session%20retrieval%20will%20fail.%20%5BReference%5D%28https%3A//github.com/better-auth/better-auth/issues/4720%29%0A%0A3.%20%2A%2AcrossSubDomainCookies%20config%3A%2A%2A%20For%20true%20cross-origin%20%28localhost%3A3000%20%E2%86%92%20localhost%3A4000%29%2C%20do%20not%20set%20a%20Domain%20attribute.%20If%20you%20enable%20crossSubDomainCookies%2C%20set%20the%20domain%20to%20your%20root%20domain%20%28e.g.%2C%20%60.example.com%60%29%20only%20if%20you%20are%20sharing%20cookies%20across%20subdomains%2C%20not%20different%20ports.%20For%20localhost%2C%20you%20should%20disable%20crossSubDomainCookies%20or%20remove%20the%20domain.%20%5BReference%5D%28https%3A//github.com/better-auth/better-auth/issues/5611%29%0A%0A4.%20%2A%2ACheck%20the%20browser%E2%80%99s%20network%20tab%3A%2A%2A%20After%20login%2C%20inspect%20the%20Set-Cookie%20header%20and%20confirm%20the%20session%20cookie%20%28usually%20%60better-auth.session_token%60%20or%20%60__Secure-better-auth.session_token%60%29%20is%20present%20and%20sent%20on%20requests%20to%20your%20backend.%20If%20it%E2%80%99s%20missing%2C%20the%20browser%20is%20blocking%20it%20due%20to%20the%20reasons%20above.%20%5BReference%5D%28https%3A//github.com/better-auth/better-auth/issues/4555%29%0A%0A5.%20%2A%2AFrontend%20fetch%3A%2A%2A%20Always%20use%20%60credentials%3A%20%22include%22%60%20in%20your%20fetch/auth%20client%2C%20which%20you%20are%20already%20doing.%0A%0A6.%20%2A%2ATrusted%20origins%3A%2A%2A%20Make%20sure%20every%20possible%20frontend%20origin%20%28including%20protocol%20and%20port%29%20is%20listed%20in%20trustedOrigins%20in%20your%20Better%20Auth%20config.%0A%0A7.%20%2A%2AMiddleware%20conflicts%3A%2A%2A%20Some%20Hono%20middlewares%20%28like%20hono/language%20with%20caching%29%20can%20overwrite%20cookies%20after%20sign-in.%20If%20you%20use%20such%20middleware%2C%20try%20disabling%20its%20cache.%20%5BReference%5D%28https%3A//github.com/better-auth/better-auth/issues/2055%29%0A%0A8.%20%2A%2ASession%20creation%3A%2A%2A%20If%20the%20session%20is%20created%20without%20a%20valid%20user%20id%2C%20Better%20Auth%20will%20erase%20the%20cookie%20immediately%20after%20login%2C%20causing%20getSession%28%29%20to%20return%20null.%20Double-check%20your%20DB%20and%20logs%20if%20you%20suspect%20this.%20%5BReference%5D%28https%3A//github.com/better-auth/better-auth/issues/5611%29%0A%0A%2A%2ASummary%3A%2A%2A%20For%20local%20development%2C%20set%20%60secure%3A%20false%60%20and%20%60allowLocalhostUnsecure%3A%20true%60%2C%20disable%20crossSubDomainCookies%2C%20and%20ensure%20CORS%20headers%20are%20correct.%20Inspect%20the%20browser%E2%80%99s%20network%20tab%20to%20confirm%20cookies%20are%20set%20and%20sent.%20If%20you%20deploy%20to%20production%2C%20switch%20back%20to%20%60secure%3A%20true%60%2C%20use%20HTTPS%2C%20and%20set%20the%20domain%20only%20for%20subdomain%20sharing.%0A%0AIf%20you%20follow%20these%20steps%20and%20still%20get%20null%2C%20share%20the%20Set-Cookie%20header%20from%20your%20backend%20and%20the%20request/response%20headers%20from%20your%20browser%E2%80%99s%20network%20tab%20for%20more%20targeted%20help.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/6530)
Author
Owner

@vCiKv commented on GitHub (Dec 5, 2025):

@dosu i implemented the steps above and the results did not change

relevant functions

const routeAuth = new Hono<HonoEnv>()

routeAuth.get("/get-session", async (c) => {
  // const session = c.get("session")
  // const user = c.get("user")
  const session = await auth.api.getSession({ headers: c.req.raw.headers });
  const user = session?.user
  if (!user) return c.body(null, 401);

  return c.json({
    session: session.session,
    user
  });
});

routeAuth.post("/signin", async (c) => {
  try {
    const { email, password } = await c.req.json() as { email: string; password: string; };
    if (!email || !password) {
      return formatResponseH(c, 400, "no credentials");
    }
    const signInRequest = await auth.api.signInEmail({
      body: {
        password,
        email,
      },
      headers: c.req.raw.headers,
    })
    const user = signInRequest.user
    if (!signInRequest) {
      return formatResponseH(c, 404, "create account")
    }
    if (user.isBlacklisted) {
      return formatResponseH(c, 401, "Your Account is baned please contact support");
    }
    return formatResponseH(c, 200, "ok")
  } catch (e) {
    console.error(e)
    return formatResponseH(c, 501, "error occurred")
  }
})

the servers response

Request headers: Headers {
  "host": "localhost:4000",
  "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
  "accept": "application/json, text/plain, */*",
  "accept-language": "en-US,en;q=0.5",
  "accept-encoding": "gzip, deflate, br, zstd",
  "origin": "http://localhost:3000",
  "dnt": "1",
  "connection": "keep-alive",
  "referer": "http://localhost:3000/",
  "sec-fetch-dest": "empty",
  "sec-fetch-mode": "cors",
  "sec-gpc": "1",
  "sec-fetch-site": "same-site",
}
Cookie header: undefined
Retrieved session: null

client information

Image Image
<!-- gh-comment-id:3615301407 --> @vCiKv commented on GitHub (Dec 5, 2025): @dosu i implemented the steps above and the results did not change relevant functions ```ts const routeAuth = new Hono<HonoEnv>() routeAuth.get("/get-session", async (c) => { // const session = c.get("session") // const user = c.get("user") const session = await auth.api.getSession({ headers: c.req.raw.headers }); const user = session?.user if (!user) return c.body(null, 401); return c.json({ session: session.session, user }); }); routeAuth.post("/signin", async (c) => { try { const { email, password } = await c.req.json() as { email: string; password: string; }; if (!email || !password) { return formatResponseH(c, 400, "no credentials"); } const signInRequest = await auth.api.signInEmail({ body: { password, email, }, headers: c.req.raw.headers, }) const user = signInRequest.user if (!signInRequest) { return formatResponseH(c, 404, "create account") } if (user.isBlacklisted) { return formatResponseH(c, 401, "Your Account is baned please contact support"); } return formatResponseH(c, 200, "ok") } catch (e) { console.error(e) return formatResponseH(c, 501, "error occurred") } }) ``` the servers response ```js Request headers: Headers { "host": "localhost:4000", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0", "accept": "application/json, text/plain, */*", "accept-language": "en-US,en;q=0.5", "accept-encoding": "gzip, deflate, br, zstd", "origin": "http://localhost:3000", "dnt": "1", "connection": "keep-alive", "referer": "http://localhost:3000/", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-gpc": "1", "sec-fetch-site": "same-site", } Cookie header: undefined Retrieved session: null ``` client information <img width="1001" height="577" alt="Image" src="https://github.com/user-attachments/assets/9ea96469-16e5-4a5c-99da-e9c5747de783" /> <img width="994" height="570" alt="Image" src="https://github.com/user-attachments/assets/c2b4fe56-553d-4262-a408-e118648cd087" />
Author
Owner

@dosubot[bot] commented on GitHub (Dec 5, 2025):

The missing Cookie header on your GET /api/auth/get-session request means the browser is not sending the session cookie, so Better Auth always returns null. This is almost always due to cookie attributes or CORS config in a cross-origin setup like localhost:3000 → localhost:4000.

Here’s what to check next:

  1. crossSubDomainCookies: For different ports (not subdomains), disable crossSubDomainCookies and remove any domain attribute from your cookie config. Setting a domain for localhost or different ports will prevent the cookie from being sent. Only use crossSubDomainCookies for real subdomains (e.g., api.localhost and app.localhost), not different ports. See docs

  2. Cookie attributes for localhost: For local HTTP, set secure: false and allowLocalhostUnsecure: true in defaultCookieAttributes. If secure: true is set, browsers will block the cookie on HTTP, so it never gets sent back. See explanation

  3. Check the Set-Cookie header: After sign-in, open your browser’s network tab, find the sign-in response, and look at the Set-Cookie header. Make sure the cookie is actually being set, and check its attributes (domain, path, secure, sameSite). If the cookie is missing or has a domain attribute, it won’t be sent on subsequent requests.

  4. Check browser cookie storage: In DevTools > Application > Cookies, confirm the session cookie (better-auth.session_token or __Secure-better-auth.session_token) is present for localhost:4000 after sign-in.

  5. Check the GET request: In the network tab, inspect the GET /api/auth/get-session request. Is the session cookie being sent in the request headers? If not, the browser is blocking it due to cookie attributes or CORS.

  6. Try Chrome incognito: Some browsers (especially Firefox, Brave, Safari) block third-party cookies by default. Test in Chrome incognito with all extensions disabled to rule out browser-side blocking. See more

  7. HTTPS for Secure cookies: If you want to use secure: true and SameSite=None, you must run your backend over HTTPS (e.g., with mkcert or ngrok). Browsers will not send Secure cookies over HTTP. See explanation

If you can, please share the Set-Cookie header from the sign-in response and a screenshot of your browser’s cookie storage after sign-in. This will help pinpoint if the cookie is being set and why it’s not sent on GET requests.

If you want a workaround for server-side session retrieval in Next.js, you can create a custom API route or server action that forwards the cookie header from the browser to your auth backend, as shown in this example.

Let me know what you find in the Set-Cookie header and browser cookies after sign-in!

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3615314853 --> @dosubot[bot] commented on GitHub (Dec 5, 2025): <!-- Answer --> The missing Cookie header on your GET /api/auth/get-session request means the browser is not sending the session cookie, so Better Auth always returns null. This is almost always due to cookie attributes or CORS config in a cross-origin setup like localhost:3000 → localhost:4000. Here’s what to check next: 1. **crossSubDomainCookies**: For different ports (not subdomains), disable crossSubDomainCookies and remove any domain attribute from your cookie config. Setting a domain for localhost or different ports will prevent the cookie from being sent. Only use crossSubDomainCookies for real subdomains (e.g., api.localhost and app.localhost), not different ports. [See docs](https://github.com/better-auth/better-auth/blob/015a5c4b660b69f2a941ffd069ea831c8670a45d/docs/content/docs/integrations/hono.mdx#L19-L186) 2. **Cookie attributes for localhost**: For local HTTP, set `secure: false` and `allowLocalhostUnsecure: true` in defaultCookieAttributes. If `secure: true` is set, browsers will block the cookie on HTTP, so it never gets sent back. [See explanation](https://github.com/better-auth/better-auth/issues/4555) 3. **Check the Set-Cookie header**: After sign-in, open your browser’s network tab, find the sign-in response, and look at the Set-Cookie header. Make sure the cookie is actually being set, and check its attributes (domain, path, secure, sameSite). If the cookie is missing or has a domain attribute, it won’t be sent on subsequent requests. 4. **Check browser cookie storage**: In DevTools > Application > Cookies, confirm the session cookie (better-auth.session_token or __Secure-better-auth.session_token) is present for localhost:4000 after sign-in. 5. **Check the GET request**: In the network tab, inspect the GET /api/auth/get-session request. Is the session cookie being sent in the request headers? If not, the browser is blocking it due to cookie attributes or CORS. 6. **Try Chrome incognito**: Some browsers (especially Firefox, Brave, Safari) block third-party cookies by default. Test in Chrome incognito with all extensions disabled to rule out browser-side blocking. [See more](https://github.com/better-auth/better-auth/issues/4483) 7. **HTTPS for Secure cookies**: If you want to use `secure: true` and SameSite=None, you must run your backend over HTTPS (e.g., with mkcert or ngrok). Browsers will not send Secure cookies over HTTP. [See explanation](https://github.com/better-auth/better-auth/issues/4555) If you can, please share the Set-Cookie header from the sign-in response and a screenshot of your browser’s cookie storage after sign-in. This will help pinpoint if the cookie is being set and why it’s not sent on GET requests. If you want a workaround for server-side session retrieval in Next.js, you can create a custom API route or server action that forwards the cookie header from the browser to your auth backend, as shown in [this example](https://github.com/better-auth/better-auth/issues/4188). Let me know what you find in the Set-Cookie header and browser cookies after sign-in! <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/7de94995-1cf6-47af-9dc2-f742cfd5bfce?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/7de94995-1cf6-47af-9dc2-f742cfd5bfce?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/7de94995-1cf6-47af-9dc2-f742cfd5bfce?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/7de94995-1cf6-47af-9dc2-f742cfd5bfce?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/7de94995-1cf6-47af-9dc2-f742cfd5bfce?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/7de94995-1cf6-47af-9dc2-f742cfd5bfce?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/7de94995-1cf6-47af-9dc2-f742cfd5bfce?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=The%20missing%20Cookie%20header%20on%20your%20GET%20/api/auth/get-session%20request%20means%20the%20browser%20is%20not%20sending%20the%20session%20cookie%2C%20so%20Better%20Auth%20always%20returns%20null.%20This%20is%20almost%20always%20due%20to%20cookie%20attributes%20or%20CORS%20config%20in%20a%20cross-origin%20setup%20like%20localhost%3A3000%20%E2%86%92%20localhost%3A4000.%0A%0AHere%E2%80%99s%20what%20to%20check%20next%3A%0A%0A1.%20%2A%2AcrossSubDomainCookies%2A%2A%3A%20For%20different%20ports%20%28not%20subdomains%29%2C%20disable%20crossSubDomainCookies%20and%20remove%20any%20domain%20attribute%20from%20your%20cookie%20config.%20Setting%20a%20domain%20for%20localhost%20or%20different%20ports%20will%20prevent%20the%20cookie%20from%20being%20sent.%20Only%20use%20crossSubDomainCookies%20for%20real%20subdomains%20%28e.g.%2C%20api.localhost%20and%20app.localhost%29%2C%20not%20different%20ports.%20%5BSee%20docs%5D%28https%3A//github.com/better-auth/better-auth/blob/015a5c4b660b69f2a941ffd069ea831c8670a45d/docs/content/docs/integrations/hono.mdx%23L19-L186%29%0A%0A2.%20%2A%2ACookie%20attributes%20for%20localhost%2A%2A%3A%20For%20local%20HTTP%2C%20set%20%60secure%3A%20false%60%20and%20%60allowLocalhostUnsecure%3A%20true%60%20in%20defaultCookieAttributes.%20If%20%60secure%3A%20true%60%20is%20set%2C%20browsers%20will%20block%20the%20cookie%20on%20HTTP%2C%20so%20it%20never%20gets%20sent%20back.%20%5BSee%20explanation%5D%28https%3A//github.com/better-auth/better-auth/issues/4555%29%0A%0A3.%20%2A%2ACheck%20the%20Set-Cookie%20header%2A%2A%3A%20After%20sign-in%2C%20open%20your%20browser%E2%80%99s%20network%20tab%2C%20find%20the%20sign-in%20response%2C%20and%20look%20at%20the%20Set-Cookie%20header.%20Make%20sure%20the%20cookie%20is%20actually%20being%20set%2C%20and%20check%20its%20attributes%20%28domain%2C%20path%2C%20secure%2C%20sameSite%29.%20If%20the%20cookie%20is%20missing%20or%20has%20a%20domain%20attribute%2C%20it%20won%E2%80%99t%20be%20sent%20on%20subsequent%20requests.%0A%0A4.%20%2A%2ACheck%20browser%20cookie%20storage%2A%2A%3A%20In%20DevTools%20%3E%20Application%20%3E%20Cookies%2C%20confirm%20the%20session%20cookie%20%28better-auth.session_token%20or%20__Secure-better-auth.session_token%29%20is%20present%20for%20localhost%3A4000%20after%20sign-in.%0A%0A5.%20%2A%2ACheck%20the%20GET%20request%2A%2A%3A%20In%20the%20network%20tab%2C%20inspect%20the%20GET%20/api/auth/get-session%20request.%20Is%20the%20session%20cookie%20being%20sent%20in%20the%20request%20headers%3F%20If%20not%2C%20the%20browser%20is%20blocking%20it%20due%20to%20cookie%20attributes%20or%20CORS.%0A%0A6.%20%2A%2ATry%20Chrome%20incognito%2A%2A%3A%20Some%20browsers%20%28especially%20Firefox%2C%20Brave%2C%20Safari%29%20block%20third-party%20cookies%20by%20default.%20Test%20in%20Chrome%20incognito%20with%20all%20extensions%20disabled%20to%20rule%20out%20browser-side%20blocking.%20%5BSee%20more%5D%28https%3A//github.com/better-auth/better-auth/issues/4483%29%0A%0A7.%20%2A%2AHTTPS%20for%20Secure%20cookies%2A%2A%3A%20If%20you%20want%20to%20use%20%60secure%3A%20true%60%20and%20SameSite%3DNone%2C%20you%20must%20run%20your%20backend%20over%20HTTPS%20%28e.g.%2C%20with%20mkcert%20or%20ngrok%29.%20Browsers%20will%20not%20send%20Secure%20cookies%20over%20HTTP.%20%5BSee%20explanation%5D%28https%3A//github.com/better-auth/better-auth/issues/4555%29%0A%0AIf%20you%20can%2C%20please%20share%20the%20Set-Cookie%20header%20from%20the%20sign-in%20response%20and%20a%20screenshot%20of%20your%20browser%E2%80%99s%20cookie%20storage%20after%20sign-in.%20This%20will%20help%20pinpoint%20if%20the%20cookie%20is%20being%20set%20and%20why%20it%E2%80%99s%20not%20sent%20on%20GET%20requests.%0A%0AIf%20you%20want%20a%20workaround%20for%20server-side%20session%20retrieval%20in%20Next.js%2C%20you%20can%20create%20a%20custom%20API%20route%20or%20server%20action%20that%20forwards%20the%20cookie%20header%20from%20the%20browser%20to%20your%20auth%20backend%2C%20as%20shown%20in%20%5Bthis%20example%5D%28https%3A//github.com/better-auth/better-auth/issues/4188%29.%0A%0ALet%20me%20know%20what%20you%20find%20in%20the%20Set-Cookie%20header%20and%20browser%20cookies%20after%20sign-in%21)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/6530)
Author
Owner

@vCiKv commented on GitHub (Dec 5, 2025):

@dosu here are my current cookie and cors settings

cookies

import db from "@db/connect";
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";

const auth = betterAuth({
  database: prismaAdapter(db, {
    provider: "postgresql",
  }),
  emailAndPassword: {
    enabled: true,
  },
  advanced: {
    defaultCookieAttributes: {
      allowLocalhostUnsecure: true,
      sameSite: "none",
      secure: false,
      httpOnly: true,
    }
  }
});

export default auth;

CORS

const app = new Hono<HonoEnv>()
app.use("*", cors({
  origin: 'http://localhost:3000',
  allowMethods: ["POST", "GET", "PUT", "DELETE", "OPTIONS"],
  allowHeaders: ["Content-Type", "Authorization"],
  exposeHeaders: ["Content-Length"],
  maxAge: 600,
  credentials: true,
}))

i've tried the steps above and the cookie does not show on both localhost:3000 and localhost:4000 no cookie is set it says No cookies for this request and the application cookie storage does not have any cookies before/after the request even on different browsers results the same

<!-- gh-comment-id:3615346343 --> @vCiKv commented on GitHub (Dec 5, 2025): @dosu here are my current cookie and cors settings cookies ```ts import db from "@db/connect"; import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; const auth = betterAuth({ database: prismaAdapter(db, { provider: "postgresql", }), emailAndPassword: { enabled: true, }, advanced: { defaultCookieAttributes: { allowLocalhostUnsecure: true, sameSite: "none", secure: false, httpOnly: true, } } }); export default auth; ``` CORS ```ts const app = new Hono<HonoEnv>() app.use("*", cors({ origin: 'http://localhost:3000', allowMethods: ["POST", "GET", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["Content-Type", "Authorization"], exposeHeaders: ["Content-Length"], maxAge: 600, credentials: true, })) ``` i've tried the steps above and the cookie does not show on both localhost:3000 and localhost:4000 no cookie is set it says `No cookies for this request` and the application cookie storage does not have any cookies before/after the request even on different browsers results the same
Author
Owner

@dosubot[bot] commented on GitHub (Dec 5, 2025):

Your config looks correct for local development, but if no cookie is set after sign-in, it usually means one of these is happening:

  1. The backend is not actually sending a Set-Cookie header after sign-in, or the header is malformed.
  2. Middleware or a proxy is stripping or not forwarding Set-Cookie headers.
  3. The browser is blocking the cookie due to attributes, CORS, or a redirect flow.

To pinpoint the issue, please do the following:

  • Open your browser’s network tab, sign in, and inspect the response to the POST /api/auth/signin request. Look for the Set-Cookie header—copy its full value and all response headers here.
  • Check your backend logs for any errors or warnings during sign-in.
  • Confirm that your frontend sign-in request is using credentials: 'include' and that the response is not a redirect (like 307).
  • Try disabling all browser extensions and test in Chrome incognito.
  • Let me know if you have any proxy, reverse proxy, or custom middleware between frontend and backend.

If the Set-Cookie header is missing or malformed, that’s the root cause. If it’s present and correct but the cookie still isn’t stored, the browser is blocking it—often due to CORS, attributes, or a redirect immediately after setting the cookie (see details).

If you want to rule out browser enforcement, you can try enabling advanced.crossOriginCookies with secure: true and test with HTTPS (using something like ngrok or mkcert) (reference).

Once you share the Set-Cookie header and response details, I can help you debug further.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3615371108 --> @dosubot[bot] commented on GitHub (Dec 5, 2025): <!-- Answer --> Your config looks correct for local development, but if no cookie is set after sign-in, it usually means one of these is happening: 1. The backend is not actually sending a Set-Cookie header after sign-in, or the header is malformed. 2. Middleware or a proxy is stripping or not forwarding Set-Cookie headers. 3. The browser is blocking the cookie due to attributes, CORS, or a redirect flow. To pinpoint the issue, please do the following: - Open your browser’s network tab, sign in, and inspect the response to the POST /api/auth/signin request. Look for the Set-Cookie header—copy its full value and all response headers here. - Check your backend logs for any errors or warnings during sign-in. - Confirm that your frontend sign-in request is using credentials: 'include' and that the response is not a redirect (like 307). - Try disabling all browser extensions and test in Chrome incognito. - Let me know if you have any proxy, reverse proxy, or custom middleware between frontend and backend. If the Set-Cookie header is missing or malformed, that’s the root cause. If it’s present and correct but the cookie still isn’t stored, the browser is blocking it—often due to CORS, attributes, or a redirect immediately after setting the cookie ([see details](https://github.com/better-auth/better-auth/issues/4038#issuecomment-3193374037)). If you want to rule out browser enforcement, you can try enabling advanced.crossOriginCookies with secure: true and test with HTTPS (using something like ngrok or mkcert) ([reference](https://github.com/better-auth/better-auth/issues/4720#issuecomment-3302800773)). Once you share the Set-Cookie header and response details, I can help you debug further. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/954e33e6-acff-44f4-972c-a77bbb6e32c9?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/954e33e6-acff-44f4-972c-a77bbb6e32c9?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/954e33e6-acff-44f4-972c-a77bbb6e32c9?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/954e33e6-acff-44f4-972c-a77bbb6e32c9?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/954e33e6-acff-44f4-972c-a77bbb6e32c9?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/954e33e6-acff-44f4-972c-a77bbb6e32c9?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/954e33e6-acff-44f4-972c-a77bbb6e32c9?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Your%20config%20looks%20correct%20for%20local%20development%2C%20but%20if%20no%20cookie%20is%20set%20after%20sign-in%2C%20it%20usually%20means%20one%20of%20these%20is%20happening%3A%0A%0A1.%20The%20backend%20is%20not%20actually%20sending%20a%20Set-Cookie%20header%20after%20sign-in%2C%20or%20the%20header%20is%20malformed.%0A2.%20Middleware%20or%20a%20proxy%20is%20stripping%20or%20not%20forwarding%20Set-Cookie%20headers.%0A3.%20The%20browser%20is%20blocking%20the%20cookie%20due%20to%20attributes%2C%20CORS%2C%20or%20a%20redirect%20flow.%0A%0ATo%20pinpoint%20the%20issue%2C%20please%20do%20the%20following%3A%0A%0A-%20Open%20your%20browser%E2%80%99s%20network%20tab%2C%20sign%20in%2C%20and%20inspect%20the%20response%20to%20the%20POST%20/api/auth/signin%20request.%20Look%20for%20the%20Set-Cookie%20header%E2%80%94copy%20its%20full%20value%20and%20all%20response%20headers%20here.%0A-%20Check%20your%20backend%20logs%20for%20any%20errors%20or%20warnings%20during%20sign-in.%0A-%20Confirm%20that%20your%20frontend%20sign-in%20request%20is%20using%20credentials%3A%20%27include%27%20and%20that%20the%20response%20is%20not%20a%20redirect%20%28like%20307%29.%0A-%20Try%20disabling%20all%20browser%20extensions%20and%20test%20in%20Chrome%20incognito.%0A-%20Let%20me%20know%20if%20you%20have%20any%20proxy%2C%20reverse%20proxy%2C%20or%20custom%20middleware%20between%20frontend%20and%20backend.%0A%0AIf%20the%20Set-Cookie%20header%20is%20missing%20or%20malformed%2C%20that%E2%80%99s%20the%20root%20cause.%20If%20it%E2%80%99s%20present%20and%20correct%20but%20the%20cookie%20still%20isn%E2%80%99t%20stored%2C%20the%20browser%20is%20blocking%20it%E2%80%94often%20due%20to%20CORS%2C%20attributes%2C%20or%20a%20redirect%20immediately%20after%20setting%20the%20cookie%20%28%5Bsee%20details%5D%28https%3A//github.com/better-auth/better-auth/issues/4038%23issuecomment-3193374037%29%29.%20%0A%0AIf%20you%20want%20to%20rule%20out%20browser%20enforcement%2C%20you%20can%20try%20enabling%20advanced.crossOriginCookies%20with%20secure%3A%20true%20and%20test%20with%20HTTPS%20%28using%20something%20like%20ngrok%20or%20mkcert%29%20%28%5Breference%5D%28https%3A//github.com/better-auth/better-auth/issues/4720%23issuecomment-3302800773%29%29.%20%0A%0AOnce%20you%20share%20the%20Set-Cookie%20header%20and%20response%20details%2C%20I%20can%20help%20you%20debug%20further.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/6530)
Author
Owner

@vCiKv commented on GitHub (Dec 5, 2025):

on further investigation the SET-Cookie Header was not present
response from the server

Headers {
  "host": "localhost:4000",
  "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
  "accept": "application/json, text/plain, */*",
  "accept-language": "en-US,en;q=0.5",
  "accept-encoding": "gzip, deflate, br, zstd",
  "origin": "http://localhost:3000",
  "dnt": "1",
  "connection": "keep-alive",
  "referer": "http://localhost:3000/",
  "sec-fetch-dest": "empty",
  "sec-fetch-mode": "cors",
  "sec-gpc": "1",
  "sec-fetch-site": "same-site",
}

response from the client

{
	"responseHeaders": {
		"headers": [
			{
				"name": "Access-Control-Allow-Credentials",
				"value": "true"
			},
			{
				"name": "Access-Control-Allow-Origin",
				"value": "http://localhost:3000"
			},
			{
				"name": "Access-Control-Expose-Headers",
				"value": "Content-Length"
			},
			{
				"name": "Content-Length",
				"value": "32"
			},
			{
				"name": "Content-Type",
				"value": "application/json"
			},
			{
				"name": "Date",
				"value": "Fri, 05 Dec 2025 08:33:45 GMT"
			},
			{
				"name": "Vary",
				"value": "Origin"
			}
		]
	}
}
<!-- gh-comment-id:3616291877 --> @vCiKv commented on GitHub (Dec 5, 2025): on further investigation the SET-Cookie Header was not present response from the server ```js Headers { "host": "localhost:4000", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0", "accept": "application/json, text/plain, */*", "accept-language": "en-US,en;q=0.5", "accept-encoding": "gzip, deflate, br, zstd", "origin": "http://localhost:3000", "dnt": "1", "connection": "keep-alive", "referer": "http://localhost:3000/", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-gpc": "1", "sec-fetch-site": "same-site", } ``` response from the client ```js { "responseHeaders": { "headers": [ { "name": "Access-Control-Allow-Credentials", "value": "true" }, { "name": "Access-Control-Allow-Origin", "value": "http://localhost:3000" }, { "name": "Access-Control-Expose-Headers", "value": "Content-Length" }, { "name": "Content-Length", "value": "32" }, { "name": "Content-Type", "value": "application/json" }, { "name": "Date", "value": "Fri, 05 Dec 2025 08:33:45 GMT" }, { "name": "Vary", "value": "Origin" } ] } } ```
Author
Owner

@Saikrishna1876 commented on GitHub (Dec 5, 2025):

@vCiKv I have tried my best to try to understand your issue, and I have come up with small working project that you have described above. The project link is here.

<!-- gh-comment-id:3616440421 --> @Saikrishna1876 commented on GitHub (Dec 5, 2025): @vCiKv I have tried my best to try to understand your issue, and I have come up with small working project that you have described above. The project link is [here](https://github.com/Saikrishna1876/better-auth-issue-6530).
Author
Owner

@vCiKv commented on GitHub (Dec 6, 2025):

@Saikrishna1876 thanks so much for the repo it was key in helping me figure things out, essentially i misunderstood the docs and usage of better-auth + hono (it's my first time using both) this might be a long one so ill quickly go over it

for a basic hono+better-auth all you need to do is add this to your http server
hono (will work and set-cookies)

const app = new Hono<HonoEnv>()
app.use("*",cors({})) //cors options for your app from 'hono/cors'
app.on(["POST", "GET"], "/api/auth/*", (c) => {
  return auth.handler(c.req.raw); //this basically overwrites/creates the relevant urls  going to "api/auth" and your client code works normally 
});

nextjs (will work and get cookies)

const signIn = async()=>{ 
  await authClient.signIn.email({ //under the hood it's a fetch request to server.url/api/auth/sign-in/email
    email: "user@example.com",
    password: "password123",
  });
}

these will work with no issues

in my implementation i did not want to mix server and client in next-js and an implantation was already being worked on before i started to use better auth on this url server.url/api/auth/signin i wanted more control to process some data before accepting the request and to maintain that i implemented this code section

hono (will work but will not set cookie)

app.post("api/auth/signin", async (c) => {
  try {
    const { email, password } = await c.req.json() as { email: string; password: string; };
    if (!validateEmail(email) || !password) {
      return formatResponseH(c, 400, "no credentials");
    }
    const signInRequest = await auth.api.signInEmail({
      body: {
        password,
        email,
      },
      headers: c.req.raw.headers,
    })

    const user = signInRequest.user

    if (user?.isBlacklisted) {
      return formatResponseH(c, 401, "Your Account is banned please contact support");
    }
    return formatResponseH(c, 200, "ok")

  } catch (e) {
    console.error(e)
    return formatResponseH(c, 501, "error occurred")
  }
})

next js (will not work cause route is overwritten 404 error)

await axios.post("server.url/api/auth/signin",formData);

this style did not work this url server.url/api/auth/signin would return 404 because this line return auth.handler(c.req.raw); overrides it and at the time i did not understand what that line does after "solving" it and finding the server.url/api/auth/signin everything was working fine except the session did not save and cookies did not send. after being stuck, reevaluation checking cros and cookie options and seeing @Saikrishna1876 repo i realized everything that went wrong

THE SOLUTION (for the style i wanted)

hono (will work and set-cookie)

const app = new Hono<HonoEnv>()
app.use("*",cors({})) //cors options for your app from 'hono/cors'
//app.on(["POST", "GET"], "/api/auth/*", (c) => { return auth.handler(c.req.raw); }); //remove so our routes don't get overwritten 
app.post("api/auth/signin", async (c) => {
  try {
    const { email, password } = await c.req.json() as { email: string; password: string; };
    if (!validateEmail(email) || !password) {
      return formatResponseH(c, 400, "no credentials");
    }
    const signInRequest = await auth.api.signInEmail({
      body: {
        password,
        email,
      },
      headers: c.req.raw.headers,
      asResponse:true //✅ IMPORTANT this is the fix return as a request
    })

    // const user = signInRequest.user //better-auth returns a response now so this will fail 
   //with request data get header set cookie and manually set it 
 const betterAuthRequestCookie = signInRequest.headers.get("set-cookie")
  if (betterAuthRequestCookie) {
    c.header("Set-Cookie", betterAuthRequestCookie) //now the cookie is set if found
  } 
    const payload = await signInRequest.json()
    const user = payload?.user // ��� this is correct

    if (user?.isBlacklisted) {
      return formatResponseH(c, 401, "Your Account is banned please contact support");
    }
    return formatResponseH(c, 200, "ok")

  } catch (e) {
    console.error(e)
    return formatResponseH(c, 501, "error occurred")
  }
})
app.get("/api/auth/*", (c) => auth.handler(c.req.raw)).post((c) => auth.handler(c.req.raw)) //to catch all the default better-auth routes so all client functions still work
// app.all(""/api/auth/*"", (c) => auth.handler(c.req.raw)) // this will also work

nextjs (will work and get cookies)

await axios.post("server.url/api/auth/signin",formData); //now this works perfectly and sends the cookies

i'm sure there's way more ways to optimize this or some key-points i missed but this is what i have so far; i also think the documentation could help out a little just making it easier to understand concept, limitations and pitfalls (e.g. why something exist)

TLDR:
if you want to use a custom endpoint on hono e.g. server.url/api/auth/signin instead of server.url/api/auth/sign-in/email the default one or you want to use http fetch calls over the better-auth client then on your server you need to return your auth.api requests by enabling asResponse:true after the header the setting the relevant headers hope this helps

Image
<!-- gh-comment-id:3621343743 --> @vCiKv commented on GitHub (Dec 6, 2025): @Saikrishna1876 thanks so much for the repo it was key in helping me figure things out, essentially i misunderstood the docs and usage of better-auth + hono (it's my first time using both) this might be a long one so ill quickly go over it for a basic hono+better-auth all you need to do is add this to your http server hono (will work and set-cookies) ``` js const app = new Hono<HonoEnv>() app.use("*",cors({})) //cors options for your app from 'hono/cors' app.on(["POST", "GET"], "/api/auth/*", (c) => { return auth.handler(c.req.raw); //this basically overwrites/creates the relevant urls going to "api/auth" and your client code works normally }); ``` nextjs (will work and get cookies) ```js const signIn = async()=>{ await authClient.signIn.email({ //under the hood it's a fetch request to server.url/api/auth/sign-in/email email: "user@example.com", password: "password123", }); } ``` these will work with no issues in my implementation i did not want to mix server and client in next-js and an implantation was already being worked on before i started to use better auth on this url `server.url/api/auth/signin` i wanted more control to process some data before accepting the request and to maintain that i implemented this code section hono (will work but will not set cookie) ```js app.post("api/auth/signin", async (c) => { try { const { email, password } = await c.req.json() as { email: string; password: string; }; if (!validateEmail(email) || !password) { return formatResponseH(c, 400, "no credentials"); } const signInRequest = await auth.api.signInEmail({ body: { password, email, }, headers: c.req.raw.headers, }) const user = signInRequest.user if (user?.isBlacklisted) { return formatResponseH(c, 401, "Your Account is banned please contact support"); } return formatResponseH(c, 200, "ok") } catch (e) { console.error(e) return formatResponseH(c, 501, "error occurred") } }) ``` next js (will not work cause route is overwritten 404 error) ```js await axios.post("server.url/api/auth/signin",formData); ``` this style did not work this url `server.url/api/auth/signin` would return 404 because this line `return auth.handler(c.req.raw);` overrides it and at the time i did not understand what that line does after "solving" it and finding the `server.url/api/auth/signin` everything was working fine except the session did not save and cookies did not send. after being stuck, reevaluation checking cros and cookie options and seeing @Saikrishna1876 repo i realized everything that went wrong THE SOLUTION (for the style i wanted) hono (will work and set-cookie) ```js const app = new Hono<HonoEnv>() app.use("*",cors({})) //cors options for your app from 'hono/cors' //app.on(["POST", "GET"], "/api/auth/*", (c) => { return auth.handler(c.req.raw); }); //remove so our routes don't get overwritten app.post("api/auth/signin", async (c) => { try { const { email, password } = await c.req.json() as { email: string; password: string; }; if (!validateEmail(email) || !password) { return formatResponseH(c, 400, "no credentials"); } const signInRequest = await auth.api.signInEmail({ body: { password, email, }, headers: c.req.raw.headers, asResponse:true //✅ IMPORTANT this is the fix return as a request }) // const user = signInRequest.user //better-auth returns a response now so this will fail //with request data get header set cookie and manually set it const betterAuthRequestCookie = signInRequest.headers.get("set-cookie") if (betterAuthRequestCookie) { c.header("Set-Cookie", betterAuthRequestCookie) //now the cookie is set if found } const payload = await signInRequest.json() const user = payload?.user // ��� this is correct if (user?.isBlacklisted) { return formatResponseH(c, 401, "Your Account is banned please contact support"); } return formatResponseH(c, 200, "ok") } catch (e) { console.error(e) return formatResponseH(c, 501, "error occurred") } }) app.get("/api/auth/*", (c) => auth.handler(c.req.raw)).post((c) => auth.handler(c.req.raw)) //to catch all the default better-auth routes so all client functions still work // app.all(""/api/auth/*"", (c) => auth.handler(c.req.raw)) // this will also work ``` nextjs (will work and get cookies) ```js await axios.post("server.url/api/auth/signin",formData); //now this works perfectly and sends the cookies ``` i'm sure there's way more ways to optimize this or some key-points i missed but this is what i have so far; i also think the documentation could help out a little just making it easier to understand concept, limitations and pitfalls (e.g. why something exist) TLDR: if you want to use a custom endpoint on hono e.g. `server.url/api/auth/signin` instead of `server.url/api/auth/sign-in/email` the default one or you want to use http fetch calls over the better-auth client then on your server you need to return your `auth.api` requests by enabling `asResponse:true` after the header the setting the relevant headers hope this helps <img width="687" height="321" alt="Image" src="https://github.com/user-attachments/assets/a03f61d2-a6d1-46a2-b056-d55d4293e80a" />
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#10543