Helpful functions.php for WordPress (copy paste)

Joined
Feb 2, 2015
Messages
155
Likes
142
Degree
1
This is mostly directed at beginners with WP and not so great with PHP, but you might find some that you need anyways to copy and paste.

If you're not utilizing your functions.php you're probably editing a lot of theme files that makes it more a bitch for theme updates if you're using a child theme (hopefully you are).

Security

Some security additions to help hide the version of WP you're using and help lean it out more too for speed.
Code:
//remove WP version string from header [security] [footprint]
remove_action('wp_head', 'wp_generator');

//hide login error messages (ex. "Wrong Password") [security] [footprint]
add_filter('login_errors',create_function('$a', "return null;"));

//remove admin name in comments class [security] [footprint]
function remove_comment_author_class( $classes ) {
    foreach( $classes as $key => $class ) {
        if(strstr($class, "comment-author-")) {
            unset( $classes[$key] );
        }
    }
    return $classes;
}
add_filter( 'comment_class' , 'remove_comment_author_class' );

//remove WP version param from any enqueued scripts [security] [footprint] [speed]
function remove_wpversion_cssjs( $src ) {
    if ( strpos( $src, 'ver=' ) )
        $src = remove_query_arg( 'ver', $src );
    return $src;
}
add_filter( 'style_loader_src', 'remove_wpversion_cssjs', 999 );
add_filter( 'script_loader_src', 'remove_wpversion_cssjs', 999 );

//change URL string from Author to Writer [footprint]
function new_author_base() {
global $wp_rewrite;
    $author_slug ='sellers';
    $wp_rewrite->author_base = $author_slug;
}
add_action('init','new_author_base');


Tracking/Analytics


Add whatever tracking codes or additional scripts you need to the header, this will add it to the bottom of the file (so you probably want to just add JS, always put CSS before this stuff for page speed).
Code:
function add_trackingheader() {
?>
<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  ga('create', 'UA-YOURNUMBERSHERE-1', 'auto');
  ga('send', 'pageview');
</script>
<meta name="msvalidate.01" content="YOURNUMBERSHERE" />
<?php
}
add_action( 'wp_head', 'add_trackingheader', 999 );

You can also hook it to the footer, but most of these tracking scripts are asynchronous and won't affect anything else being loaded, but you can place whatever you want here.
Code:
//add JS to footer
function add_js_to_footer() {
?>
<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  ga('create', 'UA-YOURNUMBERSHERE-1', 'auto');
  ga('send', 'pageview');
</script>
<meta name="msvalidate.01" content="YOURNUMBERSHERE" />
<?php
}
add_action( 'wp_footer', 'add_js_to_footer', 999 );


Speed


Note about these. Plugins you're adding to the site or in your purchased theme's layout you might be loading CSS/JSS that you don't need to use on every page or don't need at all. If you need to load these scripts on specific pages you can refer to this.
Code:
//remove CSS from header
add_action( 'wp_print_styles', 'deregister_my_styles', 100 );
function deregister_my_styles() {
    wp_deregister_style( 'jetpack_css' );
}

Replace WP Jquery with Google's CDN version.
Code:
// even more smart jquery inclusion
add_action('init','jquery_register');

// register from google and for footerfunction jquery_register() {
    if(!is_admin()) {
    wp_deregister_script('jquery');
    wp_register_script('jquery',('https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js'),false,null,true);
    wp_enqueue_script('jquery');
    }
}

Sets a limit for your post revisions to help stop so much database bloat.
Code:
//set limit for post revisions to 10
if(!defined('WP_POST_REVISIONS')) define('WP_POST_REVISIONS',10);

Provided by @Ryuzaki here to remove added Emoji styles. These are asynchronous so they shouldn't affect speed, but it's just another footprint + added stuff we don't need so why not remove it.
Code:
//remove emoji [speed] [footprint]
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );

This resizes images if they're too big for the Large media setting. The idea here is for client sites or your own you won't end up with these massive image files that can eat away at your memory.
Code:
//resize images that are too big to the Large media size setting [speed]
function replace_uploaded_image($image_data){// if there is no large image : returnif(!isset($image_data['sizes']['large']))return $image_data;
$upload_dir = wp_upload_dir();
$uploaded_image_location = $upload_dir['basedir'].'/'.$image_data['file'];
$large_image_location = $upload_dir['path'].'/'.$image_data['sizes']['large']['file'];
unlink($uploaded_image_location);
rename($large_image_location,$uploaded_image_location);
$image_data['width']= $image_data['sizes']['large']['width'];
$image_data['height']= $image_data['sizes']['large']['height'];
unset($image_data['sizes']['large']);
return $image_data;
}
add_filter('wp_generate_attachment_metadata','replace_uploaded_image');

Remove the default fields for user's that WP has: AIM, Jabber, YIM. These add database bloat.
Code:
function remove_default_userfields( $contactmethods ) {
unset($contactmethods['aim']);
unset($contactmethods['jabber']);
unset($contactmethods['yim']);
return $contactmethods;
}
add_filter('user_contactmethods','remove_default_userfields',10,1);

