Hosting Planer

A Collection of Useful WordPress Must-Use (MU) Plugins

A Collection of Useful WordPress Must-Use (MU) Plugins


If you’ve been building WordPress sites for a while you probably have a folder of code snippets you copy into every new project. I know I do. Mine has been growing for years.

It’s always the same handful of fixes. Clean up the header output, switch off features the site is never going to use, tighten a security default or two, and get rid of the little admin annoyances clients always seem to notice. None of it takes more than a minute to write. The annoying part is writing it again on the next project.

Most tutorials tell you to drop this stuff in your theme’s functions.php file. That works fine until you switch themes, or hand the site over to a client who installs a different one, or update a child theme and lose the lot. A much better home for them is the mu-plugins folder.

Below are various handy must-use plugins I’ve put together over the years. Each one is a single file that does one specific thing. Copy the ones you want, ignore the rest, or grab the whole collection from GitHub at the end of the article.

In this article
What Are MU-Plugins?How to Use These SnippetsCleanup and PerformancePrivacy and Third-Party RequestsSecurity and HardeningComments and SpamAdmin ExperienceStaging and Demo SitesGet Them All on GitHubWrapping Up

What Are MU-Plugins?

MU stands for “must-use”. Any PHP file you drop into wp-content/mu-plugins/ gets loaded automatically on every request, before regular plugins, and there’s no activation step at all.

There are a few things worth knowing before you start dropping files in there:

You can’t deactivate them from the admin. There’s no activate or deactivate link. To turn one off you delete or rename the file. That’s actually a nice feature when you’re handing a site to a client and you don’t want them switching off your security tweaks by accident.

They load first. MU-plugins run before regular plugins, so they’re a good spot for anything that needs to define a constant or set up a filter early.

Only files in the root of the folder get loaded. WordPress doesn’t scan subdirectories. Drop in my-snippet/my-snippet.php and nothing happens. The file has to sit directly in mu-plugins/.

Activation hooks don’t fire. Anything using register_activation_hook to run setup code won’t work here. None of my snippets need it, but it’s worth knowing if you try moving a regular plugin into the folder.

They load alphabetically, so name your files sensibly if load order matters.

You won’t get update notifications, because there’s no repository to check against. You maintain them yourself.

You can see everything that’s loaded under Plugins → Must-Use in the admin. That’s why each of my files still has a proper plugin header. Without one the file still runs, it just shows up as an unnamed entry in the list.

If you want the full picture, the Must Use Plugins page in the Advanced Administration Handbook is the official reference. It covers things like changing the directory with WPMU_PLUGIN_DIR, and explains why the name is a leftover from WordPress MU rather than an accurate description of what the folder does.

How to Use These Snippets

Create the wp-content/mu-plugins/ folder if it doesn’t exist yet, then drop in whichever files you want. That’s it. There’s nothing to activate.

If you’d rather not use mu-plugins, all of these work fine as regular plugins. You can also paste the code into a child theme’s functions.php file or a code snippets plugin, just leave off the plugin header.

Heads up: A few of these are pretty aggressive and are meant for staging or demo sites, not production. I’ve flagged those individually, so read the notes before you install something that stops your contact form emails from sending.

Cleanup and Performance

These are the ones I install without really thinking about it. None of them change how the site works for visitors. They just stop WordPress doing work and storing data nobody asked for.

Clean Head

WordPress prints a bunch of markup into your that most sites never use. An RSD link for remote publishing clients nobody has run in a decade. A Windows Live Writer manifest. Shortlinks, oEmbed discovery links, and a generator tag that tells everyone exactly which version of WordPress you’re on.

None of it is huge on its own. But it’s bytes on every single page load, and that generator tag is handing automated scanners a free hint. This one strips the lot.

Heads up: Two of these are worth a second thought. Removing feed_links kills RSS autodiscovery, so drop those two lines if anyone actually subscribes to your feed. And removing rest_output_link_wp_head only removes the discovery link, it doesn’t disable the REST API. Some tools rely on that link to find your endpoints.

Disable WP Emoji Support

WordPress ships a JavaScript file that converts emoji into images for browsers that can’t render them. In 2026 basically every browser can. So what you’re left with is an extra script, an extra stylesheet, and a DNS prefetch to s.w.org on every page.

This one goes further than the emoji lines in Clean Head. It also strips the emoji handling out of the classic editor, feeds and outgoing emails, plus the resource hint.

[\d]+)’]
);

return $endpoints;
} );

