30 Custom WordPress Functions to Supercharge Your Website

A curated collection of battle-tested WordPress functions covering performance, security, admin customisation and frontend tweaks — updated for 2026.

A curated collection of 30 battle-tested WordPress functions I've used across hundreds of client projects over the past 15+ years. Each snippet is ready to drop into your theme's functions.php or a site-specific plugin. Fully updated for WordPress 6.x and current best practices.

Table of Contents

  1. Before you start
  2. Performance optimisation
  3. Admin dashboard customisation
  4. Security hardening
  5. Frontend customisation
  6. Modern WordPress (6.x+)

Before you start

Where to add these functions

There are two ways to add custom functions to WordPress:

Option 1: Your theme's functions.php — Every theme has one at /wp-content/themes/YOUR_THEME/functions.php. Just add your code at the end. If you're using a pre-built or parent theme, always use a child theme so updates don't wipe your changes.

Option 2: A site-specific plugin (recommended) — This keeps your functions independent of your theme, so they survive theme switches. Create /wp-content/plugins/custom-functions/custom-functions.php:

<?php
/*
Plugin Name: Custom Functions
Description: Site-specific WordPress customisations
*/

// Add your functions below this line

Activate the plugin from the WordPress admin and you're good to go.


Performance optimisation

Every unnecessary script, style and meta tag WordPress outputs is an extra HTTP request or bytes your visitors have to download. These functions strip out the bloat that most sites will never use.

1. Disable WordPress emoji scripts

WordPress loads its own emoji detection script and stylesheet on every single page. Unless you're relying on WordPress to render emoji (you're almost certainly not — all modern browsers handle emoji natively), this is wasted overhead.

/**
 * Disable WordPress emoji scripts and styles
 */
function disable_wp_emoji() {
    remove_action('admin_print_styles', 'print_emoji_styles');
    remove_action('wp_head', 'print_emoji_detection_script', 7);
    remove_action('admin_print_scripts', 'print_emoji_detection_script');
    remove_action('wp_print_styles', 'print_emoji_styles');
    remove_filter('wp_mail', 'wp_staticize_emoji_for_email');
    remove_filter('the_content_feed', 'wp_staticize_emoji');
    remove_filter('comment_text_rss', 'wp_staticize_emoji');
    add_filter('emoji_svg_url', '__return_false');
    add_filter('tiny_mce_plugins', function ($plugins) {
        return is_array($plugins) ? array_diff($plugins, ['wpemoji']) : [];
    });
}
add_action('init', 'disable_wp_emoji');

This removes the emoji script from the frontend and admin, stops WordPress from converting emoji in emails and feeds, and removes the TinyMCE emoji plugin from the editor. It's one of the simplest performance wins you can make.

2. Remove jQuery Migrate

WordPress still bundles jQuery Migrate as a dependency of jQuery on the frontend. It exists to help old plugins cope with newer versions of jQuery, but most actively maintained themes and plugins dropped their dependency on it years ago. Removing it saves a ~10KB script from loading on every page.

/**
 * Remove jQuery Migrate from the frontend
 */
function remove_jquery_migrate($scripts) {
    if (!is_admin() && isset($scripts->registered['jquery'])) {
        $script = $scripts->registered['jquery'];
        if ($script->deps) {
            $script->deps = array_diff($script->deps, ['jquery-migrate']);
        }
    }
}
add_action('wp_default_scripts', 'remove_jquery_migrate');

3. Remove global styles and SVG filters

Since WordPress 5.9, a chunk of inline CSS for the global styles system (used by block themes and the Site Editor) is injected into the <head> of every page, along with a block of SVG filter definitions in the <body>. If you're using a classic (non-block) theme, none of this is used and it's safe to remove.

/**
 * Remove global styles and SVG filters (classic themes only)
 */
function remove_global_styles() {
    remove_action('wp_enqueue_scripts', 'wp_enqueue_global_styles');
    remove_action('wp_body_open', 'wp_global_styles_render_svg_filters');
}
add_action('init', 'remove_global_styles');

4. Disable oEmbed discovery

WordPress registers itself as an oEmbed provider, allowing other WordPress sites to embed your posts as rich cards. It loads an extra JavaScript file to support this. If you don't need other sites embedding your content, disable it to save the HTTP request.

/**
 * Disable oEmbed provider functionality
 */
