GeneratorsCorePlugin Header

Plugin Header Generator

Fourteen header fields, a guard clause, path constants and — if you want it — a singleton bootstrap. The file every plugin starts from, written properly once.

acme-toolkit.php
Saved just now
How the Plugins screen reads
Acme ToolkitDeactivate | Settings
Editorial tools for the Acme site: briefs, review workflow and a weekly digest.
Version 1.0.0 | By GrowQuest | Requires PHP 7.4
Identity
79 characters — the Plugins screen truncates around 140.
Requirements and licence
What else goes in the file
Domain Path
Points core at /languages for your own translation files.
load_plugin_textdomain()
Loads the .mo files you ship, which core does not do for you.
Bootstrap class
A single-instance entry object with includes() and hooks().
Network only
Network: true — activatable network-wide on multisite only.
Save as acme-toolkit/acme-toolkit.php — the folder name must match the text domain.
<?php
/**
 * Plugin Name:        Acme Toolkit
 * Description:        Editorial tools for the Acme site: briefs, review workflow and a weekly digest.
 * Version:            1.0.0
 * Requires at least:  6.0
 * Tested up to:       6.8
 * Requires PHP:       7.4
 * Author:             GrowQuest
 * Author URI:         https://growquest.io
 * License:            GPL-2.0-or-later
 * License URI:        https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:        acme-toolkit
 * Domain Path:        /languages
 * Update URI:         false
 *
 * Generated with WP CodeKit — powered by GrowQuest (https://growquest.io).
 *
 * @package Acme_Toolkit
 */

defined( 'ABSPATH' ) || exit;

// Fail loudly here rather than fatally later.
if ( version_compare( PHP_VERSION, '7.4', '<' ) ) {
	return;
}

define( 'ACME_TOOLKIT_VERSION',   '1.0.0' );
define( 'ACME_TOOLKIT_FILE',      __FILE__ );
define( 'ACME_TOOLKIT_PATH',      plugin_dir_path( __FILE__ ) );
define( 'ACME_TOOLKIT_URL',       plugin_dir_url( __FILE__ ) );
define( 'ACME_TOOLKIT_BASENAME',  plugin_basename( __FILE__ ) );

/**
 * Load translations.
 *
 * Since WordPress 4.6 core loads directory-hosted translations for you;
 * this is for the ones you ship yourself.
 */
function acme_toolkit_load_textdomain() {
	load_plugin_textdomain( 'acme-toolkit', false, dirname( ACME_TOOLKIT_BASENAME ) . '/languages' );
}
add_action( 'init', 'acme_toolkit_load_textdomain' );

/**
 * The plugin, as one object.
 */
final class Acme_Toolkit {

	/**
	 * The single instance.
	 *
	 * @var Acme_Toolkit|null
	 */
	private static $instance = null;

	/**
	 * Get the instance, creating it on first call.
	 *
	 * @return Acme_Toolkit
	 */
	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
			self::$instance->boot();
		}

		return self::$instance;
	}

	/**
	 * Load the pieces and wire the hooks.
	 */
	private function boot() {
		$this->includes();
		$this->hooks();
	}

	/**
	 * Require the plugin’s own files.
	 */
	private function includes() {
		require_once ACME_TOOLKIT_PATH . 'includes/class-settings.php';
		require_once ACME_TOOLKIT_PATH . 'includes/class-assets.php';
	}

	/**
	 * Everything this plugin hooks into.
	 */
	private function hooks() {
		add_action( 'init', array( $this, 'register' ) );
	}

	/**
	 * Registration that belongs on init.
	 */
	public function register() {
		// Post types, taxonomies and shortcodes go here.
	}
}

Acme_Toolkit::instance();

About this generator

Plugin Header Generator Online

The WordPress plugin header generator that writes the whole first file, not just the comment block. Fourteen aligned header fields, the defined( 'ABSPATH' ) || exit; guard, a PHP version check that returns instead of fatalling, the five path constants every plugin ends up needing, and — if you want it — a singleton bootstrap class to hang the rest of the plugin from.

A live mock-up of the Plugins screen shows how your row will actually read: the name, the description, the version, the author and the Requires PHP note. Switch the output and the same fields seed a starter readme.txt. Free, no account, and everything is generated in the browser.

Reviewed Output tested on WordPress 6.8 · PHP 8.3

Why a generated plugin header beats copying the one from your last project

The header is parsed from the first 8 kB of the main file, and every field except Plugin Name fails silently when it is wrong. A text domain that does not match the folder means translations never load. A version that is not version_compare()-able means updates behave unpredictably. A missing Update URI means wordpress.org can offer someone else's plugin as an update for yours.

Every field, aligned and in order

Plugin Name, Plugin URI, Description, Version, Requires at least, Tested up to, Requires PHP, Author, Author URI, License, License URI, Text Domain, Domain Path, Update URI and Network — written in the order WordPress documents, with the values column-aligned.

Slug and text domain kept in sync

The text domain must equal the plugin folder name or your .mo files never load, so a mismatch is an error with a one-click fix. An unsafe folder slug — capitals, spaces, underscores — is flagged the same way.

Update URI, for plugins that are not in the directory

