Skip to content

Instantly share code, notes, and snippets.

@ApoGouv
Last active December 21, 2023 10:05
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save ApoGouv/daf833c23911c5f062f76a8e70ef23bc to your computer and use it in GitHub Desktop.
Save ApoGouv/daf833c23911c5f062f76a8e70ef23bc to your computer and use it in GitHub Desktop.
WP - Optimizations, Security hardening and Cleanup.

** WP - Optimizations, Security hardening and Cleanup **

Here you can find a a list of WP optimization and cleanup functions and methodology.

1.  .htaccess
  ** Security **
- Disable the server signature
- Disable directory browsing
- Force https to your site.
- Prevent access to specific files and folders (Wordpress specific and in general).*
- Block WordPress Author Scans
- Block WordPress xmlrpc.php requests
- Block Requests by Referer
- Block "bad" bots
- Block HTTP 1.0 and Empty User Agent requests**
- Specify Security HEADERS
- Protect System Files, Disable PHP in Uploads, Filter Request Methods
-
- Last modified on 21-12-2023 
    - Rearranged custom rewrite rules and gathered them together.
    - Combined crawling bot block.
    - Added some more specific file blocking.

*For Apache 2.4+
**May brake functionalities! e.g. usually on websites with xml feeds that others fetch programmatically with curl/wget etc.

  ** Performance **
- Enable Browser Cache for your static files. This will fix the "Leverage Browser Caching" in the google pagespeed results.
- Enable file compreassion for faster page load.

2) wp-config.php

- Set secure cookies!
- Example of how to enable wp-debug and log the errors, during development.
- Limit posts revisions, in order to have smaller DB.
- Force wp-admin to SSL.
- Disable Theme and Plugin backend editor.
- Change the update method. /!\  This is usefull usually when bringing a site from server to localhost.

3) functions.php

- Organize your functions.php by including your custom files for better readability. You can seperate you custom files,
by responsibility, by back/frond end, by what they modify (e.g. woocommerce mods).

3.1) cds-wp-admin-tweaks-n-extras.php

- Example custom function to add an extra column to the admin area of the posts list.

3.2) cds-optimizations-n-tweaks.php

-- Example custom functions for optimizing WP and removing tags for security

- Clean wp_head. Remove unnecessary links, scripts and the_generator (the wp one) from <head>.
- Remove WPML generator.
- Remove Slider Revolution generator.
- Remove Visual Composer / WPBakery Page Builder generator.
- Remove comment-reply.min.js completely.
- Remove jQuery Migrate Script from header and Load jQuery from Google API.
- Remove Yoast SEO comments.
- Remove Query Strings (the version) from script and style files, 
  some files won't be cached if there is a query string in them. /!\ may produce errors! check carefully!
-  remove wp version param from any enqueued scripts (using wp_enqueue_script()) or styles (using wp_enqueue_style()).

3.3) varius_extra_functions.php
Generic snippets for specific functionalities.

4) wp_generic_security_server_config.md

Contains rules for Apache in order to enhance security of WP.

*Rules in server config should be preferred over .htaccess for performance. The entries in 01.htaccess can be converted to server config rules.

5) wp_generic_plugin_development.md

Contains generic WP Plugin deveopment links and quick guide to setup PHPCS with VS Code.

6) wp_change_db_table_prefix.md

Instructions to rename default wp_ DB table prefix, after WP have been installed.

# Disable the server signature
ServerSignature Off

# Add default charset
AddDefaultCharset UTF-8

# Disable directory browsing
Options -Indexes

# Custom rewrite rules to block usual suspects and
# enhance security, like force redirect to https.
<IfModule mod_rewrite.c>
RewriteEngine On

RewriteBase /

# Redirect http to https
#
# RewriteCond %{HTTPS} !=on
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]
#

# Redirect www to non-www and http to https
#
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,NE,R=301]

# Redirect non-www to www and http to https
#
# RewriteCond %{HTTP_HOST} !^www\. [NC,OR]
# RewriteCond %{HTTPS} off
# RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
# RewriteRule ^ https://www.%1%{REQUEST_URI} [R=301,L,NE]

# ----------------------------------------------------------------------

# Block WordPress Author Scans
RewriteCond %{QUERY_STRING} (author=\d+) [NC]
RewriteRule ^ - [NC,F,L]

# ----------------------------------------------------------------------

# Block Requests by Referer
# Referers can be retrieved from Webalyzer stats on cPanel.
# anonymousfox is left both as an example and cause it is the usual suspect.
RewriteCond %{HTTP_REFERER} anonymousfox\.co
RewriteRule ^ - [NC,F,L]

# ----------------------------------------------------------------------

# Block Requests to specific file
# RewriteCond %{REQUEST_URI} ^/entity.php
# RewriteRule ^ - [NC,F,L]

# ----------------------------------------------------------------------

# ## START - ATTENTION ##
# ## Following two rules are for blocking HTTP 1.0 Requests and Requests with Empty HTTP_USER_AGENT String.
# ## This will block also the majority of programatic request like with wget or curl.
# ## Further exceptions may apply to your setup or just disable the rules.
# ##
# ##

# Block HTTP/1.0 Requests
# Add exception for specific folder. e.g. an images folder.
# Add exception for server's own IP.
RewriteCond %{REQUEST_URI} !^/uploads [NC,OR]
RewriteCond %{REQUEST_URI} !^/wp\-content/uploads [NC]
# RewriteCond %{REMOTE_ADDR} !^(xxx\.xxx\.xxx\.xxx)
RewriteCond %{SERVER_PROTOCOL} ^HTTP/1\.0$
RewriteRule ^ - [NC,F,L]