function disable_oembed() {
    remove_action('wp_head', 'wp_oembed_add_discovery_links');
    remove_action('wp_head', 'wp_oembed_add_host_js');
}
add_action('init', 'disable_oembed');
add_action('wp_footer', function () {
    wp_dequeue_script('wp-embed');
});

5. Clean up wp_head

WordPress outputs several meta tags and links in your document's <head> that most sites don't need: RSD discovery links (for XML-RPC clients), Windows Live Writer manifest links, shortlink tags, and the WordPress version number. Removing these reduces clutter and avoids exposing your WordPress version to potential attackers.

/**
 * Remove unnecessary tags from wp_head
 */
function clean_wp_head() {
    remove_action('wp_head', 'rsd_link');                         // RSD link
    remove_action('wp_head', 'wlwmanifest_link');                 // Windows Live Writer
    remove_action('wp_head', 'wp_shortlink_wp_head', 10, 0);     // Shortlink
    remove_action('wp_head', 'wp_generator');                     // WordPress version
    add_filter('the_generator', '__return_empty_string');          // Also strip from feeds
}
add_action('after_setup_theme', 'clean_wp_head');

6. Disable RSS feeds

If your site doesn't use RSS (many business and brochure sites don't), you can disable the feeds entirely. This prevents WordPress from generating the feed XML and removes the <link rel="alternate"> tags from your <head>.

/**
 * Disable all RSS feeds
 */
function disable_rss_feeds() {
    wp_die(
        __('RSS feeds are not available. Please visit our <a href="' . esc_url(home_url('/')) . '">homepage</a>.'),
        __('Feeds Disabled'),
        ['response' => 403]
    );
}
add_action('do_feed', 'disable_rss_feeds', 1);
add_action('do_feed_rdf', 'disable_rss_feeds', 1);
add_action('do_feed_rss', 'disable_rss_feeds', 1);
add_action('do_feed_rss2', 'disable_rss_feeds', 1);
add_action('do_feed_atom', 'disable_rss_feeds', 1);

// Remove feed links from <head>
remove_action('wp_head', 'feed_links', 2);
remove_action('wp_head', 'feed_links_extra', 3);

7. Limit post revisions

By default, WordPress stores every single revision of every post — forever. On a busy site, this can balloon your database with thousands of unused rows. Setting a sensible limit keeps things manageable without losing the ability to undo recent changes.

/**
 * Limit post revisions to 5 per post
 * Add this to wp-config.php (not functions.php)
 */
define('WP_POST_REVISIONS', 5);

This is a wp-config.php constant, not a function — add it above the /* That's all, stop editing! */ line in your WordPress config file.

8. Disable self-pingbacks

When you link to one of your own posts in a new article, WordPress sends itself a pingback — creating a comment notification for no reason. This function stops that from happening.

/**
 * Disable self-pingbacks
 */
function disable_self_pingbacks(&$links) {
    $home = home_url();
    foreach ($links as $l => $link) {
        if (str_starts_with($link, $home)) {
            unset($links[$l]);
        }
    }
}
add_action('pre_ping', 'disable_self_pingbacks');

Admin dashboard customisation

These functions help you clean up the WordPress admin, brand it for clients, and make the day-to-day editing experience smoother.

9. Customise the login page

Brand the WordPress login screen with your own stylesheet, logo link and link text. This is especially nice for client sites — it replaces the WordPress logo with yours and links it to the site homepage instead of wordpress.org.

/**
 * Customise the WordPress login page
 */
function custom_login_css() {
    wp_enqueue_style(
        'custom-login',
        get_template_directory_uri() . '/assets/css/login.css',
        [],
        '1.0'
    );
}
add_action('login_enqueue_scripts', 'custom_login_css');

// Point the logo link to the homepage
add_filter('login_headerurl', function () {
    return home_url('/');
});

// Set the logo link text to the site name
add_filter('login_headertext', function () {
    return get_option('blogname');
});

Your login.css file can style the .login h1 a element to swap in your own logo using background-image.

10. Remove comments entirely

Most business websites don't use WordPress comments. If yours is one of them, this removes the Comments menu, admin bar item, and comment support from posts and pages — keeping the admin clean.

/**
 * Remove comments from the admin entirely
 */
add_action('admin_menu', function () {
    remove_menu_page('edit-comments.php');
});

add_action('init', function () {
    remove_post_type_support('post', 'comments');
    remove_post_type_support('page', 'comments');
}, 100);