This a random side one that I like, it just makes it so all the HTML tags are closed before the posts ends. If your clients are poking in the page or VA's this might save you some grief if pages are getting broken from editing in visual editor and breaking anything.
Code:
//automatically clean up html wysiwyg editor by closing missing tags (ex. <p>, <a>, etc) [wp-admin]
function clean_bad_content($bPrint = false) {
    global $post;
    $szPostContent  = $post->post_content;
    $szRemoveFilter = array("~<p[^>]*>\s?</p>~", "~<a[^>]*>\s?</a>~", "~<font[^>]*>~", "~<\/font>~", "~style\=\"[^\"]*\"~", "~<span[^>]*>\s?</span>~");
    $szPostContent  = preg_replace($szRemoveFilter, '', $szPostContent);
    $szPostContent  = apply_filters('the_content', $szPostContent);
    if ($bPrint == false) return $szPostContent;
    else echo $szPostContent;
}
I'll try and post some more too.
 
// Get Rid of WP Version Footprint Throughout Site
Don't be a target. Keep your core updated of course, but don't announce your version to scrapers.
Code:
// Get Rid of WP Version Footprint Throughout Site
function ryu_remove_version() {
return '';
}
add_filter('the_generator', 'ryu_remove_version');




// Remove Footprints and Search Engine Confusion from wp_head Function
Removes a lot of unnecessary meta data such as links to a million RSS feeds like categories, comments. Removes relational links such as previous and next from pages that don't need it.
Code:
// Remove Footprints and Search Engine Confusion from wp_head Function
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wp_generator');
remove_action('wp_head', 'start_post_rel_link');
remove_action('wp_head', 'index_rel_link');
remove_action('wp_head', 'feed_links_extra', 3 );
remove_action('wp_head', 'feed_links', 2 );
remove_action('wp_head', 'parent_post_rel_link', 10, 0 );
remove_action('wp_head', 'start_post_rel_link', 10, 0 );
remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0);




// Remove Emoji CSS and JS from header
If you aren't going to use emoji's, load one less javascript file and remove inline CSS from header
Code:
// Remove Emoji CSS and JS from header
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );




// Change the Wordpress Login Logo
For fun or for clients, change the logo on the login page to your own. You'll need to change the CSS numbers around to match your own image size. You can change the URL path or upload it to the same spot with the same name.
Code:
// Change the Wordpress Login Logo
add_action('login_head', 'ryu_custom_login_logo');
function ryu_custom_login_logo() {
    echo '<style type="text/css">
    h1 a { background-image:url('.get_template_directory_uri().'/images/login.png) !important; background-size: 311px 100px !important;height: 100px !important; width: 312px !important; margin-bottom: 30px !important; padding-bottom: 0 !important; }
    .login form { margin-top: 10px !important; }
    </style>';
}




// Change the Login Logo Link
Change the link of the logo from Wordpress to your own homepage
Code:
// Change the Login Logo Link
function ryu_url_login_logo(){
    return get_bloginfo( 'wpurl' );
}
add_filter('login_headerurl', 'ryu_url_login_logo');




// Change Login Logo Alt Text
Change the alt text from "Powered by Wordpress" to whatever you want
Code:
// Change Login Logo Alt Text
function ryu_login_logo_url_title() {
  return 'MySite.com Logo';
}
add_filter( 'login_headertitle', 'ryu_login_logo_url_title' );




// Change Dashboard Footer Text
For fun or clients, change the footer text information inside the dashboard
Code:
// Change Dashboard Footer Text
function change_footer_admin() {
  echo '<span id="footer-thankyou">My Company - Website design by <a href="#" target="_blank">Ryuzaki Eru</a></span>';
}
add_filter('admin_footer_text', 'change_footer_admin');




// Add a Dashboard Widget for Designer Contact Information
When a client needs help, make sure they know to call you instead of your competitor
Code:
// Add a Dashboard Widget for Designer Contact Information
function ryu_add_dashboard_widgets() {
  wp_add_dashboard_widget('wp_dashboard_widget', 'Theme Details', 'ryu_theme_info');
}
add_action('wp_dashboard_setup', 'ryu_add_dashboard_widgets' );
function ryu_theme_info() {
  echo "<ul>
  <li><strong>Developed By:</strong> My Company</li>
  <li><strong>Website:</strong> <a href='#' target='_blank'>www.buildersociety.com</a></li>
  <li><strong>Contact:</strong> <a href='mailto:#'>myemail@address</a></li>
<li><strong>Phone:</strong> (xxx) xxx-xxxx</li>
  </ul>";
}




// Remove replacement "fancy" quotations, etc. added by Wordpress
Wordpress changes a lot of punctuation to versions which can confuse other programs when dealing with copy and pasting. If you get encoding problems where you end up with stuff like &xxx instead of an apostrophe, this is your fix.
Code:
// Remove replacement "fancy" quotations, etc. added by Wordpress
remove_filter ('single_post_title', 'wptexturize');
remove_filter ('bloginfo', 'wptexturize');
remove_filter ('wp_title', 'wptexturize');
remove_filter ('category_description', 'wptexturize');
remove_filter ('list_cats', 'wptexturize');
remove_filter ('comment_author', 'wptexturize');
remove_filter ('comment_text', 'wptexturize');
remove_filter ('the_title', 'wptexturize');
remove_filter ('the_content', 'wptexturize');
remove_filter ('the_excerpt', 'wptexturize');