/**
* Return a 404 for author archives.
*
* Runs before redirect_canonical() so that ?author=1 requests 404 rather than
* being redirected to /author/username/, which would leak the name in the
* Location header.
*/
add_action( ‘template_redirect’, function () {
if ( ! is_author() ) {
return;
}

global $wp_query;

$wp_query->set_404();
status_header( 404 );
nocache_headers();
}, 0 );

/**
* Return a generic login error.
*
* Stops the login screen from confirming whether a username exists.
*/
add_filter( ‘login_errors’, function () {
return __( ‘Login failed. Please check your credentials and try again.’ );
} );

The author archive handler runs on template_redirect at priority 0, and that priority is doing real work. Core’s canonical redirect runs on the same hook at priority 10. Getting in first means ?author=1 returns a 404 instead of a 301 with the username sitting in the Location header.

Heads up: If you have real author archives you want indexed, delete that block and keep the rest. The generic login error is worth thinking about too if your site has a lot of non-technical users. It removes the difference between “you typed your password wrong” and “that account doesn’t exist”, which is the whole point, but it does make support requests vaguer.

Disable XML-RPC

XML-RPC is the remote publishing interface WordPress had long before the REST API existed. Almost nothing uses it now, but xmlrpc.php is still sitting there on every install accepting requests. It’s one of the most reliably brute-forced files on the internet, mostly because system.multicall lets someone try hundreds of logins in a single HTTP request.

The pingback method is the other half of it. It can be pointed at a third party and used to bounce traffic off your server, with your site as the unwitting participant.

Heads up: The xmlrpc_enabled filter on its own isn’t enough, and this is the bit most snippets get wrong. It only disables methods that need authentication. The pingback methods never needed a login in the first place, so they keep working. That’s why I unset them separately. If your host lets you block xmlrpc.php at the server level, do that too, since it stops the request before PHP even runs.

Disable Application Passwords

Application passwords let external tools authenticate against the REST API without using a real password. They’re a genuinely good feature if you’re using them. If you’re not, they’re an authentication method sitting open on every account on the site, and one your client has never heard of and is never going to audit.

On a site where nothing is making REST requests from outside, switching them off removes a whole category of credential you’d otherwise have to think about.

Heads up: Check before you use this one. Mobile apps, headless frontends, deployment scripts, uptime monitors and some backup and migration plugins all authenticate this way. If the site quietly depends on it, something will break and the error message won’t make it obvious why.

Disable File Editor

The built-in plugin and theme editors let anyone with the right capability edit live PHP on your production server from a browser. If an admin account ever gets compromised, that editor is the quickest route from stolen password to permanent backdoor.

The usual advice is to define DISALLOW_FILE_EDIT in wp-config.php, and that’s still the best option if you can get to that file. Plenty of managed hosts don’t give you access, so this does it from mu-plugins instead with a fallback for when the constant is already defined.

That else branch matters more than it looks. Some hosts define DISALLOW_FILE_EDIT as false in a mu-plugin of their own, and once a constant is defined you can’t redefine it. The file_mod_allowed filter gives you a second shot at it.

Disable User Registration

The Anyone can register setting under Settings → General is one checkbox. One misclick, or one plugin doing something unexpected, and your site is open to public registration. Bots find that checkbox remarkably fast.

Filtering the option means registration is off no matter what’s in the database, and it stays off even if someone ticks the box.

Heads up: This is for single-site installs. Multisite handles registration through its own network setting, so use the Network Settings screen there instead.

Comments are either the best part of a site or a permanent moderation tax. Which one you get mostly depends on how much of the machinery around them you leave switched on.

There are plenty of plugins that rip comments out of WordPress completely. This one is more specific, and it’s what I use on the Total theme demos. Existing comments stay visible so visitors can see how the theme styles them, but nobody can actually post anything.

It’s surprisingly handy outside of demos too. Archived blogs, docs sites and portfolios often want the old discussion to stay readable without leaving the door open to spam.

Heads up: This closes pings on new and existing content, but it doesn’t touch the XML-RPC pingback endpoint, which is a separate way into the same feature. Pair it with the next plugin if you want the whole thing gone.

Obfuscate Email Shortcode

Putting an email address on a page as plain text is an open invitation to scrapers. The usual workarounds involve JavaScript, or writing it out as “hello [at] example [dot] com”, which is ugly and annoying for actual humans.

WordPress has a built-in function for this that hardly anyone uses. antispambot() encodes the address as HTML entities. Browsers decode them without any fuss, so visitors see and click a normal email address, while a scraper reading the raw HTML just gets a wall of entity codes. This wraps it in a shortcode.



Source link

Leave a Comment

Your email address will not be published. Required fields are marked *