Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Adding Meta Tags in WordPress Without a Plugin (And Why Not Keywords)

Sean

Platform Writer

Aug 14, 2026
8 min read

You can add meta tags without a plugin by hooking wp_head in your child theme’s functions.php. Before you do, one thing worth knowing: the meta keywords tag specifically has been ignored by Google since 2009 and carries no ranking value. If you want the SEO benefit, spend the effort on titles and descriptions instead.

Adding Meta Tags in WordPress Without a Plugin (And Why Not Keywords)

The question gets asked as keywords and almost always means meta tags generally. So this covers both: why keywords are not worth your time, and how to add the tags that are, without installing anything.

Table of contents

The keywords tag, briefly

Google announced publicly in 2009 that it does not use the meta keywords tag for ranking web search results, and that has not changed. Bing has said it may be used as a spam signal, which is the opposite of helpful. Every major SEO plugin either omits the field or hides it behind a setting with a note explaining it does nothing.

The reason is straightforward: it was a self-declared list of what a page is about, with no verification, which made it worthless the moment anyone thought to stuff it.

Two narrow cases where it still does something. Some internal site search products read it, and a few smaller or regional search engines still consider it. If you have one of those, it is a real requirement and worth implementing. Otherwise it is decoration.

The tags that do matter are the title, the meta description, canonical, robots, and the Open Graph and Twitter tags that control how links appear when shared. Those are what the rest of this covers.

The right way: functions.php and wp_head

The wp_head action fires inside the head element on every page. Hook it, and output whatever tags you need.

Do this in a child theme, not the parent. Edits to a parent theme are erased by the next theme update, and that is a class of loss that happens constantly.

add_action( 'wp_head', 'custom_meta_tags', 1 );
function custom_meta_tags() {
    if ( is_singular() ) {
        global $post;

        $description = get_post_meta( $post->ID, '_custom_description', true );
        if ( ! $description ) {
            $description = get_the_excerpt( $post );
        }
        $description = wp_strip_all_tags( $description );
        $description = mb_substr( $description, 0, 155 );

        printf(
            '<meta name="description" content="%s" />' . "\n",
            esc_attr( $description )
        );
    } elseif ( is_front_page() ) {
        printf(
            '<meta name="description" content="%s" />' . "\n",
            esc_attr( get_bloginfo( 'description' ) )
        );
    }
}

Three details that matter. Escape with esc_attr, always, because a description containing a quotation mark otherwise breaks the tag and potentially the page. Use mb_substr rather than substr so multi-byte characters are not cut in half. And check the context, since a description that makes sense on a post is wrong on an archive.

The priority of 1 puts your tags near the top of the head, which is conventional but not required.

Open Graph and Twitter tags, which are worth more

These control the preview when someone shares a link, and their practical effect on click-through is far larger than anything the keywords tag ever did.

add_action( 'wp_head', 'custom_social_tags', 5 );
function custom_social_tags() {
    if ( ! is_singular() ) {
        return;
    }
    global $post;

    $title = get_the_title( $post );
    $url   = get_permalink( $post );
    $desc  = wp_strip_all_tags( get_the_excerpt( $post ) );
    $image = get_the_post_thumbnail_url( $post, 'large' );

    $tags = array(
        'og:type'        => 'article',
        'og:title'       => $title,
        'og:description' => $desc,
        'og:url'         => $url,
        'og:site_name'   => get_bloginfo( 'name' ),
    );
    if ( $image ) {
        $tags['og:image'] = $image;
    }

    foreach ( $tags as $property => $content ) {
        printf(
            '<meta property="%s" content="%s" />' . "\n",
            esc_attr( $property ),
            esc_attr( $content )
        );
    }

    printf(
        '<meta name="twitter:card" content="%s" />' . "\n",
        $image ? 'summary_large_image' : 'summary'
    );
}

Note that Open Graph tags use the property attribute while Twitter tags use name. Getting that wrong is a common reason previews do not render, and validators will not always tell you clearly.

The image should be at least 1200 by 630 pixels and reachable at an absolute URL. A relative path silently produces no image.

Titles and canonical, and what WordPress already does

Do not output a title tag yourself. Modern themes declare support for title-tag and WordPress generates it, so adding your own produces two, which is worse than a suboptimal one.

Modify the generated title through the document_title_parts filter instead.

