Server Actions lose authentication in Cloudflare Workers environment
Describe the Bug
When running Payload CMS on Cloudflare Workers (using @opennextjs/cloudflare), Server Actions in the admin UI fail to authenticate despite valid authentication cookies being sent with the request. This causes all admin form submissions (e.g., updating documents, adding relationships) to redirect to /admin/login instead of processing the request.
Server Actions receive the authentication cookie but fail to validate it, resulting in:
- POST request to
/admin/collections/{collection}/{id}returns redirect to/login - RSC (React Server Components) payload contains error digest
- Browser console shows:
Error: An error occurred in the Server Components render. The specific message is omitted in production builds...
The issue only occurs in the Cloudflare Workers runtime, the same code works in standard Next.js dev mode (next dev).
When creating or editing an entry in the UI, I see all POST requests to /admin/collections/<collection-name>/<id> fail with a 500 code. The Network tab shows for each of these:
Request Headers:
- ✅
Cookie: payload-token=<valid-token>is present - ✅ All expected headers are sent
Response:
- ❌ No
Set-Cookieheaders (server doesn't attempt to update cookie) - ❌ Returns HTML with RSC error payload
- ❌ Response contains redirect digest:
NEXT_REDIRECT;replace;/admin/login?redirect=%2Fadmin%2Fcollections%2F<collection-name>%2F<id>;307;
When sending the same POST request with the same payload through Postman, I get a successful response:
- When the same POST request is made via Postman (with same payload-token cookie), it returns a 200 OK with the rendered admin page HTML
- This confirms the backend authentication logic works - the issue is specific to how Server Actions handle authentication in Workers
Possible reason
The Server Action (serverFunction in src/app/(payload)/layout.tsx) calls handleServerFunctions() which should receive the authentication context. However, in the Cloudflare Workers environment:
- The authentication cookie is sent by the browser ✅
- The cookie reaches the server ✅
- The Server Action execution context cannot access/validate the cookie ❌
- Payload's auth middleware treats the request as unauthenticated
- Server returns a redirect response
- RSC cannot properly serialize the redirect in Workers, causing the cryptic error
This suggests that Server Actions in Cloudflare Workers don't have proper access to request headers/cookies, or the execution context is different from standard Node.js environments.
Link to the code that reproduces this issue
https://github.com/nimbleape/payload-opennextjs-cloudflare-issue-reproduction
Reproduction Steps
- Connect the repo to a D1 database by updating the
wrangler.jsoncfile. - Run migrations with
pnpm payload migrate:create && pnpm payload deploy:database
- Build and run the app using
opennextjs-cloudflarewith
pnpm preview
(this uses remote bindings). 4. In the Users collection, create a new user and try entering text in any field to see the POST requests being fired and return a 500.
Which area(s) are affected? (Select all that apply)
area: ui
Environment Info
Binaries:
Node: 20.18.1
npm: 10.8.2
Yarn: 4.6.0
pnpm: 10.22.0
Relevant Packages:
payload: 3.64.0
next: 15.4.7
@opennextjs/cloudflare: 1.12.0
@payloadcms/db-d1-sqlite: 3.64.0
@payloadcms/drizzle: 3.64.0
@payloadcms/graphql: 3.64.0
@payloadcms/next/utilities: 3.64.0
@payloadcms/plugin-cloud-storage: 3.64.0
@payloadcms/richtext-lexical: 3.64.0
@payloadcms/storage-r2: 3.64.0
@payloadcms/translations: 3.64.0
@payloadcms/ui/shared: 3.64.0
react: 19.1.0
react-dom: 19.1.0
wrangler: 4.47.0
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 24.5.0: Tue Apr 22 19:48:46 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T8103
Available memory (MB): 16384
Available CPU cores: 8
I'm experiencing the same issue.
Additional info: The /me route (with valid payload token) returns a 200 but with the following body: { "user": null, "message": "Account" }
After a few minutes, the request is handled correctly and i'm able to access the CMS.
Any updates regarding this?
Hi @marcovidonis @YacineJazi @lantchou I tried to reproduce the issue by following the reproduction steps with the reproduction code but unfortunately I haven't been able to reproduce it. Any more details you can provide? Does using a more recent version of Payload+OpenNext+Wrangler fix it?
Hi @rjgtav
In my case, the first login succeeds without fail. However, when I log out, and then try logging in again with the same credentials, the aforementioned issue occurs. Maybe this can help you?
Hi @ViktorHuygeb ! Hmm unfortunately I'm still not able to reproduce the issue, after logging out and logging back in multiple times. I tested by deploying to Cloudflare and also with pnpm preview. Payload version 3.64.0, OpenNext version 1.12.0 and Wrangler version 4.47.0.
Can you provide a repository that reproduces the issue? And maybe even deploy it to a dummy workers.dev link? To ensure it's not a Cloudflare Account/Zone setting that is causing this
@rjgtav
Thanks for looking into it! Whilst investigating further I found the origin of the problem being hyperdrive that was caching my login requests, which kept returning invalid auth tokens.
I fixed it temporarily by disabling the hyperdrive caching for my database connection, but this isn't ideal. Thanks for looking into it tho!
Hi @rjgtav, so just to add more context, the issue I'm experiencing is not with logging in per se, it's with any action on documents. I can log in as a user, but if I try updating a document, or adding a relationship, for example, the POST request that is sent to the server fails to authenticate.
I'm using @payloadcms/db-d1-sqlite for my DB connection.
@ViktorHuygeb interesting... thanks for figuring out the root cause! Will probably need to patch the way Payload fetches the current session to make sure it is never cached.
@marcovidonis I see. Are you seeing the same issue in your repro project? If not, can you update it so I can try to reproduce it? Have you tried updating Payload to the latest version?
@rjgtav Thanks for looking into this.
I confirm that my reproduction repo shows my problem. I've just pushed an update where I upgraded Payload, @payloadcms/ and @opennextjs/cloudflare to the latest version, and Next to v15.5.7.
The behaviour is still the same: when making a change to a doc (could be just entering text in a field when creating a new user), the Network tab shows that the POST request to http://localhost:8787/admin/collections/users/create returns a 500 code.
Please note that the issue only occurs in the Cloudflare Workers runtime (when running pnpm preview in my example), while the same code works in standard Next.js dev mode (running next dev).
While just trying to create a simple entry like this seems to be successful despite the 500, this issue causes problems when there are relationship fields in the collections.
I've pushed another update where I introduced a Titles collection to show how the error affects usability. I've added the following field to Users:
{
name: 'title',
type: 'relationship',
relationTo: 'titles',
required: false,
}
In Titles, I've added a join field so I can see, for each title, which users it was assigned to. The field is:
{
name: 'users',
type: 'join',
collection: 'users',
on: 'title',
maxDepth: 2,
},
Now you can:
- Create a title
- Assign the title to a user
- Navigate back to the Titles collection
- See that the users field fails to load: see below
Expected behaviour: we should be able to see the list of users related to this title.
In the real app I'm working on, I rely on relationships and join fields extensively. I have a Next.js Front End app that I build using @opennextjs/cloudflare, but the build fails when trying to access collections containing join fields from the CMS.
I believe that solving the underlying 500 error issue that we see in the most simple case should solve the problems with relationships as well.
Any thoughts?
@marcovidonis Hmm.. I managed to reproduce the 500 errors to http://localhost:8787/admin/collections/users/create. Not sure what is causing that, but I noticed that in your package.json, in the preview script, you have the "--remote" flag at the end. This flag deploys a temporary worker with your code and then runs it. The official payload template doesn't use that flag as it uses the local emulation by wrangler but is still able to connect to the remote D1 database deployed on Cloudflare, thanks to remote bindings.
If I remove the "--remote" flag, then the 500s become 200s. It also fixes the 500s when loading the list of users in the page of a Title. Deploying your example also returns 200s for all those scenarios.
Any particular reason you're using the --remote flag in the preview command?
@rjgtav thanks for confirming the issue. You're right about the --remote flag, the wrangler.jsonc already specifies remote: true on bindings, so doing a remote preview is not necessary.
This does solve the issue in the reproduction repo: the UI works and I can GET a collection via Postman.
However, this doesn't seem to solve the issue I'm seeing in the project I'm working on. When I point my Front End app (which is also built with opennextjs-cloudflare and deployed on Workers) to the opennextjs-cloudflare build of Payload (whether local or deployed), the build just fails because it can't fetch content from the CMS: I still get a 500 error. I can also see this on GET requests from Postman. The collection definitions in my real app are more complex than the ones in the reproduction repo, and at the moment I haven't been able to find a way to recreate the same issue in the example. I'll need to find some time to pin down what is still causing the issue I'm seeing and find a way to reproduce it.