{"id":906,"date":"2021-09-25T11:11:44","date_gmt":"2021-09-25T10:11:44","guid":{"rendered":"https:\/\/editjournal.redakt.eu\/faxmodem\/?p=906"},"modified":"2026-06-29T12:34:35","modified_gmt":"2026-06-29T11:34:35","slug":"missing-incorrect-csrf-cookie-type","status":"publish","type":"post","link":"https:\/\/editjournal.redakt.eu\/faxmodem\/blog\/development\/cakephp\/missing-incorrect-csrf-cookie-type\/","title":{"rendered":"CakePHP 4 &#8220;Missing or incorrect CSRF cookie type&#8221;"},"content":{"rendered":"<h1>The Problem<\/h1>\n<p>You must be making a non-GET (e.g. POST) request to an endpoint protected by <code>Cake\\Http\\Middleware\\CsrfProtectionMiddleware<\/code>. Most likely it is enabled on the application level <a href=\"https:\/\/github.com\/cakephp\/app\/blob\/ea27790a4fb399e7490c308d053ef874a72432c7\/src\/Application.php#L103\">in your <code>src\/Application.php<\/code><\/a>.<\/p>\n<p><strong>Previously<\/strong>, with <code>CsrfComponent<\/code> in CakePHP 3, you <a href=\"https:\/\/book.cakephp.org\/3\/en\/controllers\/components\/security.html#disabling-security-component-for-specific-actions\">could have <code>unlockedActions<\/code><\/a>. In case of this new middleware, you have two ways to approach this:<\/p>\n<ol>\n<li>Add exception when adding the middleware to the queue.<\/li>\n<li>Disable the middleware completely, and enable it for the parts of your application where you need it.<\/li>\n<\/ol>\n<h1>1. 'Adding an exception' way<\/h1>\n<p>Quick links to the documentation:<\/p>\n<ul>\n<li>CakePHP 3: <a href=\"https:\/\/book.cakephp.org\/3\/en\/controllers\/components\/security.html#disabling-security-component-for-specific-actions\">https:\/\/book.cakephp.org\/3\/en\/controllers\/components\/security.html#disabling-security-component-for-specific-actions<\/a><\/li>\n<li>CakePHP 4: <a href=\"https:\/\/book.cakephp.org\/4\/en\/security\/csrf.html#skipping-csrf-checks-for-specific-actions\">https:\/\/book.cakephp.org\/4\/en\/security\/csrf.html#skipping-csrf-checks-for-specific-actions<\/a><\/li>\n<li>CakePHP 5: <a href=\"https:\/\/book.cakephp.org\/5\/en\/security\/csrf.html#skipping-csrf-checks-for-specific-actions\">https:\/\/book.cakephp.org\/5\/en\/security\/csrf.html#skipping-csrf-checks-for-specific-actions<\/a><\/li>\n<\/ul>\n<p>Go to <code>src\/Application.php<\/code> and find the <code>middleware()<\/code> method. By default, you will see a chain of <code>$middlewareQueue-&gt;add()<\/code> calls. The last one will add <code>new CsrfProtectionMiddleware<\/code>. You will have to break the chain, and manipulate your instance of this <code>CsrfProtectionMiddleware<\/code> by adding an exception, before adding it to the queue.<\/p>\n<p>So your <code>middleware()<\/code> method will probably look like this.<\/p>\n<pre><code>$middlewareQueue\n    \/\/ A bunch of standard add() calls go here\n    -&gt;add(\u2026)\n    -&gt;add(\u2026)\n    \/\/ Keep routing added before before CSRF!\n    -&gt;add(new RoutingMiddleware($this))\n    \/\/ Now, delete the last add() containing new CsrfProtectionMiddleware\n    ;\n\n\/\/ Initialise CsrfProtectionMiddleware as separate variable we can work with\n$csrf = new CsrfProtectionMiddleware([\n    &#039;httponly&#039; =&gt; true,\n]);\n\/\/ Token check will be skipped when callback returns `true`.\n$csrf-&gt;skipCheckCallback(function ($request) {\n    \/\/ Skip token check for API URLs.\n    if ($request-&gt;getParam(&#039;prefix&#039;) === &#039;Api&#039;) {\n        return true;\n    }\n});\n\/\/ Ensure routing middleware has already been added to the queue\n$middlewareQueue-&gt;add($csrf);\n\nreturn $middlewareQueue;<\/code><\/pre>\n<h3>(For bonus points) If you need to do this from a plugin<\/h3>\n<p>Imagine you're working on a plugin that needs to add a prefix, and that prefix has to be exempt from the CSRF checks. In your <code>ExamplePlugin\/src\/ExamplePlugin.php<\/code>:<\/p>\n<pre><code>public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue\n{\n    \/\/ Loop through queue directly using Iterator capabilities of MiddlewareQueue\n    foreach ($middlewareQueue as $layer) {\n        \/\/ Skip right away if this is not a CSRF layer\n        if (!($layer instanceof \\Cake\\Http\\Middleware\\CsrfProtectionMiddleware)) {\n            continue;\n        }\n        \/\/ Add the callback\n        $layer-&gt;skipCheckCallback(function ($request) {\n            \/\/ Adjust the condition to your needs; I had to allow specific prefix in current plugin\n            if ($request-&gt;getParam(&#039;plugin&#039;) === $this-&gt;getName() &amp;&amp; $request-&gt;getParam(&#039;prefix&#039;) === &#039;Api&#039;) {\n                return true;\n            }\n        });\n        \/\/ Once we found the CSRF layer, stop the loop as there&#039;s no need to continue\n        break;\n    }\n    return $middlewareQueue;\n}<\/code><\/pre>\n<p><strong>Note<\/strong>: prefix you're comparing to needs to match case in which you defined it. If in <code>ExamplePlugin\/config\/routes.php<\/code> you defined <code>$routes-&gt;prefix(&#039;Api&#039;, ...)<\/code>, then you do <code>$request-&gt;getParam(&#039;prefix&#039;) === &#039;Api&#039;<\/code>, not lowercase <code>api<\/code>.<\/p>\n<p>Further reading and context:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/cakephp\/cakephp\/issues\/17984\">https:\/\/github.com\/cakephp\/cakephp\/issues\/17984<\/a><\/li>\n<\/ul>\n<p>Documentation:<\/p>\n<ul>\n<li>CakePHP 4 and 5: <a href=\"https:\/\/book.cakephp.org\/5\/en\/controllers\/middleware.html#adding-middleware-from-plugins\">https:\/\/book.cakephp.org\/5\/en\/controllers\/middleware.html#adding-middleware-from-plugins<\/a><\/li>\n<li>Same as the above, but for CakePHP 3: <a href=\"https:\/\/book.cakephp.org\/3\/en\/controllers\/middleware.html#adding-middleware-from-plugins\">https:\/\/book.cakephp.org\/3\/en\/controllers\/middleware.html#adding-middleware-from-plugins<\/a><\/li>\n<li><code>\\Cake\\Http\\MiddlewareQueue<\/code> docs: <a href=\"https:\/\/api.cakephp.org\/5.0\/class-Cake.Http.MiddlewareQueue.html\">CakePHP 5<\/a>, <a href=\"https:\/\/api.cakephp.org\/4.0\/class-Cake.Http.MiddlewareQueue.html\">CakePHP 4<\/a>, <a href=\"https:\/\/api.cakephp.org\/3.10\/class-Cake.Http.MiddlewareQueue.html\">CakePHP 3<\/a><\/li>\n<\/ul>\n<h1>2. 'Disable-Reenable' way<\/h1>\n<h3>Disable CSRF<\/h3>\n<p>To disable the application-level protection completely, remove it from <code>Application::middleware<\/code> in <code>src\/Application.php<\/code>.<\/p>\n<pre><code>-    \/\/ Cross Site Request Forgery (CSRF) Protection Middleware\n-    \/\/ https:\/\/book.cakephp.org\/4\/en\/controllers\/middleware.html#cross-site-request-forgery-csrf-middleware\n-    ->add(new CsrfProtectionMiddleware([\n-     'httponly' => true,\n-    ]));\n+    ;<\/code><\/pre>\n<p>(Do not forget to put back the trailing <code>;<\/code>)<\/p>\n<h3>Re-enable for specific prefixes<\/h3>\n<ol>\n<li>\n<p>Create a new method called <code>routes<\/code> in <code>src\/Application.php<\/code> with the following contents. You can add it last.<\/p>\n<pre><code>public function routes(\\Cake\\Routing\\RouteBuilder $routes) : void\n{\n    \/\/ Cross Site Request Forgery (CSRF) Protection Middleware\n    \/\/ https:\/\/book.cakephp.org\/4\/en\/controllers\/middleware.html#cross-site-request-forgery-csrf-middleware\n    $options = [\n     'httponly' => true,\n    ];\n    $routes->registerMiddleware('csrf', new CsrfProtectionMiddleware($options));\n    parent::routes($routes);\n}<\/code><\/pre>\n<p><code>CsrfProtectionMiddleware<\/code> is now registered and available under <code>csrf<\/code> alias.<\/p>\n<\/li>\n<li>\n<p>Enable the above <code>csrf<\/code> for specific routes and prefixes. In <code>config\/routes.php<\/code>:<\/p>\n<pre><code>$routes->scope('\/', function (RouteBuilder $builder) {\n    $builder->applyMiddleware('csrf');\n    \/\/ \u2026\n});\n\n$routes->prefix('Admin', function (RouteBuilder $routes) {\n    $routes->applyMiddleware('csrf');\n    $routes->fallbacks(DashedRoute::class);\n});<\/code><\/pre>\n<\/li>\n<li>\n<p>You can skip applying the middleware for the prefixes where you do not need it, e.g. <code>\/api<\/code>:<\/p>\n<pre><code>$routes->prefix('Api', function (RouteBuilder $routes) {\n    $routes->setExtensions(['json']);\n    $routes->fallbacks(DashedRoute::class);\n});<\/code><\/pre>\n<\/li>\n<li>\n<p>As always, <a href=\"https:\/\/book.cakephp.org\/4\/en\/console-commands\/cache.html\">clear the cache<\/a> before testing:<\/p>\n<pre><code>bin\/cake cache clear _cake_routes_<\/code><\/pre>\n<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Reconfiguring `CsrfProtectionMiddleware` to allow POST requests to specific scopes. The article offers two ways to navigate around the &#8220;Missing or incorrect CSRF cookie type&#8221; error.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1047],"tags":[715,737],"class_list":["post-906","post","type-post","status-publish","format-standard","hentry","category-cakephp","tag-cakephp","tag-middleware","entry"],"_links":{"self":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/906","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/comments?post=906"}],"version-history":[{"count":11,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/906\/revisions"}],"predecessor-version":[{"id":1832,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/906\/revisions\/1832"}],"wp:attachment":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/media?parent=906"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/categories?post=906"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/tags?post=906"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}