add_filter( 'document_title_parts', 'custom_title_parts' );
function custom_title_parts( $parts ) {
    if ( is_singular() ) {
        $custom = get_post_meta( get_the_ID(), '_custom_title', true );
        if ( $custom ) {
            $parts['title'] = $custom;
        }
    }
    return $parts;
}

// Separator, if the theme's default is not what you want.
add_filter( 'document_title_separator', function () {
    return '|';
} );

WordPress also outputs a canonical link on singular pages already, so adding another creates a conflict. Only intervene if you have a specific reason, such as consolidating paginated archives or handling a syndicated copy.

For noindex on pages that should not be in search results, the wp_robots filter is the modern approach and replaces the older practice of printing a robots meta tag directly.

add_filter( 'wp_robots', function ( $robots ) {
    if ( is_search() || is_404() ) {
        $robots['noindex'] = true;
    }
    return $robots;
} );

Adding an editable field, without a plugin

Hardcoded descriptions are not much use. A meta box gives editors a field per post, and it is about forty lines.

add_action( 'add_meta_boxes', function () {
    add_meta_box(
        'custom_seo',
        'SEO',
        'render_seo_box',
        array( 'post', 'page' ),
        'normal',
        'high'
    );
} );

function render_seo_box( $post ) {
    wp_nonce_field( 'custom_seo_save', 'custom_seo_nonce' );
    $value = get_post_meta( $post->ID, '_custom_description', true );
    printf(
        '<textarea name="custom_description" rows="3" style="width:100%%" maxlength="160">%s</textarea>',
        esc_textarea( $value )
    );
    echo '<p>Recommended length: 150 to 160 characters.</p>';
}

add_action( 'save_post', function ( $post_id ) {
    if ( ! isset( $_POST['custom_seo_nonce'] ) ) {
        return;
    }
    if ( ! wp_verify_nonce( $_POST['custom_seo_nonce'], 'custom_seo_save' ) ) {
        return;
    }
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    $description = sanitize_textarea_field( wp_unslash( $_POST['custom_description'] ?? '' ) );
    update_post_meta( $post_id, '_custom_description', $description );
} );

All four guards in the save handler are necessary. The nonce check stops forged requests, the autosave check stops the field being wiped by an autosave that does not include it, and the capability check stops a user editing a post they do not own.

The underscore prefix on the meta key hides it from the default custom fields interface, which is what you want for a value with its own editor.

Plugin or not

Doing this by hand is reasonable when you want a small number of tags, you are comfortable in PHP, and you would rather not add another plugin to maintain.

A plugin is the better choice when you want editors to have previews and guidance, when you need sitemaps, schema markup, and redirect management as well, or when nobody on the team will be able to maintain custom code after you leave.

The honest trade: custom code is lighter and does exactly what you wrote. A plugin does far more, keeps up with changing requirements from search engines and social platforms, and is understood by the next person. For a site somebody else will inherit, that last point usually wins.

What is not a good reason to avoid a plugin is performance. A well-built SEO plugin adds very little to page generation time, and the plugins actually slowing WordPress sites are page builders and sliders rather than metadata.

How this fits the rest of the stack

Editing functions.php means either an SFTP client or the theme editor, and the theme editor is the one you should never use on a live site because a syntax error takes the whole site down with no way back in. Managed WordPress on RunxBuild includes a file manager in the dashboard for editing files directly, which is a safer middle ground. The RunxBuild hosting calculator shows what a WordPress plan costs alongside everything else, from $3 a month.

Useful related references:

FAQ

Do meta keywords still work for SEO?

No. Google confirmed in 2009 that it ignores the meta keywords tag for web search ranking and that has not changed. Some internal site search tools and smaller engines still read it, but for search ranking it does nothing.

How do I add meta tags in WordPress without a plugin?

Hook the wp_head action in a child theme’s functions.php and print the tags there, escaping every value with esc_attr. Use a child theme so a parent theme update does not erase the code.

Should I edit header.php or functions.php?

functions.php with the wp_head hook. Editing header.php works but is theme-specific and harder to make conditional per page type, and both are lost on theme update unless you use a child theme.

Why is my meta description not showing in search results?

Search engines frequently rewrite descriptions based on the query rather than using yours. Check the page source to confirm the tag is present and that there is only one; beyond that, the description is a suggestion rather than an instruction.

Do I need to add a canonical tag in WordPress?

Usually not. WordPress outputs one on singular pages already, and adding a second creates a conflict. Only intervene for specific cases such as paginated archives or syndicated content.

#WordPress Meta Tags#Meta Keywords#WordPress SEO#wp_head#functions.php