File-based vs. database snippets: a real performance breakdown
Where the work actually goes on a page load, why the query count matters more than the milliseconds, and what zero buys you.
Every snippet plugin does the same job: hold some PHP you wrote and run it at the right moment. The interesting difference is where the code lives while it waits. Almost all of them keep it in a database table. FluentSnippets keeps it in files.
That sounds like an implementation detail. It is not, and this post walks through exactly what it costs on a normal page load.
The database path
A database-backed snippet plugin has to answer one question on every single request, before WordPress has done anything interesting: which snippets should run?
The only place that answer lives is a table, so the sequence is:
- Connect to MySQL — already done by WordPress, so this one is free.
SELECTthe active snippets, usually every row with its code column.- Unserialize or decode the settings stored alongside each one.
eval()the code, or write it to a temp file and include that.
Steps 2 through 4 happen on the front end, for a logged-out visitor, on a page that may not use a single snippet. And because the query runs before the main query, it lands in the part of the request where nothing else is warm yet.
Object caching helps, and most of these plugins use it. But a cache you have to check is still a lookup, and on a site without persistent object caching — which is most shared hosting — the query runs every time.
The file path
FluentSnippets stores each snippet as an ordinary PHP file:
wp-content/fluent-snippet-storage/
├── index.php generated map of every snippet
├── .htaccess denies web access to the .php files
├── 1-google-analytics-4.php
├── 2-disable-comments-site-wide.php
└── 3-free-shipping-bar.php
Reading and parsing the header of every one of those files on every request would just move the cost
around, so the headers are parsed once, when you save, and written into a generated index.php that
returns a plain array:
return array (
'published' => array (
'1-google-analytics-4.php' => array (
'type' => 'php_content',
'run_at' => 'wp_head',
'priority' => 10,
),
),
'hooks' => array ( 'wp_head' => array( '1-google-analytics-4.php' ) ),
);
This is the part that matters. index.php is a PHP file, so PHP’s opcode cache holds its compiled
form in memory. After the first request, reading it costs approximately nothing — no I/O, no parse,
no query. Running a snippet is then an include() of a file opcache is also holding.
The runtime path never touches the database. No tables are created, nothing is stored in options, and no query runs on the front end.
Counting the work
You do not need a profiler to compare these. Count what each one has to do before it knows which snippets to run:
| Cost per request | Database plugin | FluentSnippets |
|---|---|---|
| Queries added per front-end request | 1 or more | 0 |
| Extra database tables | 1–2 | 0 |
| Rows read before the main query | Every active snippet | None |
| Work on a cache miss | The full query again | Unchanged |
| Where the code is compiled from | A string, at runtime | A file, held by opcache |
Measure it on your own site with Query Monitor and you will see a handful of milliseconds either way. That is the honest answer: on a healthy server, this is not the difference between a fast page and a slow one, and nobody should switch plugins over a few milliseconds.
The column worth reading is the first row: zero. Not “one cached query”, not “one query on a cache miss” — none, on every request, forever.
Why zero is a different kind of number
Small numbers scale badly in ways that zero does not.
It survives cache misses. A logged-in editor, a ?ver= cache buster, a WooCommerce cart page —
all of the requests that skip your page cache are exactly the requests where per-request work adds
up. Zero queries stays zero on all of them.
It survives a busy database. Query time is not a constant, it is a function of what else the server is doing. The handful of milliseconds you measure on an idle staging box is a different number under Black Friday load, which is precisely when you least want extra work in the critical path.
It shrinks the attack surface. Code stored in a table is code that arrives through SQL. Code
stored in a file, in a directory with an .htaccess denying access to .php, with an ABSPATH
guard at the top of every file, does not.
Files bring their own trade-off, and it is worth naming: your snippets now live in wp-content, so a migration tool that copies the database but not the files will leave them behind. Copy the storage directory with the rest of wp-content and you are fine.
The part that is not about speed
The reason we built it this way was never the milliseconds. It was that a file is a thing you can look at.
You can git diff a snippet. You can rsync a directory of them to staging. You can open one in an
editor and read exactly what is running, without a database client. And when you delete the plugin,
the files stay on disk — with standalone mode enabled, they keep running
without the plugin installed at all.
That is harder to benchmark than a query count, and it is the thing we would actually miss.
Start with how it works if you want the full request lifecycle, step by step.