- 🧠
auth.getUser()returningnullin API routes is usually due to missing or unparsed authentication cookies. - ⚠️ Supabase requires access tokens, often stored as HttpOnly cookies, to identify users server-side.
- 🔐 Supabase auth helpers for Next.js can automatically handle cookie parsing and secure user identification.
- 🔄 Middleware setup in Next.js streamlines session verification on both private routes and API calls.
- 🚫 Without manually passing tokens or using helpers, server-side functions can't authenticate users properly.
Running into auth.getUser() returning null inside your Next.js API routes can be frustrating—especially when the client seems fully authenticated. You're not alone. This common issue often boils down to how cookies and tokens are handled server-side. This guide explains how Supabase auth works, why this problem happens, and how to fix it step-by-step. You can then get the current user correctly in your backend routes.
How Supabase Authentication Works (At a Glance)
Supabase uses a modern token-based authentication model similar to those found in major authentication frameworks like Firebase and Auth0. When users log in, Supabase generates an access token and a refresh token in the background. These tokens are typically stored in secure HTTP-only browser cookies for security and persistence.
The access token is a JWT (JSON Web Token) that includes information about the user and has a defined expiration time (typically 1 hour). The refresh token allows the client to obtain a new access token without requiring the user to log in again.
On the client side:
- Auth state is managed via Supabase’s JavaScript client.
- Cookies are automatically stored and contain the needed tokens.
- Calls to
supabase.auth.getUser()will typically return the current user as expected.
On the server side (like in Next.js API routes), however, problems can arise. Because tokens are stored in browser cookies, the server has to explicitly extract and validate those cookies to understand which user is making a request.
The Server-Side Challenge in Next.js
When you're building API routes in Next.js, those routes run entirely on the server and do not automatically have access to browser session cookies unless they are forwarded alongside the request.
Next.js API handlers deal with raw req and res objects. Unlike Express (which has middleware like cookie-parser), these don’t automatically parse HTTP cookies into usable data. This leads to the following pain points:
- Cookies aren't parsed: You won’t get structured cookies unless you manually parse them.
- HttpOnly cookies not accessible in JS: Because of browser security settings, tokens like
sb-access-tokenaren’t accessible via JavaScript and must be parsed server-side. - SSL and Security flags: In production, cookies with
SecureandSameSiteflags might be dropped in improperly configured environments.
The result? Even if a user is logged in on the client, auth.getUser() inside a Next.js backend route can still return null.
Why auth.getUser() Returns Null
Let's look at what happens when you call auth.getUser() on the server:
-
It expects you to provide a valid access token, either via:
- A Bearer token in the
Authorizationheader. - A cookie (
sb-access-token) that contains the access token.
- A Bearer token in the
-
If neither is available—or the provided value is invalid—Supabase can’t validate the session.
That’s why you see this often:
const { data: { user }, error } = await supabase.auth.getUser();
console.log(user); // null
Even worse, this misleads developers into thinking something is wrong with Supabase itself when the root problem is that the token isn't being properly passed to the backend.
How Supabase Uses Cookies to Authenticate
Upon successful login, Supabase stores two cookies:
| Cookie Name | Purpose | Visibility | Notes |
|---|---|---|---|
sb-access-token |
JWT used for accessing resources | HttpOnly | Expires in ~1 hour |
sb-refresh-token |
Used to regenerate access credentials | HttpOnly | Long-lived (~1 week) |
These cookies are configured with important flags:
HttpOnly: Not accessible via JavaScript—improving security.Secure: Ensures cookies are sent only over HTTPS.SameSite: Often set to Lax or Strict, limiting when cookies are sent.
These settings protect your app—but they add complexity on the server side. Supabase does not automatically retrieve or parse these cookies in your server environment like API Routes or Server-Side Rendering contexts.
That’s why when using Supabase + Next.js, developers should either manually extract cookies or use Supabase’s provided utilities.
Diagnosing the Problem in Your Project
Troubleshooting begins with confirming what the server sees—whether the cookies exist or not.
Step 1: Log Incoming Cookies
Confirm whether the sb-access-token is even being sent to your API route:
export default function handler(req, res) {
console.log(req.headers.cookie); // Output raw cookie string
res.end();
}
If req.headers.cookie is undefined or doesn’t contain sb-access-token, the client hasn’t sent the token—or it’s being blocked due to a missing HTTPS or SameSite configuration.
Step 2: Inspect Request in Browser or Postman
From your browser’s DevTools:
- Go to the Network tab.
- Refresh your page or trigger the API route.
- Click the request to your Next.js route, then go to the Headers section.
- Expand Request Headers and Cookies—check for
sb-access-token.
Or use Postman to simulate a call, manually adding cookies to the request. If the cookies aren’t sent, chances are your API route is not on the same domain or doesn't respect cookie policy.
Step 3: Inspect Supabase's Response
Sometimes a session might expire silently. Better handle this:
- Watch for session expiration in the client.
- Use
supabase.auth.onAuthStateChange(...)to handle expiry and auto-refresh.
Solutions: Properly Handling Supabase Cookies in Next.js API Routes
Time to fix it. Two main approaches—manual parsing or Supabase's Auth Helpers.
Strategy 1: Manually Parse Cookies
You can use the popular cookie npm package to extract sb-access-token from the request headers.
📦 Install the cookie parser:
npm install cookie
🛠️ Sample code:
import { parse } from 'cookie';
import { createClient } from '@supabase/supabase-js';
export default async function handler(req, res) {
const cookies = parse(req.headers.cookie || '');
const token = cookies['sb-access-token'];
if (!token) {
return res.status(401).json({ error: 'No token found' });
}
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
global: {
headers: {
Authorization: `Bearer ${token}`
},
},
});
const { data: { user }, error } = await supabase.auth.getUser();
if (error || !user) {
return res.status(401).json({ error: 'Invalid user' });
}
res.status(200).json({ user });
}
📝 Notes:
- This method provides full control but comes with risks: cookie format, token expiration, and errors must be manually handled.
Strategy 2: Use Supabase Auth Helpers (Recommended)
Supabase’s official @supabase/auth-helpers-nextjs library is built specifically for this problem.
✅ Benefits:
- Automatically parses cookies.
- Automatically manages session sync.
- Fully typed and integrates into Next.js’s design patterns.
📦 Install:
npm install @supabase/auth-helpers-nextjs
📘 Example using createServerSupabaseClient:
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs';
export default async function handler(req, res) {
const supabase = createServerSupabaseClient({ req, res });
const {
data: { user },
error
} = await supabase.auth.getUser();
if (error || !user) {
return res.status(401).json({ error: 'Unauthorized' });
}
res.status(200).json({ user });
}
It’s cleaner, robust, and easier to maintain.
Code Example: Authenticated API Route
Here’s a real project setup where you're protecting an endpoint /api/user-profile.
// pages/api/user-profile.js
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs';
export default async function handler(req, res) {
const supabase = createServerSupabaseClient({ req, res });
const { data: { user }, error } = await supabase.auth.getUser();
if (error || !user) {
return res.status(401).json({ error: 'Not authenticated' });
}
res.json({ id: user.id, email: user.email });
}
Middleware Setup in Next.js
The helpers package includes a middleware utility to block unauthenticated users or protect API routes.
🧱 Simple middleware config:
// middleware.ts
import { withMiddlewareAuth } from '@supabase/auth-helpers-nextjs';
export const middleware = withMiddlewareAuth();
export const config = {
matcher: ['/dashboard/:path*', '/api/user-profile'],
};
Benefits:
- Denies access before route logic.
- Compatible with both API routes and page directories.
- Confirms the user is logged in with a valid session.
Using SSR and getServerSideProps
To load user-specific data in server-rendered pages, use Supabase’s server helper in getServerSideProps.
🧩 Example:
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs';
export const getServerSideProps = async (ctx) => {
const supabase = createServerSupabaseClient(ctx);
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return {
redirect: {
destination: '/login',
permanent: false,
},
};
}
return {
props: {
user,
},
};
};
This approach guarantees the page has access to authenticated user data at the time of rendering.
Testing Auth Flow in Development
🧪 Ensure everything works before going live:
-
Use browser DevTools:
- Network tab → Check request and response cookies.
- Confirm
sb-access-tokenis present and matches expectations.
-
Use Postman or curl:
- Send headers with cookies to test APIs.
- Simulate expired tokens or missing cookies.
-
Add error logging:
- Log out both tokens and Supabase errors (safely and securely).
-
Test HTTPS:
- For local development, use
localhostor setup HTTPS tunnels (e.g.,ngrok).
- For local development, use
Security Best Practices
Protect your app from session hijacking and token leaks:
- ✅ Use
HttpOnly,Secure, andSameSite=stricton all auth cookies, especially in production. - ✅ Validate token expiration and handle refresh logic where appropriate.
- ❌ Don't log sensitive tokens or credentials in client or server logs.
- ✅ Implement CSRF protection where needed.
- ✅ Apply middleware to properly limit access to protected routes.
Auth Without the Blindspots
If auth.getUser() returns null, especially in Next.js API routes, it’s almost always a cookie or session parsing issue. The server isn't getting the credentials it needs. By understanding how authentication flows in a server/client architecture and using the right tools—like middleware and auth-helpers—you can solve this problem for good.
Whether you manually parse cookies or rely on Supabase’s well-built auth-helpers, fixing this issue makes secure API handling possible again. It lets you give personalized responses and get user data. This is all part of a strong, growing auth system.
If this guide helped, share it with your team. Want to make hard full-stack problems easier? Find more of our Supabase + Next.js guides here. Happy coding!
Citations
- Supabase Docs. (2024). Working with Auth Helpers in Next.js. Supabase. https://supabase.com/docs/guides/auth/server-side/nextjs
- Stack Overflow. (2024). Supabase getUser() returns null in API route. Retrieved from https://stackoverflow.com
- Next.js Documentation. (2024). API Routes: Request Helpers. Vercel. https://nextjs.org/docs/api-routes/api-middlewares