// Remove Links From the Admin Bar
Remove clutter for yourself and confusion for clients by removing extra things from the dashboard navigation menu. You can add and subtract from this with a little research.
Code:
// Remove Links From the Admin Bar
function ryu_admin_bar_render() {
    global $wp_admin_bar;
    $wp_admin_bar->remove_menu('comments');
    $wp_admin_bar->remove_menu('wp-logo');
    $wp_admin_bar->remove_menu('view-site');
    $wp_admin_bar->remove_menu('new-media');
}
add_action( 'wp_before_admin_bar_render', 'ryu_admin_bar_render' );




// Stop Empty Search Requests Redirecting to Homepage
I don't like empty search strings to redirect to the homepage. This actually searches for nothing and shows the user that they goofed up instead of confusing them.
Code:
// Stop Empty Search Requests Redirecting to Homepage
function ryu_stop_search_redirect( $vars ) {
    if( isset( $_GET['s'] ) && empty( $_GET['s'] ) )
        $vars['s'] = " ";
    return $vars;
}
add_filter( 'request', 'ryu_stop_search_redirect' );




// Adjust Yoast Breadcrumbs to Show "Here" Instead of Post Title
My post titles tend to be fairly long. Combine that with categories and sub-categories and Yoast's breadcrumbs are rendered useless. Replace the post title with "Here" or any other text to shorten it up.
Code:
// Adjust Yoast Breadcrumbs to Show "Here" Instead of Post Title
add_filter('wpseo_breadcrumb_single_link' ,'ryu_change_last_crumb', 10 ,2);
function ryu_change_last_crumb($link_output, $link ){
    $Path=$_SERVER['REQUEST_URI'];
    $URI='http://www.mydomain.com'.$Path;
    if( $link['url'] == $URI ) {
        $link_output = 'Here';
    }
    return $link_output;
}




// Unregister Some Default Widgets
Get rid of widgets you or your client won't use. Add or subtract from this list to suit your needs
Code:
// Unregister Some Default Widgets
function unregister_default_widgets() {
     unregister_widget('WP_Widget_Calendar');
     unregister_widget('WP_Widget_Archives');
     unregister_widget('WP_Widget_Meta');
     unregister_widget('WP_Widget_Search');
     unregister_widget('WP_Widget_Recent_Comments');
     unregister_widget('WP_Widget_RSS');
     unregister_widget('WP_Widget_Tag_Cloud');
}
add_action('widgets_init', 'unregister_default_widgets', 11);




// Call additional functions files for organization
I have a million functions. It helps to organize them in different files and then include the file into the main functions.php. For instance, I'll create a ton of various shortcodes and put them all in one file.
Code:
// Call additional functions files for organization
require_once('your-other-file.php');




// Create Shortcodes
Instead of pasting your adsense code in a million places, you can paste it in one spot in the functions file and use a shortcode around your site. Then if you need to change the code, you can do it once instead of hunting down a thousand places. This goes for literally anything you use more than once, like email opt-in boxes, author boxes, etc.
Code:
// Create Shortcodes
function ryuShortCode() {
    return 'Whatever Code You Need Goes  Here';
}
add_shortcode('MyAdvertisement', 'ryuShortCode');
In this example above, you'd use [MyAdvertisement] to insert the code. This is a simple version that doesn't accept parameters.




I have a lot more in my various sites that serve various functions but are too specific to the site itself. The ones above follow @juliantrueflynn 's lead and are of the copy/paste variety.
 
Another JS call that I didn't need, and comes with WP.

Code:
//Removes auto embed
function my_deregister_scripts(){
  wp_deregister_script( 'wp-embed' );
}
add_action( 'wp_footer', 'my_deregister_scripts' );

Remove jquery-migrate.min.js script (some crunchify branding in there, we need ryu branding instead :wink: )

Code:
// Remove jQuery Migrate Script from header and Load jQuery from Google API
function crunchify_remove_jquery_migrate_load_google_hosted_jquery() {
if (!is_admin()) {
wp_deregister_script('jquery');
wp_register_script('jquery', 'https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js', false, null);
wp_enqueue_script('jquery');
}
}
add_action('init', 'crunchify_remove_jquery_migrate_load_google_hosted_jquery');
 
// Disable REST API
REST API is some new bloat that's been added in the past couple of versions. It's for Apps and Plugins eventually to pull data from your site for display elsewhere. It's only for logged in users and apps given permission but it still takes some processing as it's already ready by the time you need it. Shout out to @contract for finding this code.
Code:
Removed

^ EDIT: Turns out they made it impossible to turn this off in Wordpress 4.7. They removed the filter altogether. They are forcing us to keep it live and want theme, app, and plugin creators to use it in place of ajax-admin or whatever it is.