# ----------------------------------------------------------------------

# Block Requests with Empty HTTP_USER_AGENT String
# Reg ex ^(?:\s|-)*$ matches any whitespace character or the character "-"
# Add exception for specific folder. e.g. an images folder.
# Add exception for server's own IP.
RewriteCond %{REQUEST_URI} !^/uploads [NC,OR]
RewriteCond %{REQUEST_URI} !^/wp\-content/uploads [NC]
# RewriteCond %{REMOTE_ADDR} !^(xxx\.xxx\.xxx\.xxx)
RewriteCond %{HTTP_USER_AGENT} ^(?:\s|-)*$
RewriteRule ^ - [NC,F,L]

# ##
# ##
# ## END - ATTENTION ##

# ----------------------------------------------------------------------

# 6G:[USER AGENT] + Custom Block common crawling spam Bots
RewriteCond %{HTTP_USER_AGENT} (?:virusbot|spambot|evilbot|acunetix|BLEXBot|domaincrawler\.com|LinkpadBot|MJ12bot|MJ12bot/v|majestic12\.co\.uk|AhrefsBot|TwengaBot|SemrushBot|nikto|winhttp|Xenu\s+Link\s+Sleuth|Baiduspider|HTTrack|clshttp|harvest|extract|grab|miner|python-requests|binlar|casper|checkpriv|choppy|cmsworld|diavol|dotbot|feedfinder|flicky|g00g1e|heritrix|httrack|kmccrew|loader|nutch|planetwork|postrank|purebot|pycurl|python|seekerspider|siclab|skygrid|sqlmap|sucker|turnit|vikspider|xxxyy|youda|zmeu|zune|ZoominfoBot|Konqueror|survey|SeznamBot|serendeputy|DuckDuckGo-Favicons-Bot|DuckDuckBot|Go-http-client|yak-linkfluence|libwww-perl) [NC]
RewriteRule ^(.*)$ - [F]

# ----------------------------------------------------------------------

# Protect System Files, Disable PHP in Uploads, Filter Request Methods, by iThemes Security
# Protect System Files
RewriteRule ^wp-admin/install\.php$ - [F]
RewriteRule ^wp-admin/includes/ - [F]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F]
RewriteRule ^wp-includes/theme-compat/ - [F]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (^|.*/)\.(git|svn)/.* - [F]

# Disable PHP in WP Uploads folder
RewriteRule ^wp\-content/uploads/.*\.(?:php[1-7]?|pht|phtml?|phps)\.?$ - [NC,F]

# Filter Request Methods
# Disable connect, debug, move, put, trace and track
RewriteCond %{REQUEST_METHOD} ^(connect|debug|move|trace|track) [NC]
RewriteRule ^.* - [F]

</IfModule>

# ----------------------------------------------------------------------
# | Access Control                                                     |
# ----------------------------------------------------------------------

# "Block access to .htaccess and .htpasswd"
<FilesMatch ^(?i:\.ht.*)$>
    Require all denied
</FilesMatch>

# "Block unauthorized access to wp-config.php"
# To remove this rule, revert this security measure on each WordPress installation on this domain
<Files wp-config.php>
    Require all denied
</Files>

# Block WordPress xmlrpc.php requests
<Files xmlrpc.php>
    Require all denied
</Files>

# Prevent access to specific files
<FilesMatch "\.(htaccess|htpasswd|ini|phps|fla|psd|log|sh|cmd|exe|bat|csh|txt|zip|tar|gz|7z|rar)$">
    Require all denied
</FilesMatch>

# Block Access to WP and Generic sensitive files
<FilesMatch "^.*(((?:wp-config)\.(?:php|bak|swp))|php.ini|.[hH][tT][aApP].*|((?:error_log|readme|license|changelog|-config|-sample)\.(?:php|md|log|txt|htm|html)))$">
	Require all denied
</FilesMatch>


# Security Headers
# May cause errors, usually when loading external scripts, iframes etc..
<IfModule mod_headers.c>

    # Remove X-Powered-By header
    Header unset X-Powered-By
    Header always unset X-Powered-By

    # Use HTTP Strict Transport Security to force client to use secure connections only.
    # max-age=15768000 is 6 Months
    Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"

    # Enforce enable the cross-site scripting (XSS) filter built into modern web browsers.
    Header set X-XSS-Protection "1; mode=block"

    # Provide clickjacking protection by not allowing iframes to load our website.
    header always set x-frame-options "SAMEORIGIN"

    # Prevent IE and Chrome from sniffing a response away from the declared content-type. This helps reduce the danger of drive-by downloads and helps treat the content the right way.
    Header always set x-content-type-options "nosniff"

    # Activate CORS
    <FilesMatch "\.(ttf|ttc|otf|eot|woff|woff2|font.css|css|js|gif|png|jpe?g|svg|svgz|ico|webp)$">
        Header set Access-Control-Allow-Origin "*"
    </FilesMatch>
    # Set the Referrer-Policy
    # no-referrer-when-downgrade - The origin, path, and querystring of the URL are sent as a referrer when
    #                              the protocol security level stays the same or improves,
    #                              but isn't sent to less secure destinations.
    Header set Referrer-Policy "no-referrer-when-downgrade"
</IfModule>


