dify icon indicating copy to clipboard operation
dify copied to clipboard

CSRF token is missing or invalid.

Open imaxwen opened this issue 4 months ago • 1 comments

Self Checks

  • [x] I have read the Contributing Guide and Language Policy.
  • [x] This is only for bug report, if you would like to ask a question, please head to Discussions.
  • [x] I have searched for existing issues search for existing issues, including closed ones.
  • [x] I confirm that I am using English to submit this report, otherwise it will be closed.
  • [x] 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
  • [x] Please do not modify this template :) and fill in all the required fields.

Dify version

1.10.0

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

Our self hosted dify is using different subdomains for CONSOLE_WEB_URL (https://agent.myhost.com) and CONSOLE_API_URL(https://api.agent.myhost.com).

After upgrading to 1.9.2 and now 1.10.0, this issue has not been resolved yet. I updated env vars below in the .env file after upgrading to 1.10.0:

COOKIE_DOMAIN=agent.myhost.com
NEXT_PUBLIC_COOKIE_DOMAIN=agent.myhost.com

Steps to reproduce:

1、 Open a new Chrome window in incognito mode. 2、 access the CONSOLE_WEB_URL and login, login was succeed. 3、 many XHR requests got 401 status code and the body message shows :

{
    "code": "unauthorized",
    "message": "CSRF token is missing or invalid.",
    "status": 401
}

4、However, the csrf_token could be found in the cookie of the request header.

Here is an cURL request copied from browser request log(I only mannually changed the domain for privacy reason):

$: curl 'https://api.agent.myhost.com/console/api/workspaces' \
  -H 'accept: */*' \
  -H 'accept-language: zh-CN,zh;q=0.9' \
  -H 'content-type: application/json' \
  -b 'access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNzM4MWYyZjQtYTI2ZC00NmVmLWJhN2EtM2MzODIzZTY2MmZjIiwiZXhwIjoxNzYzMTEyNjcyLCJpc3MiOiJTRUxGX0hPU1RFRCIsInN1YiI6IkNvbnNvbGUgQVBJIFBhc3Nwb3J0In0.e2TkPiH2TVDpoZ9uptzmemNL34N9gS9rqDbnhAm7gvY; refresh_token=fb5c35ea4387d4b645b1e3fd46d1b8fe745662fe33d3d531bf5ce223418ca01b7da7cb3f5fe4fe6edb791d24e1444a605aba63b293b845e50179cbf63dfd0457; csrf_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjMxMTI2NzIsInN1YiI6IjczODFmMmY0LWEyNmQtNDZlZi1iYTdhLTNjMzgyM2U2NjJmYyJ9.AbgdktmbPpRXi8Pun-lKsRidsrlV3iQ6rwg6qM95upg' \
  -H 'origin: https://agent.myhost.com' \
  -H 'priority: u=1, i' \
  -H 'referer: https://agent.myhost.com/' \
  -H 'sec-ch-ua: "Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: same-site' \
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36' \
  -H 'x-csrf-token;'
{
    "code": "unauthorized",
    "message": "CSRF token is missing or invalid.",
    "status": 401
}

✔️ Expected Behavior

The API requests should be successful.

❌ Actual Behavior

The API requests return 401 unauthorized.

imaxwen avatar Nov 14 '25 08:11 imaxwen

Your CSRF validation issue is likely due to two things: the cookie domain configuration and how the CSRF token is sent in requests.

  1. COOKIE_DOMAIN should be set to the parent domain (not a subdomain). For your setup, use COOKIE_DOMAIN=myhost.com (or .myhost.com, both are accepted) in both your API and web .env files. This allows cookies (including csrf_token) to be shared between agent.myhost.com and api.agent.myhost.com as required for cross-subdomain authentication. Setting it to agent.myhost.com restricts the cookie to that subdomain only, which breaks cross-subdomain requests. See the usage notes in the latest .env.example files and recent PRs.

  2. The CSRF token must be sent in the X-CSRF-TOKEN header as well as in the cookie. Dify's backend checks the header, not just the cookie. If you are making requests manually (e.g., with cURL), you must include the header:

    -H 'X-CSRF-TOKEN: <your_csrf_token_value>'
    

    The frontend should handle this automatically, but if you are customizing requests or using tools, double-check that the header is present. If the header is missing, you will get a 401 error even if the cookie is set (details, code).

  3. CORS settings: Make sure your CORS configuration allows credentials and the X-CSRF-TOKEN header, but this should be handled by default in recent Dify versions (see here).

Checklist:

  • Set COOKIE_DOMAIN=myhost.com (not a subdomain) in both API and web .env files.
  • Set NEXT_PUBLIC_COOKIE_DOMAIN=myhost.com in the web .env file.
  • Restart all containers after changing .env.
  • Confirm that requests include both the csrf_token cookie and the X-CSRF-TOKEN header.

This should resolve the CSRF 401 errors in your cross-subdomain Dify deployment.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

dosubot[bot] avatar Nov 14 '25 08:11 dosubot[bot]

I'm running this on Railway and noticed that the environment variables have to be available during build time in the Dockerfile:

ARG NEXT_PUBLIC_COOKIE_DOMAIN
ARG NEXT_PUBLIC_API_PREFIX
ARG NEXT_PUBLIC_PUBLIC_API_PREFIX
ARG NEXT_PUBLIC_MARKETPLACE_API_PREFIX
ENV NEXT_PUBLIC_COOKIE_DOMAIN=${NEXT_PUBLIC_COOKIE_DOMAIN}
ENV NEXT_PUBLIC_API_PREFIX=${NEXT_PUBLIC_API_PREFIX}
ENV NEXT_PUBLIC_PUBLIC_API_PREFIX=${NEXT_PUBLIC_PUBLIC_API_PREFIX}
ENV NEXT_PUBLIC_MARKETPLACE_API_PREFIX=${NEXT_PUBLIC_MARKETPLACE_API_PREFIX}

Had to build it locally and push it to docker to make it work as a quick fix:

$ docker buildx create --name dify-builder --use   # run once

$ docker buildx build \
    --platform linux/amd64 \
    -t <docker-user>/dify-web:cookie-domain \
    --build-arg NEXT_PUBLIC_COOKIE_DOMAIN=... \
    --build-arg NEXT_PUBLIC_API_PREFIX=... \
    --build-arg NEXT_PUBLIC_PUBLIC_API_PREFIX=... \
    --build-arg NEXT_PUBLIC_MARKETPLACE_API_PREFIX=... \
    -f web/Dockerfile web \
    --push

monotykamary avatar Nov 16 '25 04:11 monotykamary

The solution Dosu proposed requires a rebuild.

Unset the COOKIE_DOMAIN environment variable on the API side. When it is set, the API issues a csrf-token cookie, but the frontend attempts to read __Host-csrf_token.

Rebuilding would make the frontend read csrf-token, but if you want to avoid a rebuild, unsetting COOKIE_DOMAIN will resolve the issue.

COOKIE_DOMAIN=

If COOKIE_DOMAIN is unset, both the frontend and backend will read the __Host-csrf_token cookie.

te-chan avatar Nov 17 '25 00:11 te-chan

I got the same problem, but I only set the CONSOLE_API_URL to my custom host.

importcjj avatar Nov 17 '25 16:11 importcjj