I did find a way to remove all reference to it in the headers and to make it spit out 404 JSON data. Because in the meantime it was literally showing usernames (massive security breach). It's only accessible by authenticated users, but still... Prepare to start having your /wp-json/ sub-directory hammered by spammers and hackers. I'm considering blocking access to it entirely in .htaccess. I'll report back.

// Disable XML-RPC
XML-RPC is old tech used to remotely publish content from other apps while on the go. Stuff like IFTTT will use it so make sure you want to get rid of it. Traditionally it's stuff like Windows Live Writer and Market Samurai that used it so you could post to all of your sites from one central application.
Code:
add_filter('xmlrpc_enabled', '__return_false');

Block requests to the xmlrpc.php file in your .htaccess in case spammers are still pinging it.
Code:
# Block xmlrpc.php requests
<Files xmlrpc.php>
order allow,deny
deny from all
</Files>
 
Great info and thank you for sharing @Ryuzaki @juliantrueflynn !
Anyone know how to remove the Yoast html comments? example:<!-- / Yoast SEO plugin. -->
For some reason this bugs me.
 
@JasonSc

// Removing Yoast HTML Comments

(Must be done each time you update the plugin)
Code:
• wordpress-seo/frontend/class-frontend.php
Edit the Source Code tag comment that leaves a Yoast Footprint.
Line 616 & 1855
Change to <!-- Whatever You Want or Remove -->

• wordpress-seo/css/xml-sitemap-xsl.php
Change the XML Sitemaps page to get rid of Yoast mentions  and add your own and your site to the title.
Line 28 - Meta Title
Line 90 - H1 Title
Line 92 - Yoast stuff
 
// Stop TinyMCE from adding "noopener noreferrer" to target="_blank" links.
Wordpress is adding this but it interferes with a lot of affiliate program TOS agreements, like Amazon.
Code:
// Stop TinyMCE from adding "noopener noreferrer" to external links with target="blank"
add_filter('tiny_mce_before_init','tinymce_allow_unsafe_link_target');
function tinymce_allow_unsafe_link_target( $mceInit ) {
    $mceInit['allow_unsafe_link_target']=true;
    return $mceInit;
}
 
// Stop TinyMCE from adding "noopener noreferrer" to target="_blank" links.
Wordpress is adding this but it interferes with a lot of affiliate program TOS agreements, like Amazon.
Code:
// Stop TinyMCE from adding "noopener noreferrer" to external links with target="blank"
add_filter('tiny_mce_before_init','tinymce_allow_unsafe_link_target');
function tinymce_allow_unsafe_link_target( $mceInit ) {
    $mceInit['allow_unsafe_link_target']=true;
    return $mceInit;
}
This still relevant?
 
This still relevant?

I think they rolled that back after they realized the noreferrer was doing more damage than good. It's still in my functions though, so it's not hurting anything if you have it. If you think to remove it, I would for organization purposes. I need to do it too.
 
// Stop Wordpress From Generating Extra Image Sizes
Code:
function ryu_remove_default_image_sizes($sizes) {
    unset($sizes['thumbnail']);
    unset($sizes['medium']);
    unset($sizes['medium_large']);
    unset($sizes['large']);
    return $sizes;
}
add_filter('intermediate_image_sizes_advanced', 'ryu_remove_default_image_sizes');

This came up in another thread so I wanted to share it here too. If you create your own theme or child theme and remove the need to create the extra images Wordpress auto-generates, you can tell it to stop creating certain ones.

It used to be that Wordpress created only 3 sizes: thumbnail, medium, and large. Now it's adding medium-large too, although you won't find that in the Settings > Media page. It'll just be silently filling up your hard drive.