add_action('admin_bar_menu', function ($wp_admin_bar) {
    $wp_admin_bar->remove_menu('comments');
}, 999);

Replace the default "Thank you for creating with WordPress" text at the bottom of every admin page. Useful for branding client dashboards.

/**
 * Custom admin footer message
 */
add_filter('admin_footer_text', function () {
    return '<span id="footer-thankyou">Built and maintained by <a href="https://example.com" target="_blank" rel="noopener">Your Name</a></span>';
});

12. Show update notices to admins only

WordPress shows update nags to every logged-in user by default. Editors and authors don't need to see them — and they can cause unnecessary support requests from clients.

/**
 * Only show update notices to administrators
 */
function admin_only_update_notices() {
    if (!current_user_can('update_core')) {
        remove_action('admin_notices', 'update_nag', 3);
    }
}
add_action('admin_head', 'admin_only_update_notices', 1);

13. Add a custom dashboard widget

Replace the default WordPress dashboard widgets with something actually useful — quick links, support contact details, or site stats.

/**
 * Add a custom welcome widget to the dashboard
 */
function my_dashboard_widget() {
    echo '<p>Welcome! Need help? Email <a href="mailto:[email protected]">[email protected]</a></p>';
    echo '<ul>';
    echo '<li><a href="' . admin_url('post-new.php') . '">Write a new post</a></li>';
    echo '<li><a href="' . admin_url('upload.php') . '">Manage media</a></li>';
    echo '</ul>';
}

function register_dashboard_widget() {
    wp_add_dashboard_widget('site_welcome', 'Welcome', 'my_dashboard_widget');
}
add_action('wp_dashboard_setup', 'register_dashboard_widget');

14. Remove the admin toolbar on the frontend

The admin toolbar can interfere with fixed headers and custom layouts. If it's more of a nuisance than a help, disable it on the frontend.

/**
 * Hide the admin toolbar on the frontend
 */
add_action('after_setup_theme', function () {
    if (!is_admin()) {
        show_admin_bar(false);
    }
});

15. Add featured image column to post listing

A small thumbnail column in the posts list makes it much easier to scan through content and spot posts without images.

/**
 * Add a thumbnail column to the posts list
 */
add_image_size('admin-list-thumb', 80, 80, false);

add_filter('manage_posts_columns', function ($columns) {
    $new = [];
    foreach ($columns as $key => $title) {
        if ($key === 'title') {
            $new['featured_thumb'] = __('Image');
        }
        $new[$key] = $title;
    }
    return $new;
});

add_action('manage_posts_custom_column', function ($column, $post_id) {
    if ($column === 'featured_thumb') {
        echo '<a href="' . esc_url(get_edit_post_link($post_id)) . '">';
        if (has_post_thumbnail($post_id)) {
            echo get_the_post_thumbnail($post_id, 'admin-list-thumb');
        } else {
            echo '<span style="color:#999;">—</span>';
        }
        echo '</a>';
    }
}, 10, 2);

16. Customise the Settings API (theme options page)

If your theme needs a handful of configurable options (social handles, tracking codes, etc.), the WordPress Settings API lets you build a native-looking settings page without a plugin.

/**
 * Register a simple theme settings page
 */
function mytheme_settings_page() {
    ?>
    <div class="wrap">
        <h1>Theme Settings</h1>
        <form method="post" action="options.php">
            <?php
                settings_fields('mytheme_settings');
                do_settings_sections('mytheme-settings');
                submit_button();
            ?>
        </form>
    </div>
    <?php
}

add_action('admin_menu', function () {
    add_theme_page('Theme Settings', 'Theme Settings', 'manage_options', 'mytheme-settings', 'mytheme_settings_page');
});

add_action('admin_init', function () {
    add_settings_section('mytheme_general', 'General', null, 'mytheme-settings');

    add_settings_field('mytheme_analytics_id', 'Analytics ID', function () {
        $value = esc_attr(get_option('mytheme_analytics_id', ''));
        echo '<input type="text" name="mytheme_analytics_id" value="' . $value . '" class="regular-text">';
    }, 'mytheme-settings', 'mytheme_general');

    register_setting('mytheme_settings', 'mytheme_analytics_id', [
        'sanitize_callback' => 'sanitize_text_field',
    ]);
});

Retrieve in your theme with get_option('mytheme_analytics_id').


Security hardening

WordPress is secure out of the box, but its popularity makes it a target. These functions reduce your attack surface by disabling features you probably don't need and removing information that helps attackers.

