Say the attacker owns a domain, attacker.com. Attacker wants to create a webpage on it to make a CSRF attack toward the victim.com server. The targeted page is victim.com/target.php which only accepts POST request, with a specific Accept header of, say, application/x-foo.

With such setup, I couldn't find a way for the attacker to craft a CSRF page:

  • If attacker does a <form action="victim.com/target.php" method="POST"> then I see no way for the attacker to set the Accept header and so the target.php file won't do any action
  • If the attacker makes a const xhr = new XMLHttpRequest(); xhr.setRequestHeader('Accept', 'application/x-foo'); then the browser preflights a CORS request OPTIONS to the target.php which responds nothing (because it's not POST) and so the browser won't make the CSRF request
  • The attacker cannot use a URL-based CSRF (like victim.com/target.php?param=xyz) becausse it's not POST either
  • Attacker does not own the victim.com server so they cannot put a non-cross-domain XHR inb there

Even tho I cannot find an exploit, it feels very unlikely to me that a simple check such as ($_SERVER['HTTP_ACCEPT'] !== 'application/x-foo') in the victim's webpage is enough to prevent any CSRF attack... still, I see no exploit?!

It seems very close to OWASP's custom header CSRF prevention but Accept is not custom: does this change a thing?

  • See the note regarding additional restrictions to Accept in some browsers in the documentation and make sure you use a browser which is not affected by these restrictions. Jul 22, 2019 at 18:20

1 Answer 1


According to the documentation[1], the accept header is a "CORS-safelisted request header" and therefore should not trigger a preflight. Thus, checking for that header (unless the server requires an invalid character in it) should not be sufficient to prevent CSRF.

Non-simple requests get pre-flighted, but "simple" (safelisted) ones do not. As long as your server doesn't send back CORS response headers that permit the preflighted CORS request to proceed, and there's no way to make a valid simple request, that works.

[1] Thank you for the link, @Steffen Ullrich

  • Indeed, that surprised me but the test case I was studying got a typo I didn't notice at first look: ` xhrPost.setRequestHeader('Content-Type', 'application/x-www-from-urlencoded');` "from"... So you're right, the xhrPost.setRequestHeader('Accept', 'application/foo'); wasn't involved, and thanks for the resources links
    – Xenos
    Jul 23, 2019 at 13:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .