{"record":{"id":"3d9871afebb923f9","repo":"Dolibarr/dolibarr","slug":"access-refused-by-csrf-protection-in-main-inc-php-referrer","errorCode":null,"errorMessage":"Access refused by CSRF protection in main.inc.php. Referrer of form (${HTTP_REFERER}) is outside the server that serve this page (with method = ${REQUEST_METHOD}).","messagePattern":"Access refused by CSRF protection in main\\.inc\\.php\\. Referrer of form \\((.+?)\\) is outside the server that serve this page \\(with method = (.+?)\\)\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"htdocs/filefunc.inc.php","lineNumber":352,"sourceCode":"// Note about $_SERVER[HTTP_HOST/SERVER_NAME]: http://shiflett.org/blog/2006/mar/server-name-versus-http-host\n// See also CSRF protections done into main.inc.php\nif (!defined('NOCSRFCHECK') && isset($dolibarr_nocsrfcheck) && $dolibarr_nocsrfcheck == 1) {    // If $dolibarr_nocsrfcheck is 0, there is a strict CSRF test with token in main\n\tif (!empty($_SERVER['REQUEST_METHOD']) && !in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD')) && !empty($_SERVER['HTTP_HOST'])) {\n\t\t$csrfattack = false;\n\t\tif (empty($_SERVER['HTTP_REFERER'])) {\n\t\t\t$csrfattack = true; // An evil browser was used\n\t\t} else {\n\t\t\t$tmpa = parse_url($_SERVER['HTTP_HOST']);\n\t\t\t$tmpb = parse_url($_SERVER['HTTP_REFERER']);\n\t\t\tif ((empty($tmpa['host']) ? $tmpa['path'] : $tmpa['host']) != (empty($tmpb['host']) ? $tmpb['path'] : $tmpb['host'])) {\n\t\t\t\t$csrfattack = true;\n\t\t\t}\n\t\t}\n\t\tif ($csrfattack) {\n\t\t\t//print 'NOCSRFCHECK='.defined('NOCSRFCHECK').' REQUEST_METHOD='.$_SERVER['REQUEST_METHOD'].' HTTP_HOST='.$_SERVER['HTTP_HOST'].' HTTP_REFERER='.$_SERVER['HTTP_REFERER'];\n\t\t\t// Note: We can't use dol_escape_htmltag here to escape output because lib functions.lib.ph is not yet loaded.\n\t\t\tdol_syslog(\"--- Access to \".(empty($_SERVER[\"REQUEST_METHOD\"]) ? '' : $_SERVER[\"REQUEST_METHOD\"].' ').$_SERVER[\"PHP_SELF\"].\" refused by CSRF protection (Bad referrer).\", LOG_WARNING);\n\t\t\tprint \"Access refused by CSRF protection in main.inc.php. Referrer of form (\".htmlentities(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'], ENT_COMPAT, 'UTF-8').\") is outside the server that serve this page (with method = \".htmlentities($_SERVER['REQUEST_METHOD'], ENT_COMPAT, 'UTF-8').\").\\n\";\n\t\t\tprint \"If you access your server behind a proxy using url rewriting, you might check that all HTTP headers are propagated (or add the line \\$dolibarr_nocsrfcheck=1 into your conf.php file to remove this security check).\\n\";\n\t\t\tdie;\n\t\t}\n\t}\n\t// Another test is done later on token if option MAIN_SECURITY_CSRF_WITH_TOKEN is on.\n}\nif (empty($dolibarr_main_db_host) && !defined('NOREQUIREDB')) {\n\tprint '<div class=\"center\">Dolibarr setup is not yet complete.<br><br>'.\"\\n\";\n\tprint '<a href=\"install/index.php\">Click here to finish Dolibarr install process</a> ...</div>'.\"\\n\";\n\tdie;\n}\nif (empty($dolibarr_main_url_root) && !defined('NOREQUIREVIRTUALURL')) {\n\tprint 'Value for parameter \\'dolibarr_main_url_root\\' is not defined in your \\'htdocs\\conf\\conf.php\\' file.<br>'.\"\\n\";\n\tprint 'You must add this parameter with your full Dolibarr root Url (Example: http://myvirtualdomain/ or http://mydomain/mydolibarrurl/)'.\"\\n\";\n\tdie;\n}\n\nif (empty($dolibarr_main_url_root_alt)) {","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/Dolibarr/dolibarr/blob/598aa4bdada683d17ca04b1842548821ff0eb6c6/htdocs/filefunc.inc.php#L334-L370","documentation":"filefunc.inc.php performs an early CSRF referrer check for state-changing requests (POST/PUT/etc.): HTTP_REFERER host must match the server's HTTP_HOST (or be empty/allowed). When a form is submitted from a page on another origin, $csrfattack is set, Dolibarr logs a warning, prints this message plus the proxy hint, and dies. It protects against cross-site form posts before the token check (MAIN_SECURITY_CSRF_WITH_TOKEN) later runs.","triggerScenarios":"POST request whose HTTP_REFERER points to a different host/port/scheme than the served URL — e.g. form submitted from another domain, or behind a proxy/CDN that rewrites Host or strips/alters Referer headers.","commonSituations":"Running Dolibarr behind a reverse proxy with URL rewriting where X-Forwarded-Host/Referer are not propagated; accessing the server by two different names (localhost vs domain) and the browser sends the other one; embedded forms/iframe integrations from another origin; browser extensions stripping referers.","solutions":["Ensure all HTTP headers are propagated through the proxy: set proxy_set_header Host $host; X-Forwarded-Host, X-Forwarded-Proto and appropriate Referrer-Policy, and make dolibarr_main_url_root match the externally visible URL.","Access Dolibarr using the exact same host name that appears in dolibarr_main_url_root / HTTP_HOST.","For special trusted setups only, add $dolibarr_nocsrfcheck=1 to conf.php to skip this check (reduces security — the token check remains only if MAIN_SECURITY_CSRF_WITH_TOKEN is enabled).","Update Dolibarr: newer versions handle proxies better and allow configuring accepted referrers.","If a third-party site posts to your Dolibarr, embed the form in Dolibarr or use the REST API with tokens instead of cross-origin form posts."],"exampleFix":"// before (nginx)\nlocation / { proxy_pass http://backend; }\n// after\nlocation / {\n    proxy_pass http://backend;\n    proxy_set_header Host $host;\n    proxy_set_header X-Forwarded-Host $host;\n    proxy_set_header X-Forwarded-Proto $scheme;\n    proxy_set_header Referer $http_referer;\n}","handlingStrategy":"fallback","validationCode":"// before submitting a form programmatically, ensure Referer matches the target host\nif (!str_starts_with($referer, 'https://doli.example.com/')) {\n    $referer = 'https://doli.example.com/htdocs/'; // set explicitly in the HTTP client\n}","typeGuard":"function refererMatchesHost(?string $referer, string $host): bool {\n    if ($referer === null || $referer === '') return true; // empty referer is allowed\n    return parse_url($referer, PHP_URL_HOST) === $host;\n}","tryCatchPattern":"// CSRF refusal ends with die => client sees truncated 200 body, detect by content\n$resp = $client->post($url, ['headers' => ['Referer' => $baseUrl.'/'], 'form_params' => $data]);\nif (str_contains($resp->getBody(), 'refused by CSRF protection')) {\n    // fix Referer/Host headers or configure the proxy, then retry\n}","preventionTips":["Always send an explicit Referer header matching the Dolibarr host when scripting form posts.","Configure reverse proxies to forward Host, X-Forwarded-Host and X-Forwarded-Proto.","Keep dolibarr_main_url_root identical to the public hostname users browse.","Avoid cross-origin form posts; use the REST API with tokens.","Only use $dolibarr_nocsrfcheck=1 for trusted internal setups, and document it."],"tags":["csrf","security","proxy","referrer"],"backgroundTag":"permission-denied","analyzedSha":"598aa4bdada683d17ca04b1842548821ff0eb6c6","analyzedAt":"2026-09-14T11:12:15.309Z","contentChangedAt":"2026-09-14T11:12:15.309Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}