# BEGIN WordPress

RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

# END WordPress



# ######################################################################
# # MEDIA TYPES AND CHARACTER ENCODINGS                                #
# ######################################################################

# ----------------------------------------------------------------------
# | Media types                                                        |
# ----------------------------------------------------------------------

# Serve resources with the proper media types (f.k.a. MIME types).
#
# https://www.iana.org/assignments/media-types/media-types.xhtml
# https://httpd.apache.org/docs/current/mod/mod_mime.html#addtype

<IfModule mod_mime.c>

  # Data interchange

    AddType application/atom+xml                        atom
    AddType application/json                            json map topojson
    AddType application/ld+json                         jsonld
    AddType application/rss+xml                         rss
    AddType application/geo+json                        geojson
    AddType application/rdf+xml                         rdf
    AddType application/xml                             xml


  # JavaScript

    # Servers should use text/javascript for JavaScript resources.
    # https://html.spec.whatwg.org/multipage/scripting.html#scriptingLanguages

    AddType text/javascript                             js mjs


  # Manifest files

    AddType application/manifest+json                   webmanifest
    AddType application/x-web-app-manifest+json         webapp
    AddType text/cache-manifest                         appcache


  # Media files

    AddType audio/mp4                                   f4a f4b m4a
    AddType audio/ogg                                   oga ogg opus
    AddType image/avif                                  avif
    AddType image/avif-sequence                         avifs
    AddType image/bmp                                   bmp
    AddType image/jxl                                   jxl
    AddType image/svg+xml                               svg svgz
    AddType image/webp                                  webp
    AddType video/mp4                                   f4v f4p m4v mp4
    AddType video/ogg                                   ogv
    AddType video/webm                                  webm
    AddType video/x-flv                                 flv

    # Serving `.ico` image files with a different media type prevents
    # Internet Explorer from displaying them as images:
    # https://github.com/h5bp/html5-boilerplate/commit/37b5fec090d00f38de64b591bcddcb205aadf8ee

    AddType image/x-icon                                cur ico


  # WebAssembly

    AddType application/wasm                            wasm


  # Web fonts

    AddType font/woff                                   woff
    AddType font/woff2                                  woff2
    AddType application/vnd.ms-fontobject               eot
    AddType font/ttf                                    ttf
    AddType font/collection                             ttc
    AddType font/otf                                    otf


  # Other

    AddType application/octet-stream                    safariextz
    AddType application/x-bb-appworld                   bbaw
    AddType application/x-chrome-extension              crx
    AddType application/x-opera-extension               oex
    AddType application/x-xpinstall                     xpi
    AddType text/calendar                               ics
    AddType text/markdown                               markdown md
    AddType text/vcard                                  vcard vcf
    AddType text/vnd.rim.location.xloc                  xloc
    AddType text/vtt                                    vtt
    AddType text/x-component                            htc

</IfModule>


# ##################################
#           PERFORMANCE
# ##################################

# START ENABLE KEEP ALIVE
<IfModule mod_headers.c>
Header set Connection keep-alive
</IfModule>
# END ENABLE KEEP ALIVE

# ######################################################################
# # WEB PERFORMANCE                                                    #
# ######################################################################

# ----------------------------------------------------------------------
# | Compression                                                        |
# ----------------------------------------------------------------------

<IfModule mod_deflate.c>

    # Force compression for mangled `Accept-Encoding` request headers
    #
    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
    # https://calendar.perfplanet.com/2010/pushing-beyond-gzipping/

    <IfModule mod_setenvif.c>
        <IfModule mod_headers.c>
            SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
            RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
        </IfModule>
    </IfModule>

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Compress all output labeled with one of the following media types.
    #
    # https://httpd.apache.org/docs/current/mod/mod_filter.html#addoutputfilterbytype

    # Compress all output labeled with one of the following media types.
    <IfModule mod_filter.c>
        AddOutputFilterByType DEFLATE application/javascript
        AddOutputFilterByType DEFLATE application/rss+xml
        AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
        AddOutputFilterByType DEFLATE application/x-font
        AddOutputFilterByType DEFLATE application/x-font-opentype
        AddOutputFilterByType DEFLATE application/x-font-otf
        AddOutputFilterByType DEFLATE application/x-font-truetype
        AddOutputFilterByType DEFLATE application/x-font-ttf
        AddOutputFilterByType DEFLATE application/x-javascript
        AddOutputFilterByType DEFLATE application/xhtml+xml
        AddOutputFilterByType DEFLATE application/xml
        AddOutputFilterByType DEFLATE font/opentype
        AddOutputFilterByType DEFLATE font/otf
        AddOutputFilterByType DEFLATE font/ttf
        AddOutputFilterByType DEFLATE image/svg+xml
        AddOutputFilterByType DEFLATE image/x-icon
        AddOutputFilterByType DEFLATE text/css
        AddOutputFilterByType DEFLATE text/html
        AddOutputFilterByType DEFLATE text/javascript
        AddOutputFilterByType DEFLATE text/plain
        AddOutputFilterByType DEFLATE text/xml
    </IfModule>

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Map the following filename extensions to the specified encoding type in
    # order to make Apache serve the file types with the appropriate
    # `Content-Encoding` response header (do note that this will NOT make
    # Apache compress them!).
    #
    # If these files types would be served without an appropriate
    # `Content-Encoding` response header, client applications (e.g.: browsers)
    # wouldn't know that they first need to uncompress the response, and thus,
    # wouldn't be able to understand the content.
    #
    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
    # https://httpd.apache.org/docs/current/mod/mod_mime.html#addencoding

    <IfModule mod_mime.c>
        AddEncoding gzip              svgz
    </IfModule>

