The first step in writing a plugin for WordPress comes before you run a single line of code: adding the right comment block (docblock) at the very top of the PHP file. Small as it looks, WordPress recognising your plugin, listing it with the right details on the Plugins screen and the translation system working all depend on those few lines. In this article we look at how the plugin header works, which fields are genuinely required, and the mistakes developers make most often.

How Does WordPress Recognise a File as a Plugin?

WordPress scans every PHP file in the wp-content/plugins/ directory looking for a standard docblock format. When a file contains a comment in that format, WordPress recognises it as an installed plugin and lists it on the Plugins screen in the admin panel. This scan does not concern itself with the rest of the file's code; it only reads the comment block at the top of the file.

The only technically required field in this block is Plugin Name. All the other headers — Plugin URI, Description, Version, Author, Author URI, License, License URI, Text Domain — are optional. When present, though, they automatically fill in the description, version number, author link and licence information on the Plugins screen; being optional does not mean they are unimportant, only that they are not needed for the file to be recognised as a plugin.

Why the Location of the Plugin File Matters

For the header to work it is not enough to write it in the right format; it also has to be in the right file. A small, single-file plugin can sit directly as a single file at wp-content/plugins/plugin-name.php. For a larger plugin made up of several files, the standard approach is to create its own folder: wp-content/plugins/plugin-name/plugin-name.php. The header docblock must be at the very top of that main file; if it is placed in a helper class file inside the plugin folder or in a file in a subdirectory, WordPress does not scan it and the plugin is not recognised.

Let's think about it with a concrete example. Suppose you are developing a plugin in the folder wp-content/plugins/site-backup/ with the files site-backup.php, includes/class-backup.php and assets/style.css. The header must only be at the very top of the site-backup.php file. If you accidentally put the same comment block at the top of includes/class-backup.php, WordPress never looks into that subdirectory while scanning the folder and the plugin does not appear on the Plugins screen at all — it does not give any error message either, it simply is not in the list.

Header Fields: Required and Optional

FieldRequired?What it does on the Plugins screen
Plugin NameYesThe only required field, the one that makes the file recognised as a plugin; it is the name shown in the list
Plugin URINoLinks to the plugin's information or support page
DescriptionNoThe description text shown on the plugin card
VersionNoShows the version number and is used as a reference for update tracking
AuthorNoLists the author's name on the Plugins screen
Author URINoThe link opened when the author's name is clicked
LicenseNoShows the licence type (e.g. GPL v2)
License URINoLinks to the full address of the licence text
Text DomainNoThe unique identifier that groups the translation strings; required for i18n

A Complete Code Example

The example below shows what a complete plugin header, and the basic security guard that follows it, should look like:

Why Is the ABSPATH Guard Necessary?

The line if ( ! defined( 'ABSPATH' ) ) { exit; }, which comes right after the file's header, prevents the file from being called directly through a URL outside the WordPress core. The ABSPATH constant is only defined when WordPress is fully loaded through wp-load.php. When an attacker or a bot tries to call the file directly from an address such as site.com/wp-content/plugins/plugin/file.php, that constant will not be defined, so the check kicks in and the script terminates without running.

The logic of this check is simple: as wp-load.php starts WordPress up, it loads the core files, the database connection and the general settings, and in the process defines the ABSPATH constant. In the normal flow — that is, when your plugin file is included by WordPress as a page loads — that constant is already defined and the check passes without trouble. But if the file is in a publicly accessible directory on the server and someone calls it straight from the browser address bar, WordPress is never involved, ABSPATH stays undefined and exit runs.

Text Domain and the Translation System

Text Domain is a unique identifier used to group a plugin's translation strings. This value must match exactly the slug passed to the load_plugin_textdomain() function (or, for plugins hosted on WordPress.org, the slug used by the automatically loaded translations). When the two values do not match, WordPress cannot work out which translation file to load; as a result the string translations (i18n) are never loaded and the interface always appears in English or in the source language. This mistake usually stays silent — it throws no PHP error, the translations simply do not work.

Common Mistakes

  • Plugin Name missing or left empty: even if every other field is written correctly, if there is no Plugin Name WordPress does not recognise the file as a plugin at all and does not list it on the Plugins screen.
  • A Text Domain mismatch: if the Text Domain value in the header does not match the slug in the load_plugin_textdomain() call or in the translation file name, the translations silently fail to work.
  • Forgetting the ABSPATH check: if this line is skipped, the file becomes callable directly from outside WordPress; that means leaving out a basic security practice.
  • Putting the header in the wrong file: this comment block must be at the very top of the plugin's main PHP file — usually at the path wp-content/plugins/plugin-name/plugin-name.php, or in that file itself in a single-file plugin. If it is placed in another file in a subdirectory, WordPress does not scan it.

Writing the header by hand leaves room for error, especially in matching the Text Domain with the file/slug. The tool below speeds this step up by generating all the required fields completely and in the correct format.