Skip to content
  1. Blog

WordPress File Headers Have Overstayed Their Welcome

Ben Word Ben Word

In 2014, I commented on WordPress Trac ticket #24152 suggesting it was time to revisit the non-standard file headers specification. Scott reopened the ticket with a detailed case for replacing them with JSON.

That was eleven years ago. The ticket is still open, and we still use the same file headers in WordPress plugins and themes.

What are file headers?

Every WordPress theme must include a style.css file with metadata in CSS comments:

/*
Theme Name: My Theme
Theme URI: https://example.com
Description: A theme description
Author: Developer Name
Author URI: https://example.com
Version: 1.0.0
Requires at least: 6.0
Requires PHP: 8.2
License: GPL-2.0-or-later
Text Domain: my-theme
Tags: blog, custom-colors
*/

Plugins do the same thing in their main PHP file:

/*
Plugin Name: My Plugin
Plugin URI: https://example.com
Description: A plugin description
Author: Developer Name
Author URI: https://example.com
Version: 1.0.0
Requires at least: 6.0
Requires PHP: 8.2
License: GPL-2.0-or-later
Text Domain: my-plugin
*/

WordPress parses these with get_file_data(), a function that reads the first 8 KB of a file and runs regular expressions against it. This is the foundation that the entire plugin and theme system is built on.

It’s a custom, non-standard specification that exists nowhere else in the software world.

Why this is a problem

It’s slow

As Scott benchmarked in 2014, parsing file headers with regular expressions is nearly 10x slower than json_decode:

Method10,000 iterations
get_file_data1.98s
json_decode218ms
parse_ini_file482ms

This matters when WordPress is scanning dozens of plugins and themes on every admin page load.

It gets worse. WordPress doesn’t know which PHP file contains a plugin’s header. When get_plugins() is called, it opens every .php file in each plugin directory, reads the first 8 KB, and runs regex against it until it finds a valid header. The results are cached, but the cache gets invalidated on plugin changes and doesn’t persist without an object cache. With a plugin.json, WordPress would know exactly where to look.

It’s error prone

The regex-based parsing has had bugs with whitespace handling. Here are all the variations that have caused problems, from #19854 and #15193:

 Original Theme Name
    Original Theme Name
        Theme Name
      Theme Name
    Theme Name
  Theme Name
 *   Theme Name
 * Theme Name
 *Theme Name
 Theme Name
/* Theme Name
/*    Theme Name
/*Theme Name
* Theme Name
/*Theme Name
        Theme Name
    Theme Name
Theme Name

JSON doesn’t have this problem. A key is a key.

It’s illogical

Why is a theme’s name, version, and license in the comments of a stylesheet? Why is a plugin’s metadata in comments in a PHP file?

Theme metadata has nothing to do with CSS. Plugin metadata has nothing to do with PHP execution. Metadata belongs in a metadata file. This is an arbitrary convention that WordPress invented. No other platform does this.

And the metadata isn’t even in one place. For plugins, it’s split between the PHP file header and readme.txt. Two different files, two different formats, for the same package. Themes have the same problem with style.css and readme.txt. A single JSON file replaces all of it.

It’s non-standard

The File Header specification is a list of 13 different things for plugins and 14 for themes. There’s no formal spec, no schema, no validation tools. It’s parsed by reading raw file bytes and running regex against comment blocks.

JSON has a formal specification, built-in parsers in every language, schema validation, and tooling in every editor.

The proposal

For themes: use theme.json

WordPress already requires theme.json for block themes. It’s already a JSON file that WordPress already parses. But theme metadata (name, version, author) is still stuck in style.css comments.

Think about that for a second. WordPress parses JSON for theme colors, fonts, spacing, layout, templates, and patterns. But for the theme’s own name and version? CSS comments.

Add a metadata property to theme.json:

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 3,
  "metadata": {
    "name": "My Theme",
    "uri": "https://example.com",
    "description": "A theme description",
    "author": "Developer Name",
    "authorUri": "https://example.com",
    "version": "1.0.0",
    "license": "GPL-2.0-or-later",
    "textDomain": "my-theme",
    "tags": ["blog", "custom-colors"],
    "requires": {
      "wordpress": "6.0",
      "php": "8.2"
    }
  },
  "settings": {
  }
}

The file already exists. The parsing infrastructure already exists. Many classic themes are already hybrid themes with a theme.json file. They’d get this for free.

For plugins: introduce plugin.json

Plugins don’t have an equivalent to theme.json, so they need a new file:

{
  "$schema": "https://schemas.wp.org/trunk/plugin.json",
  "name": "My Plugin",
  "uri": "https://example.com",
  "description": "A plugin description",
  "author": "Developer Name",
  "authorUri": "https://example.com",
  "version": "1.0.0",
  "license": "GPL-2.0-or-later",
  "textDomain": "my-plugin",
  "requires": {
    "wordpress": "6.0",
    "php": "8.2",
    "plugins": ["woocommerce", "jetpack"]
  }
}

Notice the requires.plugins field. WordPress currently handles plugin dependencies with a comma-separated string in a comment header (Requires Plugins: woocommerce, jetpack). A JSON array is the obvious way to represent a list of things.

Why not composer.json or package.json?

When this was discussed in 2014, TJNowell suggested using composer.json which is a reasonable idea. Composer is the standard dependency manager for PHP, and composer.json already has provisions for defining WordPress themes and plugins.

But WordPress doesn’t support Composer. There’s no native Composer integration in core, and no indication that’s changing. We’ve been using Composer with WordPress since 2013 through Bedrock, but that’s a project-level tool. It doesn’t solve the problem for individual themes and plugins that need to identify themselves to WordPress.

package.json has the same problem in the other direction. Not every theme or plugin uses Node. Requiring a Node package manifest for a PHP project that might not have any JavaScript build step doesn’t make sense.

The solution needs to be WordPress-native. theme.json already is. plugin.json would follow the same pattern.

Backwards compatibility

This is fully backwards compatible:

  1. If metadata exists in theme.json, use it. Otherwise, fall back to style.css headers.
  2. If plugin.json exists, use it. Otherwise, fall back to PHP file headers.

wp_get_theme() and get_plugin_data() handle the abstraction. No existing theme or plugin breaks. Developers migrate on their own timeline.

The file headers don’t even need to be formally deprecated right away. Give developers a better option and let adoption happen naturally, the same way theme.json itself was adopted for block theme settings.

Thirteen years and counting

WordPress Trac #24152 was opened in 2013. It was closed as “wontfix” because file headers are simple and JSON introduces unnecessary complexity. We disagreed then. Scott made the case in detail. Others have continued to argue for it in the years since.

Then WordPress introduced theme.json. A JSON configuration file that defines theme settings and styles. The file is right there. Just put the metadata in it.

Every argument against this has been answered. Performance is better. The format is standardized. The file already exists for themes. Backwards compatibility is trivial.

WordPress already chose JSON. It just forgot to finish the job.

Discuss this post on Roots Discourse

About the author

Ben Word

Ben Word has been creating WordPress sites since 2004. He loves dogs, climbing, and yoga, and is passionate about helping people build awesome things on the web.

Subscribe for updates

Join over 8,000 subscribers on our newsletter to get the latest Roots updates and tips on building better WordPress sites

Looking for WordPress plugin recommendations, the newest modern WordPress projects, and general web development tips and articles?

One last step! Check your email for a verification link.