</IfModule>

# ----------------------------------------------------------------------
# | ETags                                                              |
# ----------------------------------------------------------------------

# Remove `ETags` as resources are sent with far-future expires headers.
#
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
# https://developer.yahoo.com/performance/rules.html#etags
# https://tools.ietf.org/html/rfc7232#section-2.3

# `FileETag None` doesn't work in all cases.
<IfModule mod_headers.c>
    Header unset ETag
</IfModule>

FileETag None

# ----------------------------------------------------------------------
# | Cache expiration                                                   |
# ----------------------------------------------------------------------

# Serve resources with a far-future expiration date (a little above 3 months).
#
# (!) If you don't control versioning with filename-based cache busting, you
# should consider lowering the cache times to something like one week.

<IfModule mod_expires.c>
    # Enable expirations
    ExpiresActive On
    # Default expiration: 1 hour after request
    ExpiresDefault                              "access plus 1 month"
    
    # cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
    ExpiresByType text/cache-manifest           "access plus 0 seconds"
    # Your document html
    ExpiresByType text/html                     "access plus 0 seconds"
    # Data
    ExpiresByType text/xml                      "access plus 0 seconds"
    ExpiresByType application/xml               "access plus 0 seconds"
    ExpiresByType application/json              "access plus 0 seconds"
    # Feed
    ExpiresByType application/rss+xml           "access plus 1 hour"
    ExpiresByType application/atom+xml          "access plus 1 hour"
    # Favicon (cannot be renamed)
    ExpiresByType image/x-icon                  "access plus 1 week"
    # Media: images, video, audio
    ExpiresByType image/gif                     "access plus 6 months"
    ExpiresByType image/png                     "access plus 6 months"
    ExpiresByType image/jpeg                    "access plus 6 months"
    ExpiresByType image/webp                    "access plus 6 months"
    ExpiresByType video/ogg                     "access plus 6 months"
    ExpiresByType audio/ogg                     "access plus 6 months"
    ExpiresByType video/mp4                     "access plus 6 months"
    ExpiresByType video/webm                    "access plus 6 months"
    ExpiresByType image/avif                    "access plus 6 months"
    ExpiresByType image/avif-sequence           "access plus 6 months"
    # HTC files  (css3pie)
    ExpiresByType text/x-component              "access plus 1 month"
    # Webfonts
    ExpiresByType font/ttf                      "access plus 6 months"
    ExpiresByType font/otf                      "access plus 6 months"
    ExpiresByType font/woff                     "access plus 6 months"
    ExpiresByType font/woff2                    "access plus 6 months"
    ExpiresByType image/svg+xml                 "access plus 6 months"
    ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
    # CSS and JavaScript
    ExpiresByType text/css                      "access plus 1 year"
    ExpiresByType application/javascript        "access plus 1 year"
</IfModule>
# Block PHP Files in Uploads Directory
<FilesMatch .*\.ph.*$>
	Require all denied
</FilesMatch>
# Block Forbid PHP exec by SOFTACULOUS
<FilesMatch .*\.ph.*$>
    Require all denied
</FilesMatch>
<?php
// Secure Cookies (add me on TOP, at the beginning of the file)
@ini_set('session.cookie_httponly', true);
@ini_set('session.cookie_secure', true);
@ini_set('session.use_only_cookies', true);



// WP memory settings
// define('WP_MEMORY_LIMIT', '256M');

// Enable debug log in wp-content/debug.log
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);

// Limit revisions to 2
define('WP_POST_REVISIONS', 2);

// WordPress uses Ajax to auto-save revisions to the post as you edit.
// You may want to increase this setting for longer delays in between auto-saves, 
// or decrease the setting to make sure you never lose changes. The default is 60 seconds.
define('AUTOSAVE_INTERVAL', 120 );

// OR 
// Disable revisions
// define('WP_POST_REVISIONS', false);

// This setting controls the number of days before WordPress permanently deletes 
// posts, pages, attachments, and comments, from the trash bin. The default is 30 days:
define('EMPTY_TRASH_DAYS', 30 );

// Require SSL for Admin and Logins
define( 'FORCE_SSL_ADMIN', true );

// Disable the Plugin and Theme Editor
define( 'DISALLOW_FILE_EDIT', true );

// Disable script concatenation in Admin panel. 
// Will increase load time slightly but you can debug and view each js file seperately.
define('CONCATENATE_SCRIPTS', false);

// Solve problem of wordpress asking for ftp 
// when updating plugins in localhost
// define('FS_METHOD','direct');


/**
 * The WP-CRON Problem: 
 * 
 * WP-CRON does not work the same way as a normal cron job. It instead checks for scheduled events each time a 
 * site page is loaded. 
 * This works just fine with a steady stream of moderate traffic but issues arise with the two extremes.
 * 
 * High traffic — If the site gets too much traffic, it is continuously checking it’s WP-CRON schedule, 
 *                increasing the work required by the server, and negatively impacting performance.
 * 
 * Low traffic — Conversely, if there is little to no traffic, the site does not check it’s scheduled items 
 *               quickly enough and may miss scheduled jobs, like a backup from a plugin or a scheduled post.
 *
 * Streamlining this function is a two-part process:
 *
 * 1. Creating a standard cron which will run any scheduled events on your site.
 *    e.g. wget -q -O - https://example.com/wp-cron.php?doing_wp_cron
 * -Replace https://example.com with your domain name.-
 *
 * 2. Stopping WP-CRON from constantly checking for those same scheduled events.
 */
