{"id":11098,"date":"2026-09-26T02:07:55","date_gmt":"2026-09-26T02:07:55","guid":{"rendered":"https:\/\/schindlerengineering.com\/public\/?p=11098"},"modified":"2026-09-26T02:07:55","modified_gmt":"2026-09-26T02:07:55","slug":"5-51-seconds-to-0-25-three-changes-and-the-one-plugin-that-was-eating-the-site","status":"publish","type":"post","link":"https:\/\/schindlerengineering.com\/public\/2026\/09\/26\/5-51-seconds-to-0-25-three-changes-and-the-one-plugin-that-was-eating-the-site\/","title":{"rendered":"5.51 seconds to 0.25: three changes, and the one plugin that was eating the site"},"content":{"rendered":"<p>The site took <strong>5.5 seconds<\/strong> to answer. It now takes<br \/>\n<strong>0.25<\/strong>. Same host, same theme, same content. Here is the whole thing in<br \/>\nthree pictures.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/schindlerengineering.com\/public\/wp-content\/uploads\/2026\/09\/perf-before-after.png\" alt=\"Every page before and after\" style=\"max-width:100%;height:auto\" \/><\/p>\n<p>Every page. Red is before, green is after. The average went<br \/>\n<strong>5.51s &rarr; 0.25s<\/strong>, about <strong>22&times;<\/strong>.<\/p>\n<h2>What actually changed<\/h2>\n<p><img decoding=\"async\" src=\"https:\/\/schindlerengineering.com\/public\/wp-content\/uploads\/2026\/09\/perf-stages.png\" alt=\"The three changes\" style=\"max-width:100%;height:auto\" \/><\/p>\n<p>Only three settings moved:<\/p>\n<ul>\n<li><strong>Page cache was off.<\/strong> I had turned it off to debug something months ago<br \/>\nand never turned it back on.<\/li>\n<li><strong>Turning it on changed nothing.<\/strong> Bar two. That is the interesting one &mdash;<br \/>\nsee below.<\/li>\n<li><strong>One plugin was costing 4.59 seconds.<\/strong> See below that.<\/li>\n<\/ul>\n<h2>Gotcha #1: the cache was on and still not caching<\/h2>\n<p>Page cache enabled, engine set to Disk: Enhanced, and pages still took 5.35 seconds.<br \/>\nThe plugin was printing the reason in an HTML comment at the bottom of every page the<br \/>\nwhole time:<\/p>\n<pre>Page Caching using Disk: Enhanced (SSL caching disabled)<\/pre>\n<p>The site is HTTPS only. <em>Every single request<\/em> was an SSL request, so<br \/>\n<em>every single request<\/em> skipped the cache. One checkbox &mdash; &#8220;cache SSL (https)<br \/>\nrequests&#8221; &mdash; and the bar fell off a cliff.<\/p>\n<h2>Gotcha #2: find the plugin<\/h2>\n<p>Every page cost the same 5.4 seconds &mdash; the fat homepage and a nearly empty<br \/>\ncontact page alike. Flat cost like that is not content, it is something running on every<br \/>\nboot. So: switch off one plugin, measure, switch it back on. Nine times.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/schindlerengineering.com\/public\/wp-content\/uploads\/2026\/09\/perf-plugin-bisect.png\" alt=\"Per-plugin bisect\" style=\"max-width:100%;height:auto\" \/><\/p>\n<p>Eight plugins were free. <strong>wp-hide-post<\/strong> was <strong>4.59 seconds<\/strong>.<\/p>\n<p>Before touching it I checked what it was actually hiding: <strong>13 published posts,<br \/>\n13 reachable by paging the blog, 13 in the sitemap<\/strong>. It was hiding nothing. I<br \/>\ndeactivated it and checked again &mdash; the same 13, byte for byte. Nothing was exposed<br \/>\nand nothing vanished.<\/p>\n<h2>The part where I was wrong for an hour<\/h2>\n<p>Every attempt to save the cache settings came back <strong>403 Forbidden<\/strong>, and I<br \/>\ndecided it was the host&#8217;s firewall &mdash; the same one that blocks the REST API here. It<br \/>\nwas not. When I finally read the response body instead of the status code, it said:<\/p>\n<pre>The link you followed has expired.<\/pre>\n<p>That is WordPress, not a firewall. The settings page carries <em>three<\/em> hidden<br \/>\n<code>_wpnonce<\/code> fields and the last one is <strong>empty<\/strong>. PHP keeps the last<br \/>\nvalue of a repeated key, so the blank one overwrote the real security token on every<br \/>\nsubmit. My own form-filler was handing WordPress an empty nonce and WordPress was<br \/>\ncorrectly refusing it.<\/p>\n<p>I also managed to &#8220;prove&#8221; that no plugin mattered, because my timing script was getting<br \/>\na <strong>406<\/strong> from the firewall in 0.14 seconds and I had wrapped it in a bare<br \/>\n<code>except: pass<\/code>. A rejection looks exactly like a blazingly fast page if you only<br \/>\nmeasure the clock.<\/p>\n<p>Read the body. Not the status code. And never let a test swallow its own errors.<\/p>\n<hr>\n<p><em>Numbers measured with curl from a machine twenty miles from the server, two runs per<br \/>\npage, cold and warm. Nothing modelled.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The site took 5.5 seconds to answer. It now takes 0.25. Same host, same theme, same content. Here is the whole thing in three pictures. Every page. Red is before, green is after. The average went 5.51s &rarr; 0.25s, about 22&times;. What actually changed Only three settings moved: Page cache was off. I had turned &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/schindlerengineering.com\/public\/2026\/09\/26\/5-51-seconds-to-0-25-three-changes-and-the-one-plugin-that-was-eating-the-site\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;5.51 seconds to 0.25: three changes, and the one plugin that was eating the site&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-11098","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/posts\/11098","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/comments?post=11098"}],"version-history":[{"count":1,"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/posts\/11098\/revisions"}],"predecessor-version":[{"id":11099,"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/posts\/11098\/revisions\/11099"}],"wp:attachment":[{"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/media?parent=11098"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/categories?post=11098"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/schindlerengineering.com\/public\/wp-json\/wp\/v2\/tags?post=11098"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}