Since WordPress 5.8, setting Update URI to false or to a domain you control stops wordpress.org offering an update for a directory plugin whose slug happens to match yours. The generator recommends it, and explains when the opposite is correct.

Checks that mirror how the Plugins screen behaves

A description over 140 characters is flagged because the list truncates it, a version that version_compare() cannot read is an error, a missing Requires PHP is a warning, and a proprietary licence is called out as incompatible with hosting on wordpress.org.

A bootstrap you can actually build on

Optional constants for _VERSION, _FILE, _PATH, _URL and _BASENAME, a load_plugin_textdomain() call for translations you ship yourself, and a final singleton class with instance(), includes() and hooks() so the main file stays a header and a require.

Two files from one form

The same fields produce a starter readme.txt with the directory header, and the Structure tab shows a folder layout — includes/, admin/, languages/, uninstall.php — that survives the plugin growing past one file.

How does the Plugin Header Generator work?

Four steps to a main plugin file that WordPress will list, activate and update correctly.

  1. 01

    Name the plugin

    Plugin name, folder slug and the one-line description the Plugins screen shows. The preview above the form updates as you type.

  2. 02

    Set compatibility

    Version, Requires at least, Tested up to and Requires PHP. These are what stop an unsuitable site activating the plugin, and what the directory reads for its compatibility notice.

  3. 03

    Add the metadata

    Author and URIs, licence, text domain, domain path and Update URI. Toggle the translation loader, the bootstrap class and, for multisite, the network-only flag.

  4. 04

    Export both files

    Clear the Checks tab, download the main file as your-slug/your-slug.php, then switch the output mode to take the seeded readme.txt with it.

Worked example — the header and guard for a GPL plugin

The top of the generated main file. Constants, the translation loader and the bootstrap class follow underneath it.

<?php
/**
 * Plugin Name:        Acme Toolkit
 * Description:        Editorial tools for the Acme site: briefs, review workflow and a weekly digest.
 * Version:            1.0.0
 * Requires at least:  6.0
 * Tested up to:       6.8
 * Requires PHP:       7.4
 * Author:             GrowQuest
 * Author URI:         https://growquest.io
 * License:            GPL-2.0-or-later
 * License URI:        https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:        acme-toolkit
 * Domain Path:        /languages
 * Update URI:         false
 *
 * @package Acme_Toolkit
 */

defined( 'ABSPATH' ) || exit;

// Fail loudly here rather than fatally later.
if ( version_compare( PHP_VERSION, '7.4', '<' ) ) {
	return;
}

The Requires PHP header stops WordPress activating the plugin on an older PHP version, but the runtime check matters too: a site that was already running the plugin can be moved to an older PHP install, and returning early is far better than a parse error on a syntax the host does not support.

Plugin headers — frequently asked questions

What developers ask when WordPress will not list, activate or update a plugin correctly.

Which plugin header fields are required?

Only Plugin Name. WordPress will list a file as a plugin on that alone. Everything else is optional and fails quietly: no description leaves a blank line on the Plugins screen, no Requires PHP means an old host can activate the plugin and fatal, and no text domain means translations are never loaded.

Why is my plugin not showing up in the Plugins list?

WordPress only reads the first 8 kB of PHP files directly inside wp-content/plugins/ and one level down, so the header has to be in the main file, near the top, in a /** … */ block. A stray character before <?php, a header buried below 8 kB of code, or a file nested two folders deep will all keep it hidden. Note the opposite problem too: a second Plugin Name: in any other file in the folder makes WordPress list that file as a separate plugin.

Does the text domain have to match the plugin folder name?

Yes. Since WordPress 4.6 core loads translations automatically from wp-content/languages/plugins/ using the folder name, so a text domain that differs from the folder simply never resolves. If you also ship your own .mo files, set Domain Path to /languages and call load_plugin_textdomain() for those.

What is the Update URI header for?

Added in WordPress 5.8, it controls where update checks for your plugin may come from. Set it to false for a private or client plugin and wordpress.org will never offer an update for it, even if a directory plugin shares the slug — which is otherwise a real way to have a stranger's code installed over yours. Set it to a domain you control if you are serving your own updates.

How do I make my plugin require another plugin?

WordPress 6.5 added the Requires Plugins: header. It takes a comma-separated list of wordpress.org plugin slugs, such as woocommerce, and WordPress will not let the plugin be activated until those dependencies are installed and active. It only accepts directory slugs, not paths like woocommerce/woocommerce.php, and it is not generated here — add the line to the header block yourself if you need it.

Can a WordPress plugin be a single file?

Yes. A single .php file with a valid header, placed directly in wp-content/plugins/, is a complete plugin. Once it grows past a few hundred lines a folder with the main file, an includes/ directory and an uninstall.php is far easier to live with — and a folder is required if you ever want to publish it to the directory, since it needs a readme.txt beside the code.

Where do I paste the code from the Plugin Header generator?

You have three good options and one bad one. Best is a small site-specific plugin — the generator can output one for you, and it keeps working when you change theme. A code-snippets plugin is fine too. A child theme's functions.php works but ties the code to that theme. Never edit the parent theme: the next update overwrites it and your code is gone.

Can I use the generated code in client work or a commercial plugin?

Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.

The other Core generators in the library, plus the WordPress tools most often used alongside this one.