// (2) Disable WP Cron. 
// define('DISABLE_WP_CRON', true) ;


// Following comes from wp-toolkit and they say it can prevent some
// DDoS attacks.
//
// Disable scripts concatenation for WordPress admin panel
// define( 'CONCATENATE_SCRIPTS', false );
//
// IF above rule is set to FALSE, also add following rules to
// .htaccess in order to deny URLs containing the string load-styes or load-scripts:
//
// # "Disable scripts concatenation for WordPress admin panel"
// <Directory "/home/example/public_html/wp-admin">
//   <FilesMatch (load-styles|load-scripts)\.php$>
//      Require all denied
//   </FilesMatch>
// </Directory>

/**  
 * /!\ CAN CAUSE ERRORS /!\
 * This will block ALL external requests.
 * 
 * This is here for reference only.
 */
// Block external URL requests by defining WP_HTTP_BLOCK_EXTERNAL as true and this will only allow localhost and your blog to make requests
// define( 'WP_HTTP_BLOCK_EXTERNAL', true );
// define( 'WP_ACCESSIBLE_HOSTS', 'api.wordpress.org,*.github.com' ); //comma separated list of hostnames to allow, wildcard domains are supported
<?php
/**
 * CubeDesigns - Wordpress Optimizations and Tweaks
 *
 * Some of the below function are from the Plugin: Meta Generator and Version Info Remover
 * (https://wordpress.org/plugins/meta-generator-and-version-info-remover/)
 */


/************************************************************************
 * Header items cleaning.
 *
 * @return void
 ***********************************************************************/
function cds_clean_the_header_outout() {
    remove_action( 'wp_head', 'wp_generator' ); // Remove WP Generator Vesion.
    remove_action( 'wp_head', 'wp_resource_hints', 2 ); // Remove s.w.org DNS-Prefetch.
    remove_action( 'wp_head', 'wlwmanifest_link' ); // Remove wlwmanifest.xml.
    remove_action( 'wp_head', 'rsd_link' ); // Remove Really Simple Discovery Link.
    remove_action( 'wp_head', 'wp_shortlink_wp_head', 10, 0 ); // Remove Shortlink URL.
    remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); // Remove Emoji's Styles and Scripts.
    remove_action( 'wp_print_styles', 'print_emoji_styles' ); // Remove Emoji's Styles and Scripts.
    remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); // Remove Emoji's Styles and Scripts from Admin.
    remove_action( 'admin_print_styles', 'print_emoji_styles' ); // Remove Emoji's Styles and Scripts from Admin.
    remove_action( 'wp_head', 'index_rel_link' ); // Remove Link to Home Page.
    remove_action( 'wp_head', 'feed_links_extra', 3 ); // Remove Every Extra Links to RSS Feeds.
    remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10 ); // Remove Prev-Next Links from Header - Not from Post.
    remove_action( 'wp_head', 'adjacent_posts_rel_link', 10, 0 ); // Remove Prev-Next Links.
    remove_action( 'wp_head', 'start_post_rel_link', 10, 0 ); // Remove Random Link Post.
    remove_action( 'wp_head', 'parent_post_rel_link', 10, 0 ); // Remove parent Post Link.

    add_filter( 'the_generator', '__return_false' ); // Remove Generator Name from RSS Feed.
}
add_action( 'after_setup_theme', 'cds_clean_the_header_outout' );


/************************************************************************
 * Remove WPML generator
 ***********************************************************************/
if ( !empty ( $GLOBALS['sitepress'] ) ) {
    function cds_remove_wpml_generator() {
        remove_action(
            current_filter(),
            array ( $GLOBALS['sitepress'], 'meta_generator_tag' )
        );
    }
    add_action( 'wp_head', 'cds_remove_wpml_generator', 0 );
}


/************************************************************************
 * Remove Slider Revolution generator
 ***********************************************************************/
function cds_remove_revslider_meta_tag() {
    return '';
}
add_filter( 'revslider_meta_generator', 'cds_remove_revslider_meta_tag' );


/************************************************************************
 * Remove Visual Composer / WPBakery Page Builder generator
 ***********************************************************************/
function cds_remove_visual_composer_generator() {
    if ( class_exists( 'Vc_Manager' ) || class_exists( 'Vc_Base' ) ) {
        remove_action('wp_head', array(visual_composer(), 'addMetaData'));
    }
}
add_action('init', 'cds_remove_visual_composer_generator', 100);


/************************************************************************
 * Remove comment-reply.min.js completely
 ***********************************************************************/
function cds_remove_comment_reply_js() {
	wp_deregister_script( 'comment-reply' );
}
add_action('init', 'cds_remove_comment_reply_js', 10);


/************************************************************************
 * Remove jQuery Migrate Script from header 
 * and Load jQuery from Google API
 ***********************************************************************/
function cds_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','cds_remove_jquery_migrate_load_google_hosted_jquery');


/************************************************************************
 * Remove Yoast SEO comments
 ***********************************************************************/