What I do on my own custom sites (this is a bad idea for clients who aren't skilled with photo cropping and resizing) is kill all four sizes altogether. Then I set sizes up in the CSS file to manage the responsive design and hard coded widths.

What this requires is for you to size your images and optimize them BEFORE you upload them. The result is that you only store the exact sizes you want, your server isn't creating images and slowing down the user experience for your money-making users, and you don't end up paying more for hosting and bandwidth than needed.
 
Here's a few more that'll make good starting points for anyone creating their own themes and sites.

// Don't Show Pages in Search
I don't like for my About, Contact, FAQ, and Disclosures style pages showing up in my on-site search. I only use Pages for "boilerplate" content like that and all of the normal content goes into posts. So I block pages from the search functionality. Technically, what I'm actually doing is only allowing posts, not blocking pages. Be careful if you use custom post types.
Code:
function ryu_search_filter($query) {
if ($query->is_search) {
$query->set('post_type', 'post');
} return $query; }
add_filter('pre_get_posts','ryu_search_filter');

// Remove Shortlink From Header
The world moved on from Wordpress shortlinks, which are the "real" URLs of your posts. The slugs are decorative. But we now have canonical URLs and a lot more, so there's no need for these shortlinks to even appear in your header, let alone sending Google to crawl them and waste your crawl budget.
Code:
remove_action('wp_head', 'wp_shortlink_wp_head', 10, 0);

// Remove Wordpress Responsive Image srcset image width
Wordpress version 8 or 9 introduced image srcset's, which are meant to tell mobile devices to load smaller versions of an image or double resolution versions, versus the larger version the desktop might want to load. If you've designed your site with this in mind, you won't need or want Wordpress bloating up your HTML and pointing at images that don't even exist (if you used the function in the post above).
Code:
add_filter( 'wp_calculate_image_srcset', '__return_false' );

// Featured Images / Thumbnails
If you're making your own theme, you're likely wondering where the heck the featured images box is on your posts. You have to enable them first:
Code:
if ( function_exists( 'add_theme_support' ) ) {
  add_theme_support( 'post-thumbnails' );
}

// Remove & Add Menu Items From Leftside Dashboard Menu
You may have clients that like to tinker and screw up the work you did for them. Or you may have created your own theme and will never have another, so there's no point in showing themes or the customizer. Or you don't use comments at all. You can extend this to any link on the sidebar, whether a top-level menu or sub-menu item. I added some examples to put you on the trail to understanding how to target them. You might have to do some searching, since I can't explain it all here.
Code:
function ryu_edit_admin_menus() {
    global $menu;
    global $submenu;
    remove_submenu_page('themes.php','themes.php'); // Remove Themes
    unset($submenu['themes.php'][6]); // Remove Customizer
    remove_menu_page('edit-comments.php'); // Remove Comments
    remove_submenu_page('edit.php','edit-tags.php?taxonomy=post_tag'); // Remove Tags
}
add_action( 'admin_menu', 'ryu_edit_admin_menus' );

// Reorder Sidebar Admin Menu
In the same vein, after you hack a ton of items out of the sidebar, you may want to reorder them to suit your needs or preferences. This is also possible, but again you'll have to figure out how to identify the pages. Here's an example of how you can get this done. You'll see how I've taken notes on which line is for which item using comments. Separator's create spaces between items, and if I recall there are only two, so use them wisely.
Code:
function ryu_custom_menu_order($menu_ord) {
    if (!$menu_ord) return true;
    return array(
       'index.php', // Dashboard
       'separator1', // First Separator
       'edit.php', // Posts
       'upload.php', // Media
       'edit.php?post_type=page', // Pages
       'separator2', // Second Separator
       'themes.php', // Appearance
       'plugins.php', // Plugins
       'users.php', // Users
       'options-general.php', // Settings
       'separator-last', // Third Separator
       'edit.php?post_type=acf', // Advanced Custom Fields
       'wpseo_dashboard', // Yoast
       'wpcf7', // Contact Form 7
       'WP-Optimize', // WP Optimize
       'Plugin_Organizer', // Plugin Organizer
    );
}
add_filter('custom_menu_order', 'ryu_custom_menu_order'); // Activate custom_menu_order
add_filter('menu_order', 'ryu_custom_menu_order');

// Remove Links From the Admin Bar
And you may not want some of these links in the top menu of the site either. You can remove them in the following way (a few examples, you'll have to target other items you want gone):
Code:
function ryu_admin_bar_render() {
    global $wp_admin_bar;
    $wp_admin_bar->remove_menu('comments');
    $wp_admin_bar->remove_menu('wp-logo');
    $wp_admin_bar->remove_menu('view-site');
    $wp_admin_bar->remove_menu('new-media');
}
add_action( 'wp_before_admin_bar_render', 'ryu_admin_bar_render' );

// Set Visual Editor's CSS
If you're making your own theme you might wonder why the visual editor looks like plain text and doesn't reflect your CSS design. You have to tell it which CSS file to use. You can tell it one or multiple. So it could be like:
Code:
add_editor_style('css/normalize.css');
add_editor_style('css/skeleton.css');
add_editor_style('css/style.css');
or
Code:
add_editor_style('css/special-editor-style.css');
or
Code:
add_editor_style('css/combined.min.css');

// Add Responsive Container to Embeds
As you're designing your new site you may notice that certain embeds are killing your responsive design because they aren't responsive. I don't embed tweets and all of that extra stuff, I only strictly use YouTube embeds. What this function does is wrap the embed code in a new HTML div with a class of your choosing. I called it video-container, which I then set up in my CSS to have a responsive design (fluid width with proportional height). I've not strictly kept up with all of the oEmbeds. Some or all may be responsive now. Soundcloud seems to be.
Code:
function ryu_embed_html( $html ) {
    return '<div class="video-container">' . $html . '</div>';
}
add_filter( 'embed_oembed_html', 'ryu_embed_html', 10, 3 );
add_filter( 'video_embed_html', 'ryu_embed_html' ); // Jetpack
 
Cleaning Up Wordpress HTML & Response Headers

As many of you know, I deal quite a bit with technical SEO, and often at the extreme end of things. In more severe cases, every little bit of code cleanup helps contribute to a smoothly running site with minimal potential for technical issues.

I'm gonna take a minute and focus a bit on cleaning up the <head> section of your HTML. We'll also take a look at optimizing our HTTP response headers, and other elements throughout your HTML. Most people don't bother with actually trying to tweak their response headers, let alone optimizing them.

I'm not going to claim doing so will actually contribute to your ROI. To some degree, this is an exercise in OCD. That said, certain things may or may not be present in yours, which can potentially create some issues in the long run. Also, there are some cool responses you can add, that can even positively improve the security of your WP site!


// Clean up output of stylesheet <link> tags

One cool thing about HTML5 specifications is that, there are a lot of traditional code components that are no longer necessary. Browsers are intelligent enough now to understand exactly what certain, common HTML components are. For example, type='text/css' attributes on the CSS <link> tags in your site <head>. With HTML5, you don't actually need those attributes. This function will remove them.

Code:
function clean_style_tag($input) {
 
  preg_match_all("!<link rel='stylesheet'\s?(id='[^']+')?\s+href='(.*)' type='text/css' media='(.*)' />!", $input, $matches);
  if (empty($matches[2])) {
    return $input;
  }

  // Only display media if it is meaningful
  $media = $matches[3][0] !== '' && $matches[3][0] !== 'all' ? ' media="' . $matches[3][0] . '"' : '';
  return '<link rel="stylesheet" href="' . $matches[2][0] . '"' . $media . '>' . "\n";

}
add_filter( 'style_loader_tag', 'clean_style_tag' );


// Clean up output of <script> tags
Just like the removal of the CSS attributes above, this next function will do the same thing. It'll remove type='text/javscript' attributes from your <script> tags. Again, not gonna put money in your bank account. Though, if you're trying to clean up your site code as much as possible, here's one more thing you can check off the list.

Code:
function clean_script_tag($input) {
  $input = str_replace("type='text/javascript' ", '', $input);
  return str_replace("'", '"', $input);
}
add_filter( 'script_loader_tag', 'clean_script_tag' );


// Remove pingback header
If you're not actually using pingbacks on your site, you probably don't need to be sending a pingback response header on every pageview. Even if you've disabled pingbacks in your WP settings, this response header is usually still sent. This function will remove that.

Code:
function filter_headers($headers) {
  if (isset($headers['X-Pingback'])) {
    unset($headers['X-Pingback']);
  }
  return $headers;
}
add_filter( 'wp_headers', 'filter_headers', 10, 1);


// Add HTTP response security headers
Now let's step up our site security game! It's possible to add response headers which can actually help secure your site. With all the fuss about SSL (aka TLS) these days, enhanced site security indicators can only be a good thing. You may even get a bit of SEO side benefit from it.

With the Strict Transport Security header (aka HSTS), there are several attributes it can have. You'll want to research these a bit and tweak them on your own. Be aware, this tells browsers to only ever load your site over https. So if you're not careful, you could run into some issues if parts of your site are still http. The includeSubDomains attribute acts as sort of a catch-all, basically saying https-only for your entire site.

The X-Frame-Options header is pretty simple, and will let you specify what iframe behavior you want the browser to respect. In my case, I almost never use iframes for any purpose, so I just straight up DENY them all. This is helpful in extreme cases, where you've made a successful site and now spammers are iframing your site on theirs, jacking your clicks. Beware, this may break some tracking scripts and other items. Basically anything in an iframe.

X-XSS-Protection, I'd just have that on for any site honestly.

X-Content-Type-Options can mitigate certain browser vulnerabilities in <script> or <style> tags not being used appropriately in the browser.

Referrer-Policy is one you'll need to research and decide what attribute to use. Beware, depending on the attribute you choose, you could potentially lose referral info on your traffic! This is useful if you have user accounts and need to maintain strict control, preventing things like user data leaks from cross http/https traffic.

I specifically excluded one type of security header, Content-Security-Policy. For most of our purposes, you'll probably find it extremely difficult to near impossible to implement without screwing up your site. Modern browsers will respect that response pretty ruthlessly, and will refuse to load resources, images, scripts, and other things if they fall outside your policies. Great for security, but terrible for the marketer when it's stuff you DO want to load!

After implementing these, feel free to test your site with one of these tools:
Securityheaders.io
Qualys SSL Labs

Code:
function add_security_headers() {
    // Enforce the use of HTTPS
    header( "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload" );

    // Prevent Clickjacking
    header( "X-Frame-Options: DENY" );

    // Block Access If XSS Attack Is Suspected
    header( "X-XSS-Protection: 1; mode=block" );

    // Prevent MIME-Type Sniffing
    header( "X-Content-Type-Options: nosniff" );

    // Referrer Policy
    header( "Referrer-Policy: strict-origin-when-cross-origin" );
}
add_action( 'send_headers', 'add_security_headers', 1 );


// Change certain URLs to relative links
This probably won't be useful to most of you. In certain projects I've been involved in, things like EMD/PMD domains, internal linking, etc. in extreme cases can lead to severe over-optimization across a site. Although I make no claims as to the efficacy of this helping improve any of that, the theory is this.

Potentially if you change some links from absolute to relative, theoretically it may help you reduce the frequency of keyword appearance in your HTML. This is purely theory, and to-date, I've been unable to achieve clear attribution from this to a positive effect on Panda penalties. This may not even be a thing, since bots still have to parse the HTML, convert relative links to absolute, so they can add them to the crawl list. It may not matter at all.

All of that aside, should it be something you want to experiment with, adjust the filters array with the appropriate Wordpress hooks. For example, maybe you don't want all links to be relative. Maybe you want to just try setting all nav, sidebar, category, etc. links to relative. You know, the sitewide type stuff. This will do that.

Code:
function custom_relative_urls() {
    // Don't do anything if:
    // - In feed
    // - In sitemap by WordPress SEO plugin
    if ( is_feed() || get_query_var( 'sitemap' ) )
        return;

    $filters = array(
        'post_link',
        'post_type_link',
        'page_link',
        'attachment_link',
        'get_shortlink',
        'post_type_archive_link',
        'get_pagenum_link',
        'get_comments_pagenum_link',
        'term_link',
        'search_link',
        'day_link',
        'month_link',
        'year_link',
    );
    foreach ( $filters as $filter )
    {
        add_filter( $filter, 'wp_make_link_relative' );
    }
}
add_action( 'template_redirect', 'custom_relative_urls' );


// Remove unnecessary self-closing tags
This is another HTML5 thing. Self-closing tags are no longer necessary in the current iteration of the HTML spec. What I mean by that is, stuff like <meta>, <link>, etc. tags don't need the trailing forward slash anymore. Beware, older browsers may or may not have issues with this. If a significant percentage of your user base is still on much older browsers, like IE7 (God forbid!), your site may not load correctly. Again, like with most of the recommendations in this particular post, this is OCD-level stuff that I can almost guarantee does nothing for ROI. Act accordingly.

Code:
function remove_self_closing_tags($input) {
  return str_replace(' />', '>', $input);
}
add_filter('get_avatar', 'remove_self_closing_tags'); // <img />
add_filter('comment_id_fields', 'remove_self_closing_tags'); // <input />
add_filter('post_thumbnail_html', 'remove_self_closing_tags'); // <img />
 
A lot of these functions can be done through the plugin "wp hide and security enhancer" for the ones that don't want to go play in the functions.php
 
// Adjust Yoast Breadcrumbs to Show "Here" Instead of Post Title
My post titles tend to be fairly long. Combine that with categories and sub-categories and Yoast's breadcrumbs are rendered useless. Replace the post title with "Here" or any other text to shorten it up.

Couldn't this screw up your SEO? Telling Google the last item is "Here", or mess up the breadcrumbs you sometimes see in SERPs?
 
Couldn't this screw up your SEO? Telling Google the last item is "Here", or mess up the breadcrumbs you sometimes see in SERPs?

I only display it on the post page, not on categories, so the breadcrumb schema only shows up there. Only the post titles ever become "here," not any other type of page or category. Google already knows the title of the page from being in <title> and <h1> tags. As long as the rest of the breadcrumb hierarchy is intact, it's fine.

My breadcrumbs appear just fine in the SERPS, and that's because the breadcrumbs there never show the post title, only the category hierarchy.
 
I only display it on the post page, not on categories, so the breadcrumb schema only shows up there. Only the post titles ever become "here," not any other type of page or category. Google already knows the title of the page from being in <title> and <h1> tags. As long as the rest of the breadcrumb hierarchy is intact, it's fine.

My breadcrumbs appear just fine in the SERPS, and that's because the breadcrumbs there never show the post title, only the category hierarchy.

Gotcha. Having "Here" just looks weird to me though :smile:

Instead I limit the max number of characters:

Code:
/* Limit length of NavXT breadcrumbs on mobiles */
@media only screen and (max-width: 799px){
.breadcrumbs span[property="name"] {
    display: inline-block;
    padding: 0;
    margin-top: -3px;
    vertical-align: middle;
    max-width: 120px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }}
 
// Clean up output of <script> tags
Just like the removal of the CSS attributes above, this next function will do the same thing. It'll remove type='text/javscript' attributes from your <script> tags. Again, not gonna put money in your bank account. Though, if you're trying to clean up your site code as much as possible, here's one more thing you can check off the list.

Code:
function clean_script_tag($input) {
$input = str_replace("type='text/javascript' ", '', $input);
return str_replace("'", '"', $input);
}
add_filter( 'script_loader_tag', 'clean_script_tag' );

Just wanted to give everyone a quick update. Something I discovered is, this particular function seems to break the current Gutenberg plugin from functioning. Being that Gutenberg is React JS-based, it makes sense that manipulating script tags might have unintended consequences.

I've been experimenting with the plugin a bit lately. I found that if I had this function present, the post editor would not even load. I'm sure 99% of most will never even use this, as it's more of an exercise in OCD. That said, if you do use it and want to experiment with Gutenberg, just comment the function out.
 
I specifically excluded one type of security header, Content-Security-Policy. For most of our purposes, you'll probably find it extremely difficult to near impossible to implement without screwing up your site. Modern browsers will respect that response pretty ruthlessly, and will refuse to load resources, images, scripts, and other things if they fall outside your policies. Great for security, but terrible for the marketer when it's stuff you DO want to load!

I've been using the below: as long as you're sure everything on your domain is https and you're certain all off domain inserts/media networks and such are also https: this will give you the security rating without to much intrusion. (super easy if you're building from scratch)

Code:
// Content Policy
    header( "Content-Security-Policy: default-src https:" );

security_headers_ss.jpg


:evil:
 
// Content Policy header( "Content-Security-Policy: default-src https:" );
Thanks, and if some HTTP content happens to get added (maybe an ad network passback for example) what happens to the page?
 
Thanks, and if some HTTP content happens to get added (maybe an ad network passback for example) what happens to the page?

I wouldn't mess with this if you have 3rd party display ads. You'll have to constantly be trying to watch for and whitelist a ton of domains and files. It'd be impossible. @turbin3 and I just talked about this in his thread, starting at this post and going down from there. I think it's default behavior at this point for nearly all user-agents and browsers to simply not show HTTP content on HTTPS pages, while throwing up a mixed-content error in the console for developers. I don't see this directive as being necessary at this point in time.
 
@turbin3 and I just talked about this in his thread,
My bad, I missed that thread.
it's default behavior at this point for nearly all user-agents and browsers to simply not show HTTP content on HTTPS pages, while throwing up a mixed-content error in the console for developers.
This is what happens, white listing works, but for add network heavy sites @Ryuzaki is right, it would be constant nightmare.

It suits my current project as I want to keep it squeaky clean from the start, it won't be running add networks for a long while, if at all.
 
If it was me, I'd only use a CSP header for a site that wasn't ad network-focused. Like say a brand or agency site, where the number of external resources may be limited and controlled. In that case, I might do it, just to max out the security factor as far as HTTP response headers are concerned. Not gonna win you any ranks or help pad the bank account though.

To date, I've only had 1 site where I implemented a CSP response header. I ended up dropping that CSP. It was an informational type site. I was using Google DFP, Adsense, and a mix of other networks. I realized life was too short to be worrying about individual HTTP responses succeeding. I tried briefly to implement it on 2 or 3 other sites, but quickly abandoned it due to difficulty in keeping tracking scripts and pixels working properly.
 
// Remove Yoast comments from website source code
This code can be placed in your theme's functions.php file. That way it will persist through plugin upgrades. This function will work for both the free and premium versions of Yoast.

This has worked for me for awhile, since roughly WP 4.6 or so, though all of my Wordpress sites are now on 5.1 as of the date of this post. If you have any issues with it, PM me and we can figure it out. The code has several conditionals in case Yoast changes something that breaks or renames the main class being manipulated.

Feel free to ignore all the code comments. Figured I would link to the docs in case any of you are curious about the actual classes or hooks, and how they work.

PHP:
/**
 * Remove All Yoast HTML Comments. Works for free or premium version.
 * https://developer.yoast.com/code-documentation/hooks/wpseo_head/
 * https://developer.yoast.com/code-documentation/classes/wpseo_frontend/
 * https://developer.yoast.com/code-documentation/classes/wpseo_frontend/get_instance/
 * https://developer.yoast.com/code-documentation/classes/wpseo_frontend/debug_mark/
 * https://developer.wordpress.org/reference/hooks/template_redirect/
 */
function remove_yoast_comments() {
  // Conditional in case future versions break this class
  if ( ! class_exists( 'WPSEO_Frontend' ) ) {
    return;
  }

  // Get the class so we can manipulate it
  $instance = WPSEO_Frontend::get_instance();

  // Conditional in case we cannot get the WPSEO_Frontend class
  if ( ! method_exists( $instance, 'debug_mark' ) ) {
    return;
  }

  // Obliterate Yoast comments off the face of the earth
  remove_action( 'wpseo_head', array( $instance, 'debug_mark' ), 2 );
}
add_action( 'template_redirect', 'remove_yoast_comments', 9999 );
 
@turbin3, all we need now is to automatically yank their crap out of the sitemap_index.xml. I've been removing the HTML comments and the junk in the sitemap manually each update (as seen above). Takes a few minutes only but why bother if we can remove_action. I'm going to look into it and will post the solution if I can figure it out. Or if you can fly through it I'd appreciate the share.

For clarity, I'm talking about the HTML comment in the Sitemap as well as this chunk of text and the links in it:
Code:
Generated by YoastSEO, this is an XML Sitemap, meant for consumption by search engines.

You can find more information about XML sitemaps on sitemaps.org.

I'm not sure it'll be possible. Some of it's built right into the HTML without any hooks: wordpress-seo/css/main-sitemap.xsl on lines 72 and 74-75. The comments are found here: wordpress-seo/inc/sitemaps/class-sitemaps-renderer.php on line 156, but is within a big chunk of concatenated string. Not sure if that can be filtered.

For anyone reading, it's not a big deal to have that in there, it's just annoying for them to slap their branding and extraneous crap everywhere, including in the freaking sitemaps on the surface and in the source code.
 
Back