{"record":{"id":"818deffcf3dee155","repo":"tailscale/tailscale","slug":"csrf-request-denied-with-no-host-header","errorCode":null,"errorMessage":"CSRF request denied with no Host header","messagePattern":"CSRF request denied with no Host header","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"client/web/web.go","lineNumber":268,"sourceCode":"\t\t// browsers to \"potentially trustworthy\" origins i.e. localhost or those\n\t\t// served over HTTPS)\n\t\tsecFetchSite := r.Header.Get(\"Sec-Fetch-Site\")\n\t\tif secFetchSite == \"same-origin\" {\n\t\t\th.ServeHTTP(w, r)\n\t\t\treturn\n\t\t} else if secFetchSite != \"\" {\n\t\t\thttp.Error(w, fmt.Sprintf(\"CSRF request denied with Sec-Fetch-Site %q\", secFetchSite), http.StatusForbidden)\n\t\t\treturn\n\t\t}\n\n\t\t// if Sec-Fetch-Site is not available we presume we are operating over HTTP.\n\t\t// We fall back to comparing the Origin & Host headers.\n\n\t\t// use the Host header to determine the expected origin\n\t\t// (use the override if set to allow for reverse proxying)\n\t\thost := r.Host\n\t\tif host == \"\" {\n\t\t\thttp.Error(w, \"CSRF request denied with no Host header\", http.StatusForbidden)\n\t\t\treturn\n\t\t}\n\t\tif s.originOverride != \"\" {\n\t\t\thost = s.originOverride\n\t\t}\n\n\t\toriginHeader := r.Header.Get(\"Origin\")\n\t\tif originHeader == \"\" {\n\t\t\thttp.Error(w, \"CSRF request denied with no Origin header\", http.StatusForbidden)\n\t\t\treturn\n\t\t}\n\t\tparsedOrigin, err := url.Parse(originHeader)\n\t\tif err != nil {\n\t\t\thttp.Error(w, fmt.Sprintf(\"CSRF request denied with invalid Origin %q\", r.Header.Get(\"Origin\")), http.StatusForbidden)\n\t\t\treturn\n\t\t}\n\t\torigin := parsedOrigin.Host\n\t\tif origin == \"\" {","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/client/web/web.go#L250-L286","documentation":"CSRF fallback validation: the request had no Sec-Fetch-Site header (typical for plain HTTP) and also carried no Host header, so the expected origin for the Origin/Host comparison cannot be established.","triggerScenarios":"Thrown at client/web/web.go:268 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Send requests through a proper HTTP client that sets Host","Check for a misconfigured reverse proxy stripping the Host header","Use HTTPS so Sec-Fetch-Site is available instead"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}