function cbs_remove_yoast_seo_comments() {
    if ( ! class_exists( 'WPSEO_Frontend' ) ) {
        return;
    }
    $instance = WPSEO_Frontend::get_instance();
    // To ensure that future version of the plugin does not cause any problem
    if ( ! method_exists( $instance, 'debug_mark') ) {
        return;
    }
    remove_action( 'wpseo_head', array( $instance, 'debug_mark' ), 2 );
}
add_action('template_redirect', 'cbs_remove_yoast_seo_comments', 9999);

add_filter('wpseo_debug_markers', '__return_false', 9999); // Requires Yoast > v14.0 

/************************************************************************
 * Remove Query Strings (the version) from script and style files,
 * some files won't be cached if there is a query string in them
 ***********************************************************************/
// function cds_remove_script_version( $src ){
//     $parts = explode( '?ver', $src );
//     return $parts[0];
// }
// add_filter( 'script_loader_src', 'cds_remove_script_version', 15, 1 );
// add_filter( 'style_loader_src', 'cds_remove_script_version', 15, 1 );


/************************************************************************
 * remove wp version param from any enqueued scripts 
 * (using wp_enqueue_script()) or styles (using wp_enqueue_style()).
 ***********************************************************************/
function cds_remove_appended_version_script_style( $target_url ) {
    $filename_arr = explode('?', basename($target_url));
    $filename = $filename_arr[0];

    /* check if "ver=" argument exists in the url or not */
    if (strpos( $target_url, 'ver=' )) {
        $target_url = remove_query_arg( 'ver', $target_url );
    }
    /* check if "version=" argument exists in the url or not */
    if (strpos( $target_url, 'version=' )) {
        $target_url = remove_query_arg( 'version', $target_url );
    }
    return $target_url;
}
/**
 * Priority set to 20000. Higher numbers correspond with later execution.
 * Hook into the style loader and remove the version information.
 */
add_filter('style_loader_src', 'cds_remove_appended_version_script_style', 20000);
/**
 * Hook into the script loader and remove the version information.
 */
add_filter('script_loader_src', 'cds_remove_appended_version_script_style', 20000);
<?php
/**
 * CubeDesigns - Wordpress Admin Tweaks and Extras
 *
 */



/************************************************************************
 * Add and Display featured image column in Posts list 
 ***********************************************************************/
function cds_add_featured_image_column( $columns ) {
    $columns['featured_image_thumb'] = 'Featured Image';
    return $columns;
}
add_filter('manage_posts_columns' , 'cds_add_featured_image_column', 5);

function cds_display_featured_image_column_data( $column, $post_id ) {
    switch ( $column ) {
        case 'featured_image_thumb':
            echo get_the_post_thumbnail($post_id, 'thumbnail');
            break;
    }
}
add_action( 'manage_posts_custom_column' , 'cds_display_featured_image_column_data', 5, 2 ); 

CubeDesigns - Varius functions for specific cases

Theme's functions.php snippets

Add Security Headers

<?php 
/**
 * [additional_securityheaders]
 *
 * Add and define extra Headers for enhanced security
 *
 * @param  {[array]} $headers [The default WP headers]
 * @return {[array]}          [The modified headers]
 */
function cds_additional_securityheaders( $headers ) {
  if ( ! is_admin() ) {
    $headers['Referrer-Policy']             = 'no-referrer-when-downgrade'; //This is the default value, the same as if it were not set.
    $headers['X-Content-Type-Options']      = 'nosniff';
    $headers['X-XSS-Protection']            = '1; mode=block';
    $headers['Permissions-Policy']          = 'geolocation=(self "https://example.com") microphone=() camera=()';
    $headers['Content-Security-Policy']     = "script-src 'self'";
    $headers['X-Frame-Options']             = 'SAMEORIGIN';
  }

  return $headers;
}
add_filter( 'wp_headers', 'cds_additional_securityheaders' );

Clear WP Expired Sessions

