Like the blog? Get the book »

Custom Page Titles from Scratch

Custom Page Titles from Scratch

The titles of pages are controlled by the <title> tag in the <head> section of a website. They are important for all kinds of reasons. Telling the user where they are. The name of the page when bookmarked both locally and socially. They are important for SEO.

So how do we typically handle page titles in WordPress? Hopefully we are A) Using a theme that uses a pretty smart default page titling system or B) using a plugin that helps us with this same task automatically, as most SEO plugins at least attempt.

The problem with A is that it doesn’t afford any way to individually override any particular page with a special title. With B, it depends on which plugin you use, but not all of them allow for full control over the entire title, often just the slug that fits into the overall chosen method.

Finally fed up with both of these techniques, I rolled my own!

Step 1: Remove <title>

Whatever you have for the <title> tag in the header.php file, just get rid of it.

Screenshot of header.php

We’ll be inserting that automatically in the wp_head() function, so that needs to be there.

Step 2: New stuff for functions.php file

We’re doing three things here.

  1. Add a new input box to the admin pages for creating and editing new Posts/Pages
  2. Save the the value of that input box when that Post/Page is saved
  3. Echo out a new title as part of the wp_head() function
// Custom Page Titles
add_action('admin_menu', 'custom_title');
add_action('save_post', 'save_custom_title');
function custom_title() {
	add_meta_box('custom_title', 'Change page title', 'custom_title_input_function', 'post', 'normal', 'high');
	add_meta_box('custom_title', 'Change page title', 'custom_title_input_function', 'page', 'normal', 'high');
function custom_title_input_function() {
	global $post;
	echo '<input type="hidden" name="custom_title_input_hidden" id="custom_title_input_hidden" value="'.wp_create_nonce('custom-title-nonce').'" />';
	echo '<input type="text" name="custom_title_input" id="custom_title_input" style="width:100%;" value="'.get_post_meta($post->ID,'_custom_title',true).'" />';
function save_custom_title($post_id) {
	if (!wp_verify_nonce($_POST['custom_title_input_hidden'], 'custom-title-nonce')) return $post_id;
	if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return $post_id;
	$customTitle = $_POST['custom_title_input'];
	update_post_meta($post_id, '_custom_title', $customTitle);
function insert_custom_title() {
	if (have_posts()) : the_post();
	  $customTitle = get_post_meta(get_the_ID(), '_custom_title', true);
	  if ($customTitle) {
		echo "<title>$customTitle</title>";
      } else {
    	echo "<title>";
	      if (is_tag()) {
	         single_tag_title("Tag Archive for ""); echo '" - '; }
	      elseif (is_archive()) {
	         wp_title(''); echo ' Archive - '; }
	      elseif ((is_single()) || (is_page()) && (!(is_front_page())) ) {
	         wp_title(''); echo ' - '; }
	      if (is_home()) {
	         bloginfo('name'); echo ' - '; bloginfo('description'); }
	      else {
	          bloginfo('name'); }
	      if ($paged>1) {
	         echo ' - page '. $paged; }
        echo "</title>";
    else :
      echo "<title>Page Not Found | Envision</title>";

The new input box we are putting on the page saves its value as a custom field on the Post/Page you are on. The key of that custom field is _custom_title, so it doesn’t show in the regular custom field list (custom fields that start with underscores don’t).

Now when any front-end page of the site loads, it checks that page to see if that custom field exists. If it does exist, it uses that value exactly for the page title. If it doesn’t, it defaults to a generically smart page title structure.


This is the box that now is available when creating or editing a Page/Post:

Custom Field for Custom Page Title

And that is what will be used:

Custom Page Title displayed in a browser

22 responses

  1. I could be wrong, but it looks to me like this will display nothing on 404 pages and search pages that produce no results because you wrapped it in the have_posts() if statement.

    • Unless it was added since you posted your comment, it has stuff for 404 and other non-post pages

    • At the time of (both of these) comments it was incorrect. I moved the 404 handling to the else: for the loop, which now works correctly. Thanks for the find!

  2. Deluxe Blog Tips

    I think some SEO plugins do this job very well. Anyway, this tutorial is helpful for someone who want to do-it-yourself.

    I’ve just created a post related to this, How To Create A Better Meta Box In WordPress Post Editing Page, can you please look at this and give me some opinion? It will be my pleasure. Thank you very much.

  3. Hey. I kinda like that! I may just use that in my upcoming portfolio theme development.

  4. Chris (or anyone!) do you know how to handle titles (and descriptions) for custom taxonomies?

    There must be a way amongst all the WordPress Wizards out there. Have found some info to do so at the parent level (eg: where taxonomy is Brands) but not for the children of (where Nike, Reebox and 2XU belong in the Brands taxonomy). I don’t want duplicate titles for every child in the taxonomy, so the solutions I’ve found are largely null & void.

  5. That’s definitely something I would like to incorporate on future sites but is there any way to do this without the wp_head(); function? I tend to leave that off of sites I develop as I don’t really build themes as such, simple custom themes specifically for the client. If I leave wp_head() in there there can be all kinds of unnecessary meta, style and script tags in there.

  6. Great stuff! Will start using it..

  7. @Steve – you could simply call the insert custom title function in your header.php

    I would be careful about not including wp_head in your header files though. There are many plugins which launch their script from wp_head, and as such not including it would cause those plugins to fail.

  8. Jean-Paul Horn

    wpSEO (recently featured here) does just this (and more), by inserting a new meta box in every post to manually change the title , meta description and robots tag. You have to enable this in the plugins settings though.

  9. You could create a custom admin page with a text box for a custom 404 title.

    There’s a lot of code needed and I won’t go in to it, but email me (there’s a contact form on my blog) and I’ll try and help you out.

  10. like it will try it

  11. Thanks for the tip! I just extended it a bit. Custom fields not only for the title, but also for description and keywords.

    Editor’s note: 404 link removed.

  12. We can do the some thinks using All in One SEO Pack or Platinum SEO Pack…

  13. Webdesigner

    Really, really thank you for this tutorial. Getting new input fields into the wp-admin interface made me headache until now!

  14. This is what I’m searching for. I need to use custom fields to edit some PAGE titles, not Posts’ (that I use All in One SEO Pack).

    Thanks for sharing.

  15. There is an issue where in the archive pages if the first post has a custom title it will display that instead of the “Archive for category X” or such. I solved it by changing the third line of the insert_custom_title() function into:

    if ($customTitle && (!is_archive())) {

    I know it’s a bit rough, but at least it solves the issue… (I guess it would be better to check it before retrieving the custom title…?)

    • There is something else going on here, on my 404 pages a page’s title is always shown (always the same one, and I don’t know the reason for it using this particular one, probably because it is the last one and the one that’s showing in the homepage). I tried changing the same line as before to:

      if ($customTitle && (!is_archive() || !is_404())) {

      But it doesn’t really work…

    • Chris…excellent post and awesome book! TY ;)

      To zmh…try this:

      if ($customTitle && (!is_archive() && !is_404())) {

  16. Brilliant. Thanks so much Chris.

  17. Thanks for the great script, I’m using it well. I noticed something that I needed to update when I used it and thought I’d pass it on.

    For pages that are not found, you’re hardcoding the “Envision” name:

    else :
    	echo "<title>Page Not Found | Envision</title>";

    I just switched it to:

    else :
    	echo "<title>Page Not Found | ";
    	echo bloginfo('name');
    	echo "</title>";
  18. I dunno if it has to do with the latest update 3.4.1 of wordpress but my home page is showing the name of the latest article I made. Also the pages have the custom title – “Name of the blog” again. I really dunno why.?!?!

Comments are closed for this post. Contact us with any critical information.
© 2009–2024 Digging Into WordPress Powered by WordPress Monzilla Media shapeSpace