17. Disable XML-RPC

XML-RPC is a remote communication protocol that most modern sites don't need — the REST API has long since replaced it. It's one of the most commonly exploited attack vectors for brute-force login attempts and DDoS amplification. If you don't use Jetpack, the WordPress mobile app, or remote publishing tools, disable it entirely.

/**
 * Disable XML-RPC entirely
 */
add_filter('xmlrpc_enabled', '__return_false');

// Also remove the discovery links since we've disabled it
remove_action('wp_head', 'rsd_link');
remove_action('xmlrpc_rsd_apis', 'rest_output_rsd');

18. Hide login error messages

By default, WordPress tells attackers whether a username exists — the error message changes from "Unknown username" to "Incorrect password". This function returns a generic message regardless of what went wrong, so attackers can't enumerate valid usernames.

/**
 * Return a generic error on failed login attempts
 */
add_filter('login_errors', function () {
    return 'The login details you entered were incorrect. Please try again.';
});

19. Disable file editing from the dashboard

WordPress includes a built-in theme and plugin code editor accessible from the admin panel. If an attacker gains admin access, this is one of the first things they'll use to inject malware. Disabling it forces code changes to go through FTP/SSH where there's an audit trail.

/**
 * Disable the theme and plugin editor
 * Add this to wp-config.php
 */
define('DISALLOW_FILE_EDIT', true);

20. Disable Application Passwords

WordPress 5.6 introduced Application Passwords, which allow third-party apps to authenticate with the REST API. If your site doesn't use any external apps that connect via the API, disabling this feature removes an unnecessary authentication pathway.

/**
 * Disable Application Passwords
 */
add_filter('wp_is_application_passwords_available', '__return_false');

21. Add security headers

HTTP security headers tell browsers how to behave when handling your site's content. These four headers protect against clickjacking, MIME-type sniffing, excessive referrer leakage, and unnecessary browser API access.

/**
 * Add essential security headers
 */
function add_security_headers() {
    if (!is_admin()) {
        header('X-Content-Type-Options: nosniff');
        header('X-Frame-Options: SAMEORIGIN');
        header('Referrer-Policy: strict-origin-when-cross-origin');
        header('Permissions-Policy: camera=(), microphone=(), geolocation=()');
    }
}
add_action('send_headers', 'add_security_headers');

22. Force strong passwords for all users

WordPress suggests strong passwords but doesn't enforce them. This hooks into the profile update to reject weak password choices.

/**
 * Require strong passwords on profile updates
 */
add_action('user_profile_update_errors', function ($errors, $update, $user) {
    if (!empty($user->user_pass) && strlen($user->user_pass) < 12) {
        $errors->add('weak_password', '<strong>Error:</strong> Passwords must be at least 12 characters long.');
    }
}, 10, 3);

Frontend customisation

These functions help you control what visitors see — from excerpt formatting to how your theme loads external assets.

23. Change the excerpt length and suffix

WordPress generates automatic excerpts at 55 words with a [...] suffix. You'll almost always want to customise both the length and the trailing text.

/**
 * Shorten excerpt to 25 words with a custom "Read More" link
 */
add_filter('excerpt_length', function () {
    return 25;
}, 999);

add_filter('excerpt_more', function () {
    return '… <a href="' . esc_url(get_permalink()) . '" class="read-more">Read more →</a>';
});

24. Properly enqueue Google Fonts

Loading fonts via wp_enqueue_style() ensures they're properly registered in WordPress's dependency system and won't conflict with caching plugins. Use display=swap so text remains visible while fonts load.

/**
 * Enqueue Google Fonts
 */
function enqueue_google_fonts() {
    wp_enqueue_style(
        'google-fonts',
        'https://fonts.googleapis.com/css2?family=Inter:[email protected]&display=swap',
        [],
        null
    );
}
add_action('wp_enqueue_scripts', 'enqueue_google_fonts');

25. Add custom image sizes

WordPress ships with a few default image sizes (thumbnail, medium, large, full), but you'll often need sizes tailored to your design — hero banners, card thumbnails, etc.

/**
 * Register custom image sizes
 */
function register_custom_image_sizes() {
    add_image_size('hero', 1920, 800, true);     // Hard crop for hero banners
    add_image_size('card-thumb', 600, 400, true); // Card thumbnails
    add_image_size('square', 600, 600, true);     // Square crops
}
add_action('after_setup_theme', 'register_custom_image_sizes');