<?php 
// Only run if current user is logged-in and is Admin and user ID == 2 and there is the query param: cds_debug
if ( current_user_can( 'manage_options' ) && get_current_user_id() == 2 && get_query_var('cds_debug') ) {
    global $wpdb;
    
    //  Get expired wp_sessions *use LIMIT if you have a LOT of wp_sessions
    $expiration_keys = $wpdb->get_results( "
        SELECT option_name, option_value 
        FROM $wpdb->options 
        WHERE option_name LIKE '_wp_session_expires_%' 
        AND option_value < NOW() 
        LIMIT 0, 50000
    " );
  
	$now = time(); // EPOCH time
	$expired_sessions = array();

	foreach( $expiration_keys as $expiration ) {
		// Double check that the session has expired
		if ( $now > intval( $expiration->option_value ) ) {
			// Get the session ID by parsing the option_name
			$session_id = str_replace("_wp_session_expires_", "", $expiration->option_name);

			$expired_sessions[] = $expiration->option_name;
			$expired_sessions[] = "_wp_session_$session_id";
		}
	}

	// Delete all expired sessions in a single query
	if ( ! empty( $expired_sessions ) ) {
		$option_names = implode( "','", $expired_sessions );
    // highlight_string("<?php\n\$option_names =\n" . var_export($option_names) . ";\n");
		$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name IN ('$option_names')" );

	}
    
}

Remove srcset and sizes in specific page

<?php 
/**
 * [cds_clean_attached_image_attrs]
 * 
 * Remove srcset and sizes in specific page
 * 
 * @param  [type] $attr       [description]
 * @param  [type] $attachment [description]
 * @param  [type] $size       [description]
 * @return [type]             [description]
 */
function cds_clean_attached_image_attrs( $attr, $attachment, $size ) {
	// Limit to specific page. e.g. posts grid page with lots of images
    // If page is: #some_page_id
	if ( is_page(137) ) {
	    // highlight_string("<?php\n\$attr =\n" . var_export($attr, true) . ";\n");
        // Remove srcset and sizes
        if ( isset($attr["srcset"]) ) { unset($attr["srcset"]); }
        if ( isset($attr["sizes"]) ) { unset($attr["sizes"]); }
    }
    return $attr;
}
add_filter( 'wp_get_attachment_image_attributes', 'cds_clean_attached_image_attrs', 10, 3 );

Disable srcset on frontend

<?php 
/**
 * [cds_disable_wp_responsive_images]
 * 
 * disable srcset on frontend
 * 
 * @return [type] [description]
 */
function cds_disable_wp_responsive_images() {
	return 1;
}
add_filter('max_srcset_image_width', 'cds_disable_wp_responsive_images');

Add display=swap to google fonts, when not having access to the creation enque style

<?php 
/**
 * [cds_style_loader_tag_filter]
 * Add display=swap to google fonts, when not having access to the creation enque style
 *
 * @return the modified <link> tag
 */
add_filter('style_loader_tag', 'cds_style_loader_tag_filter', 10, 2);
function cds_style_loader_tag_filter($html, $handle) {
    // add &display=swap in google fonts Roboto
    if ($handle === 'Roboto') {
        if ( function_exists('simplexml_load_file') ) {
            $xmlFromHtml = simplexml_load_string($html);
            $hrefs = $xmlFromHtml->xpath("//@href");
		
            if (isset($hrefs) && !empty($hrefs) && is_array($hrefs)) {
                foreach($hrefs as $href) {
                    $modified_href = (string)$href . '&display=swap';
                    $final_link = "<link rel='stylesheet' id='Roboto-css' href='" . $modified_href . "' type='text/css' media='all' />";
                    $html = $final_link;
                    break;
                }
            }
        }
    }
    return $html;
}
<?php 

/************************************************************************
 * load Optimization file, which has various 
 * functions to improve pagespeed of the site
 * and cleanup WP
 ***********************************************************************/
include( get_stylesheet_directory() . './cds-optimizations-n-tweaks.php' );

/************************************************************************
 * load cds-wp-admin-tweaks-n-extras file, which has various 
 * functions to make changes/additions to the WP admin area
 ***********************************************************************/
include( get_stylesheet_directory() . './cds-wp-admin-tweaks-n-extras.php' );
# ######################################################################
# /!\ LocationMatch CANNOT be added to htaccess! /!\
# it is for server config only!
# https://httpd.apache.org/docs/current/mod/core.html#locationmatch
# ######################################################################

# "Block access to potentially sensitive files"
<LocationMatch ".+\\.(?i:psd|log|cmd|exe|bat|csh|ini|sh)$">
    Require all denied
</LocationMatch>

# "Block access to sensitive files"
# To remove this rule, revert this security measure on each WordPress installation on this domain
<LocationMatch "(?i:(?:wp-config\\.bak|\\.wp-config\\.php\\.swp|(?:readme|license|changelog|-config|-sample)\\.(?:php|md|txt|htm|html)))">
    Require all denied
</LocationMatch>

# "Disable PHP execution in cache directories"
# To remove this rule, revert this security measure on each WordPress installation on this domain
<LocationMatch "(?i:.*/cache/.*\\.ph(?:p[345]?|t|tml))">
    Require all denied
</LocationMatch>

# "Forbid execution of PHP scripts in the wp-content/uploads directory"
# /!\ MODIFY FOLLOWING DIRECTORY PATH /!\
<Directory "/home/example/public_html/wp-content/uploads">
    <FilesMatch \.php$>
        Require all denied
    </FilesMatch>
</Directory>

# "Forbid execution of PHP scripts in the wp-includes directory"
# /!\ MODIFY FOLLOWING TWO DIRECTORIES PATHS /!\
<IfModule mod_rewrite.c>
    <Directory "/home/example/public_html/wp-includes">
        <FilesMatch \.php$>
            RewriteEngine on
            RewriteCond %{REQUEST_FILENAME} !^/home/example/public_html/wp\-includes/js/tinymce/wp\-tinymce\.php$ [NC]
            RewriteRule .* - [NC,F,L]
        </FilesMatch>
    </Directory>
</IfModule>

# "Block author scans"
# /!\ MODIFY FOLLOWING TWO DIRECTORIES PATHS /!\
<IfModule mod_rewrite.c>
    <Directory "/home/example/public_html">
        RewriteEngine on
        RewriteCond %{QUERY_STRING} author=\d+
        RewriteCond %{REQUEST_FILENAME} !^/home/example/public_html/wp\-admin/ [NC]
        RewriteRule .* - [F,L]
    </Directory>
</IfModule>


# "Enable bot protection"
# /!\  MODIFY FOLLOWING DIRECTORY PATH /!\
<IfModule mod_rewrite.c>
    <Directory "/home/example/public_html">
        RewriteEngine on
        RewriteCond %{HTTP_USER_AGENT} "(?:acunetix|BLEXBot|domaincrawler\\.com|LinkpadBot|MJ12bot/v|majestic12\\.co\\.uk|AhrefsBot|TwengaBot|SemrushBot|nikto|winhttp|Xenu\\s+Link\\s+Sleuth|Baiduspider|HTTrack|clshttp|harvest|extract|grab|miner|python-requests)" [NC]
        RewriteRule .* - [F,L]
    </Directory>
</IfModule>

WP Plugin Development Resources and Best Practices

Start Here

Generic Plugin Development Basics

https://developer.wordpress.org/plugins/plugin-basics/

Generic Plugin Development Best Proctices

https://developer.wordpress.org/plugins/plugin-basics/best-practices/

Generic Plugin Development Boilerplate Generators

https://developer.wordpress.org/plugins/plugin-basics/best-practices/#boilerplate-starting-points

WordPress Coding Standards (WPCS)

https://developer.wordpress.org/coding-standards/wordpress-coding-standards/

Install WPCS for PHP Code Sniffer

Following instructions are for installing PHP CodeSniffer locally on an existing project with Composer and then add WPCS and configure it for VS Code.

1. Install PHP CodeSniffer locally

Navigate to the folder where you have initialized composer (e.g. cd /PATH/to/plugins/YOUR_PLUGIN/ ) and run: composer require --dev squizlabs/php_codesniffer

You can verify correct instalaltion and view current (default) coding standards via the following command: vendor/bin/phpcs -i and you should see: The installed coding standards are MySource, PEAR, PSR1, PSR2, PSR12, Squiz and Zend

2. Install WPCS locally

a. Go to th e folder where you want to download the WordPress-Coding-Standars files. In my case it is /vendor: cd vendor b. Clone the WordPress standards repository to the folder /wpcs git clone -b master https://github.com/WordPress/WordPress-Coding-Standards.git wpcs

c. Add its path to the PHP_CodeSniffer configuration

{PATH}/vendor/bin/phpcs --config-set installed_paths {PATH}/to/wpcs

/home/ACCOUNT_NAME/public_html/wp-content/plugins/YOUR_PLUGIN/vendor/bin/phpcs --config-set installed_paths /home/ACCOUNT_NAME/public_html/wp-content/plugins/YOUR_PLUGIN/vendor/wpcs

d. Running again: vendor/bin/phpcs -i

should output: The installed coding standards are MySource, PEAR, PSR1, PSR2, PSR12, Squiz, Zend, WordPress, WordPress-Core, WordPress-Docs and WordPress-Extra

3. Install PHP Sniffer & Beautifier VS Code plugin and setup

a. Install PHP Sniffer & Beautifier VS Code plugin https://marketplace.visualstudio.com/items?itemName=ValeryanM.vscode-phpsab

b. Open VS Code settings.json

Inside VS Code press Ctrl + Shift + P and serach for settings.json and add the following:

"editor.tabSize": 4,
"editor.detectIndentation": false,
// "editor.formatOnSave": true,
"window.title": "${activeEditorLong}${separator}${rootName}",
"phpsab.standard": "WordPress",
"phpsab.snifferEnable": true, // disable this if you don't need suggestions
"phpsab.executablePathCBF": "{PATH}/to/vendor/squizlabs/php_codesniffer/bin/phpcbf",
"phpsab.executablePathCS": "{PATH}/to/vendor/squizlabs/php_codesniffer/bin/phpcs",
"phpsab.snifferShowSources": true

| Make sure to replace {PATH} with the correct path to your php_codesniffer installation

Change the Table Prefix in wp-config.php config file

Open file for edit and search for: $table_prefix = 'wp_';

and rename accordingly.

Change the Table Prefix in DATABASE via SQL queries:

1. Change the prefix from ALL database tables:

a) RUN following query:

