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:

  1. Connect to MySQL — already done by WordPress, so this one is free.
  2. SELECT the active snippets, usually every row with its code column.
  3. Unserialize or decode the settings stored alongside each one.
  4. 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 requestDatabase pluginFluentSnippets
Queries added per front-end request1 or more0
Extra database tables1–20
Rows read before the main queryEvery active snippetNone
Work on a cache missThe full query againUnchanged
Where the code is compiled fromA string, at runtimeA 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.

The FluentSnippets admin list showing snippets grouped by type, each with its run location and status.
The admin does use the REST API and the database, like any admin screen. Visitors never touch either.

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.

NOTE

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.

Enjoyed this? Get the next one by email.

New articles, practical snippets and feature walkthroughs — sent when there's something worth reading, not on a schedule.

No spam, no selling. Unsubscribe anytime.

Custom code, without the database.

FluentSnippets is free on WordPress.org. Zero queries, no lock-in.