For these sizes to appear in the media uploader dropdown, you also need to register them:

add_filter('image_size_names_choose', function ($sizes) {
    return array_merge($sizes, [
        'hero'       => __('Hero Banner'),
        'card-thumb' => __('Card Thumbnail'),
        'square'     => __('Square'),
    ]);
});

26. Defer scripts for better performance

Render-blocking JavaScript is one of the biggest performance killers. This function adds defer to all enqueued scripts (except jQuery, which many plugins expect to be available immediately).

/**
 * Add defer attribute to frontend scripts
 */
function defer_frontend_scripts($tag, $handle, $src) {
    if (is_admin()) return $tag;

    // Don't defer jQuery core — too many plugins depend on inline scripts
    $no_defer = ['jquery-core', 'jquery'];
    if (in_array($handle, $no_defer, true)) return $tag;

    if (strpos($tag, 'defer') !== false) return $tag;

    return str_replace(' src=', ' defer src=', $tag);
}
add_filter('script_loader_tag', 'defer_frontend_scripts', 10, 3);

27. Remove version strings from scripts and styles

By default, WordPress appends its version number as a query string (?ver=6.5.2) to all enqueued scripts and styles. This exposes your WordPress version to anyone who views source. Removing it also improves caching, since the query string changes on every WordPress update, busting the cache unnecessarily.

/**
 * Remove version query strings from scripts and styles
 */
function remove_version_strings($src) {
    if (strpos($src, '?ver=')) {
        $src = remove_query_arg('ver', $src);
    }
    return $src;
}
add_filter('style_loader_src', 'remove_version_strings', 9999);
add_filter('script_loader_src', 'remove_version_strings', 9999);

Modern WordPress (6.x+)

These functions address quirks and features specific to recent WordPress versions that are worth knowing about.

28. Disable pingbacks and trackbacks

Trackbacks and pingbacks are relics from blogging's early days and are now almost exclusively used for spam. Disable them for new posts by default.

/**
 * Disable pingbacks and trackbacks
 */
add_filter('xmlrpc_methods', function ($methods) {
    unset($methods['pingback.ping']);
    unset($methods['pingback.extensions.getPingbacks']);
    return $methods;
});

add_filter('option_default_pingback_flag', '__return_zero');

Instead of checking has_post_thumbnail() everywhere in your templates and displaying nothing when there isn't one, you can filter post_thumbnail_html to return a default image automatically.

/**
 * Fallback featured image when none is set
 */
add_filter('post_thumbnail_html', function ($html, $post_id, $thumb_id) {
    if (empty($html)) {
        $default = get_template_directory_uri() . '/assets/img/default-thumb.jpg';
        $html = '<img src="' . esc_url($default) . '" alt="' . esc_attr(get_the_title($post_id)) . '" class="wp-post-image">';
    }
    return $html;
}, 10, 3);

30. Disable auto-updates selectively

WordPress 5.5 introduced automatic updates for plugins and themes. While automatic minor core updates have been around since 3.7, you might want fine-grained control — keeping security updates but preventing plugins from updating themselves unexpectedly (which can and does break sites).

/**
 * Keep core auto-updates, disable plugin and theme auto-updates
 */
add_filter('auto_update_core', '__return_true');           // Core minor updates
add_filter('auto_update_plugin', '__return_false');         // No plugin auto-updates
add_filter('auto_update_theme', '__return_false');          // No theme auto-updates
add_filter('auto_update_translation', '__return_true');     // Translation updates are safe


Wrapping up

These 30 functions represent the most useful and widely applicable WordPress customisations I've come across in over 15 years of building WordPress sites professionally. They address the things that come up on nearly every project: unnecessary bloat, missing security basics, admin UX rough edges, and frontend performance.

The beauty of WordPress is that almost everything is hookable. If there's something WordPress does that you don't like, there's almost certainly a filter or action to change it.

A few things to remember:

  • Always back up before making changes — especially to functions.php and wp-config.php
  • Test on staging first — a syntax error in functions.php will white-screen your entire site
  • Keep it clean — don't hoard functions you're not actually using; every line of code is a line you have to maintain
  • Prefix your functions — use a unique prefix (like your theme name) to avoid naming conflicts with plugins

Got questions, or want to share your own favourite WordPress functions? Get in touch — I'm always happy to talk shop.