SET @database  = "databasename";
SET @oldprefix = "oldprefix_";
SET @newprefix = "newprefix_";

SELECT
    concat(
        "RENAME TABLE ",
        TABLE_NAME,
        " TO ",
        replace(TABLE_NAME, @oldprefix, @newprefix),
        ';'
    ) AS "SQL"
FROM information_schema.TABLES WHERE TABLE_SCHEMA = @database;

*** NOTICE: change values of @database, @oldprefix and @newprefix BEFORE running the above query!

b) The above query will generate further queries like:

RENAME oldprefix_options to newprefix_options;
RENAME oldprefix_users to newprefix_users;

Copy them and execute them!

| After renaming the tables, you also need to replace some values in the table *_usermeta and *_options by using the queries below.

2. Rename old prefix in user meta:

Use following query

UPDATE `newprefix_usermeta`
SET meta_key = REPLACE(meta_key, 'oldprefix_', 'newprefix_')
WHERE meta_key LIKE 'oldprefix_%';

3. Update Prefix in options Table:

UPDATE wp_options
SET option_name = replace(option_name, 'wp_', 'new_')
WHERE option_name LIKE 'wp_%';

BONUS: Change the Table Prefix in DATABASE via PHPmyAdmin:

Rename Database Tables:

1 .Select the database. 2. Check on the ‘check all’ checkbox to select all tables. 3. Click on the drop-down and select 'Replace table prefix.'

After entering a new prefix, click on continue, which will change the Prefix in the database.

Rename Prefix in Options Table:

Search wp_ prefix in the options table using this query.

SELECT * FROM `wp_testing123_options` WHERE `option_name` LIKE '%wp_%'

Replace all old prefixes with new prefixes.

Update Prefix in Usermeta Table:

We need to search wp_ as a prefix on the usermeta table and replace it using this query.

SELECT * FROM `wp_testing123_usermeta` WHERE `meta_key` LIKE `%wp_%`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment