Validating, Sanitizing, and Escaping

Your code works, but is it safe? When writing your theme and plugin code, you’ll need to be extra cautious of how you handle data coming into WordPress and how it’s presented to the end user. This commonly comes up when building a settings page for your theme, creating and manipulating shortcodes, or saving and rendering extra data associated with a post. There is a distinction between how input and output are managed, and this document will walk you through that.

(If you’re interested in more thoughts on why VIP takes these practices so seriously, read The Importance of Escaping All The Things from June 2014.)

Guiding Principles

  1. Never trust user input.
  2. Escape as late as possible.
  3. Escape everything from untrusted sources (like databases and users), third-parties (like Twitter), etc.
  4. Never assume anything.
  5. Never trust user input.
  6. Sanitation is okay, but validation/rejection is better.
  7. Never trust user input.

“Escaping isn’t only about protecting from bad guys. It’s just making our software durable. Against random bad input, against malicious input, or against bad weather.”

Validating: Checking User Input

To validate is to ensure the data you’ve requested of the user matches what they’ve submitted. There are several core methods you can use for input validation; usage obviously depends on the type of fields you’d like to validate. Let’s take a look at an example.

Say we have an input area in our form like this:

<input id="my-zipcode" type="text" maxlength="5" name="my-zipcode" />

Just like that, we’ve limited my user to five characters of input, but there’s no limitation on what they can input. They could enter “11221” or “eval(“. If we’re saving to the database, there’s no way we want to give the user unrestricted write access.

This is where validation plays a role. When processing the form, we’ll write code to check each field for its proper data type. If it’s not of the proper data type, we’ll discard it. For instance, to check “my-zipcode” field, we might do something like this:

$safe_zipcode = intval( $_POST['my-zipcode'] );
if ( ! $safe_zipcode )
$safe_zipcode = '';
update_post_meta( $post->ID, 'my_zipcode', $safe_zipcode );

The intval() function casts user input as an integer, and defaults to zero if the input was a non-numeric value. We then check to see if the value ended up as zero. If it did, we’ll save an empty value to the database. Otherwise, we’ll save the properly validated zipcode.

Note that we could go even further and make sure the the zip code is actually a valid one based on ranges and lengths we expect (e.g. 111111111 is not a valid zip code but would be saved fine with the function above).

This style of validation most closely follows WordPress’ whitelist philosophy: only allow the user to input what you’re expecting. Luckily, there’s a number of handy helper functions you can use for most every data type.

Sanitizing: Cleaning User Input

Sanitization is a bit more liberal of an approach to accepting user data. We can fall back to using these methods when there’s a range of acceptable input.

For instance, if we had a form field like this:

<input id="title" type="text" name="title" />

We could sanitize the data with the sanitize_text_field() function:

$title = sanitize_text_field( $_POST['title'] );
update_post_meta( $post->ID, 'title', $title );

Behind the scenes, the function does the following:

  • Checks for invalid UTF-8
  • Converts single < characters to entity
  • Strips all tags
  • Remove line breaks, tabs and extra white space
  • Strip octets

The sanitize_*() class of helper functions are super nice for us, as they ensure we’re ending up with safe data and require minimal effort on our part.

In some instances using WP_KSES and it’s related functions might be a good idea as you can easily clean HTML while keeping anything relevant to your needs present.

Escaping: Securing Output

For security on the other end of the spectrum, we have escaping. To escape is to take the data you may already have and help secure it prior to rendering it for the end user. WordPress thankfully has a few helper functions we can use for most of what we’ll commonly need to do:

esc_html() we should use anytime our HTML element encloses a section of data we’re outputting.

<h4> <!-- <?php echo esc_html( $title ); ?> --> </h4>

esc_url() should be used on all URLs, including those in the ‘src’ and ‘href’ attributes of an HTML element.

<img alt="" src="<?php echo esc_url( $great_user_picture_url ); ?>" />

esc_js() is intended for inline Javascript.

var value = '<?php echo esc_js( $value ); ?>';

esc_attr() can be used on everything else that’s printed into an HTML element’s attribute.

<ul class="<!--<?php echo esc_attr( $stored_class ); ?>-->">

wp_kses() can be used on everything that is expected to contain HTML.  There are several variants of the main function, each featuring a different list of built-in defaults.  A popular example is wp_kses_post(), which allows all markup normally permitted in posts. You can of course roll your own filter by using wp_kses() directly.

<?php echo wp_kses_post( $partial_html ); echo wp_kses( $another_partial_html , array(
    'a' => array(
        'href' => array(),
        'title' => array()
    'br' => array(),
    'em' => array(),
    'strong' => array(),
);) ?>;

As an example, passing an array to wp_kses() containing the member

'a' => array( 'href' , 'title', )

means that only those 2 html attributes will be allowed for a tags — all the other ones will be stripped. Referencing a blank array from any given key means that no attributes are allowed for that element and they should all be stripped.

There has historically been a perception that wp_kses() is slow. While it is a bit slower than the other escaping functions, the difference is minimal and does not have as much of an impact as most slow queries or uncached functions would.

It’s important to note that most WordPress functions properly prepare the data for output, and you don’t need to escape again.

<h4><?php the_title(); ?></h4>

rawurlencode() should be used over urlencode() for ensure URLs are correctly encoded. Only legacy systems should use `urlencode()`.

<?php echo esc_url( '' . rawurlencode( $stored_class ) ); ?>

Always Escape Late

It’s best to do the output escaping as late as possible, ideally as data is being outputted.

// Okay, but not that great
$url = esc_url( $url );
$text = esc_html( $text );
echo '<a href="'. $url . '">' . $text . '</a>';

// Much better!
echo '<a href="'. esc_url( $url ) . '">' . esc_html( $text ) . '</a>';

This is for a few reasons:

  • It makes our code reviews and deploys happen faster, because rather than hunting through many lines of code, we can glance at it and know it’s safe for output.
  • Something could inadvertently change the variable between when it was first cast and when it’s output, introducing a potential vulnerability.
  • Future changes could refactor the code significantly. We review code under the assumption that everything is being output escaped/cast – if it’s not and some changes go through that make it no longer safe to output, we may incorrectly allow the code through, since we’re assuming it’s being properly handled on output.
  • Late escaping makes it easier for us to do automatic code scanning (saving us time and cutting down on review/deploy times) – something we’ll be doing more of in the future.
  • Escaping/casting on output simply removes any ambiguity and adds clarity (always develop for the maintainer).

Escape on String Creation

It is sometimes not practical to escape late. In a few rare circumstances you cannot pass the output to wp_kses since by definition it would strip the scripts that are being generated.
In situations like this always escape while creating the string and store the value in a variable that is a postfixed with _escaped, _safe or _clean. So instead of $variable do $variable_escaped or $variable_safe.
Functions must always return “safe” html that do not rely on them being late escaped.This allows you to do echo my_custom_script_code(); without needing the script tag to be passed thru a version of WP_KSES that would allow such tags.

Case Studies and FAQs

We know that validating, sanitizing and escaping can be a complex topic; we’ll add some specific case studies and frequently asked questions here as we think they might be helpful.

Q: Doesn’t a function like WP_Query handle sanitizing user input before running a query for me? Why do I need to also sanitize what I send to it?

A: For maximum security, we don’t want to rely on WP_Query to sanitize our data and hope that there are no bugs or unexpected interactions there now or in the future. It’s a good practice to sanitize anything coming from user-land as soon as you begin to interact with it, treating it as potentially malicious right away.

Q: Isn’t WP_KSES_* slow?
A: Even on large strings WP_KSES_* will not add a significant overhead to your pageload. Most of your pageloads should be cached pageloads and the first thing to focus on should be to make sure as many of your end users as possible are getting cached pages. Slow SQL Queries as well as Remote requests are often next on the list. Escaping is often negligible compared to those items.

Zack Tollman wanted to know more about wp_kses functions, so he did a pretty thorough investigation about them here. He found that wp_kses functions can be 20-40x slower than esc_* functions on PHP 5.6, but the performance hit is much smaller when using HHVM. The post was written before PHP 7 came out, but PHP 7 is likely to have similar performance to HHVM, meaning that wp_kses functions aren’t as much as a performance drain as they used to be, at least on PHP 7 servers. is using PHP 7.

Q: Why do I need to escape these values? It is impossible for them to be unsafe.
A: It is currently impossible for them to be unsafe. But a later code change could easily make it that the variable is modified and therefore can no longer be trusted. Always late escaping whenever possible makes the code much more robust and future proof.


To recap: Follow the whitelist philosophy with data validation, and only allow the user to input data of your expected type. If it’s not the proper type, discard it. When you have a range of data that can be entered, make sure you sanitize it. Escape data as much and as late as possible on output to avoid XSS and malformed HTML.

Take a look through the Data Validation Plugin Handbook page  to see all of the sanitization and escaping functions WordPress has to offer.

Using A Shared/Common Theme Across Multiple Sites

Do you have multiple themes that are basically the same? Right now if you want to make a change to all of your sites, you likely have to commit the same code to each theme repository. This is time-consuming and requires more effort on both your and our part. By using a shared theme, changes made to one site can, at your discretion, be applied to all of your sites.

There are various techniques to implement a common theme.

Code-level Configuration

The trick is to replace any per-theme code with some code that is wrapped in conditional tags. Many of our VIPs create configuration arrays in their functions.php and then switch to the different configurations based on the value of home_url(). Here’s a quick example on how to approach this:

add_action( 'init', 'my_init_site', 1 ); // let's initialize our settings very early on

 * Initilize settings based on the site url
function my_init_site() {
	global $my_settings;

	$my_settings = array();

	switch ( parse_url( home_url(), PHP_URL_HOST ) ) {
		case '':
			$my_settings['twitter_username'] = 'cooltechreviewsite';
		case '':
			$my_settings['twitter_username'] = 'coolfoodiesite';

 * Function gets a setting by name
function my_get_site_setting( $name, $default_value = '' ) {
	global $my_settings;
	return isset( $my_settings[$name] ) ? $my_settings[$name] : $default_value;

function my_twitter_link() {
	$my_twitter_username = my_get_site_setting( 'twitter_username' );
	if( ! empty( $my_twitter_username ) )
		printf( '<a href="%s">Follow us on Twitter!</a>', esc_url( '' . $my_twitter_username ) );

Other options include having a config folder with unique configurations/templates for each site:

function my_load_site_config() {
	$config_path = '';

	switch ( parse_url( home_url(), PHP_URL_HOST ) ) {
		case '':
		case '':
			$config_path = __DIR__ . '/config/';
		case '':
		case '':
			$config_path = __DIR__ . '/config/';

	if ( $config_path && file_exists( $config_path ) )
		require( $config_path );
		trigger_error( 'Uh oh, config not found!' );

Options/Customizer Panel

A better, but more involved, solution is to use a settings page to control the per-theme settings. CheezCAP makes this easier. Going this route will allow you to change settings without having to commit code changes and is especially useful on a large network of sites.

You can also integrate this into the Theme Customizer to provide a real-time view of the options as they are updated.

Parent-Child Theme

In some cases, it may not be feasible to have the same theme work for all your sites (e.g. unique template requirements). If you do, the parent-child theme approach might make sense. The idea here is that you try to encapsulate as much of the functionality into the parent (sometimes called the “master”) and have child themes for individual sites to override things as necessary. Note that if you are planning for a large number of sites and themes, this is not a good solution.

Shared Plugins

If you can’t easily follow the common theme approach and are maintaining several themes, it might still make sense to abstract shared functionality into plugins that can be shared across these themes via a shared plugins repo. The idea here is that each theme will have unique templates but will share the larger functional bits via plugins. Your team will get access to a separate SVN repo where the plugins can be stored.

If you structure your plugins in this repo correctly, you can load them using our helper function. If you add the plugin with a matching name and file shared-plugins/my-plugin/my-plugin.php, it can be loaded like follows:

wpcom_vip_load_plugin( 'my-plugin', 'shared-plugins' );

Some notes on shared plugins repos:

* plugins_url() will work in this repo
* If you need to reference the URL or path of a different VIP theme, you can use the wpcom_vip_theme_url or wpcom_vip_theme_dir functions.

Options Size Limit (1MB)

On, options are cached in memory to avoid database lookups, which speed things up. This is only effective if the cached object is kept small. Once the object reaches a certain limit (1MB) it will no longer cache and requests are sent to the database servers, which, depending on the traffic of the site, can cause a flood of requests and have a severe impact on performance. This 1MB limit is the total limit of all options in the options table. This behaviour is different from the core WordPress implementation, having autoload set to false will not impact this 1MB limitation. We recommend using the WP Large Options plugin for persistent data that may grow larger than 1MB.

Here are several guidelines you should keep in mind when working with options as storage containers.

  • Be sure the sum of all data you store in options (get_option(), etc.) is less than 400 KB at most. Options are meant for settings and plugin options. If you need to store bigger HTML fragments, consider using a custom post type or the wp-large-options plugin.
  • Each WP_Cache object can be no larger than 1MB or it will fail to cache. Where applicable and necessary, store items in individual cache objects rather than a single large cache object in order to avoid potentially hitting the limit.
  • Always store only what is really necessary in options. For example: If you need only the post_id, title or link of a post, don’t store the full post object.
  • Make sure your data is sanitized and validated.
  • Check that the data is not escaped twice. Double escaping data, especially if it is automatically filled, can cause the values to grow out of control.
  • Never write options from the front-end. Write operations caused by front-end functionality are generally a bad idea and will slow down your site.

Use current_time(), not date_default_timezone_set()

Use WordPress’s

current_time( 'timestamp' )

if you need to get a time that’s adjusted for the site’s timezone setting in the admin area.

If you need to work with the timezone offset:

get_option( 'gmt_offset' )

Please don’t use date_default_timezone_set(). The timezone in PHP needs to stay GMT+0 as that’s what WordPress expects it to be. Stats and other features are dependent on this, and will break in wild ways if you adjust the timezone.

Custom User Roles

Sometimes the default roles and capabilities aren’t exactly what you need for your site. If you need to create new roles or modify existing ones, we have a bunch of helper functions to assist you in doing this. Please use these functions rather than the traditional methods as this will ensure that your code works on and in your development environments.

As an example, here’s how you can register a “Reviewer” role:

add_action( 'init', function() {
    wpcom_vip_add_role( 'reviewer', 'Reviewer', array(
        'read' => true,
        'edit_posts' => true,
        'edit_others_posts' => true,
        'edit_private_posts' => true,
        'edit_published_posts' => true,
        'read_private_posts' => true,
        'edit_pages' => true,
        'edit_others_pages' => true,
        'edit_private_pages' => true,
        'edit_published_pages' => true,
        'read_private_pages' => true,
    ) );
} );

Note: you’ll want to use these helper functions on the ‘init’ hook.

You can find all available capabilities in WordPress Codex.

Here are some more examples:

add_action( 'init', function() {
	// Add new role
	wpcom_vip_add_role( 'super-editor', 'Super Editor', array( 'level_0' => true ) );

	// Remove publish_posts cap from authors
	wpcom_vip_merge_role_caps( 'author', array( 'publish_posts' => false ) );

	// Remove all caps from contributors
	wpcom_vip_override_role_caps( 'contributor', array( 'level_0' => false ) );

	// Duplicate an existing role and modify some caps
	wpcom_vip_duplicate_role( 'administrator', 'station-administrator', 'Station Administrator', array( 'manage_categories' => false ) );

	// Add custom cap to a role
	wpcom_vip_add_role_caps( 'administrator', array( 'my-custom-cap' ) );

	// Remove cap from a role
	wpcom_vip_remove_role_caps( 'author', array( 'publish_posts' ) );
} );


For security, only certain HTML tags are allowed in content and widgets on However, with our Protected Embeds feature for VIPs, you are able to include iframes, objects and scripts. In addition, VIP Partners can create customized shortcodes – a type of placeholder tag that can optionally accept arguments.

For example, say you want to embed a video from a website that we do not support via one of our existing shortcodes, and does not work within our Protected Embeds feature. To include it, you would create your own shortcode for this embed with arguments for the video ID, width, height, and so forth.

Check with us before creating a shortcode as another VIP Partner may already have a shortcode for the same web service. We also like to review shortcodes before you commit them.

Shortcode Best Practices

Shortcode names (and functions) should be prefixed to avoid future collisions with new shortcodes. Using something related to your site is preferred. For example: if your company is called Fred’s Refrigerators, your shortcode should be named “fred-coolvideosite.”

As with all user supplied data (the shortcode parameters in this case), it should be thoroughly validated. If someone were able to guess your account’s password, you wouldn’t want them using your shortcode to do malicious things. If you’re expecting a number, then make sure you got a number. If you’re expecting a URL, then make sure you got one of those too.

Fetching Remote Data

If you need to fetch data from another server, you should remember that doing so is a relatively slow process and that you can run into problems if there are any timeouts.

To help you to efficiently and robustly fetch your data, we have created two helper functions that you can use.

Both functions are defined in our vip-helper.php file, which can be loaded by including vip-init.php from within your theme’s functions.php file as described in our Development Environment document.


wpcom_vip_file_get_contents() works much like PHP’s built-in file_get_contents() function (although it no longer internally uses it). It returns either the HTML result as a string or false on failure. However, it caches and even returns previously cached data if a new remote request fails. We strongly recommend using this function for any remote request that does not require receiving fresh, up-to-the-second results, i.e. anything on the front end of your blog.

  1. The URL you want to fetch. This is the only required argument.
  2. The timeout limit in seconds. Can be 1 to 10 seconds and it defaults to 3 seconds. We strongly discourage using a timeout greater than 3 seconds since remote requests require that the user wait for them to complete before the rest of the page will load.
  3. The minimum cache time in seconds. It cannot be less than 60 and it defaults to 900 (15 minutes). Setting this higher will result in a faster site as remote requests are relatively slow. Results may be cached even longer if the remote sever sends a cache-control header along with its response, and if that value is larger than this value. See below for details and how to disable this.
  4. An array of additional advanced arguments. See below.

The fourth parameter is an optional argument that can be used to set advanced configuration options. The current additional advanced arguments are:

  • obey_cache_control_header — By default if the remote server sends a cache-control header with a max-age value that is larger than the cache time passed as the third parameter of this function, then this remotely provided value will be used instead. This is because it’s assumed that it’s safe to cache data for a longer period of time if the remote server says the data is not going to change. If you wish to ignore the remote server’s header response and forcibly cache for only the time specified by the third parameter, than a function call along these lines should be used:
    echo wpcom_vip_file_get_contents( '', 3, 900,
    array( 'obey_cache_control_header' =&gt; false ) );
  • http_api_args — Allows you to pass arguments directly to the wp_remote_get() call. See the Code Reference for a list of available arguments. Using this argument will allow you to send things like custom headers or cookies. Example usage:
    echo wpcom_vip_file_get_contents( '', 3, 900,
    array( 'http_api_args' =&gt; array( 'headers' =&gt; array( 'Accept-Encoding' =&gt; 'gzip' ) ) ) );

Note that like PHP’s file_get_contents() function, wpcom_vip_file_get_contents() will return the result. You will need to echo it if you want it outputted. This is different from our previous and now deprecated functions, including vip_wp_file_get_contents().


vip_safe_wp_remote_get() is a sophisticated extended version of wp_remote_get(). It is designed to more gracefully handle failure than wp_safe_remote_get() does. Note that like wp_remote_get() and wp_safe_remote_get, it does not cache. Its arguments are as follows:

  1. The URL you want to fetch. This is the only required argument.
  2. This argument is optional. Pass false if you need to set any of the next arguments.
  3. The number of fails required before subsequent requests automatically return the fallback value. This prevents continually making requests and receiving timeouts for a down or slow remote site. Defaults to 3 retries. Cannot be more than 10.
  4. Number of seconds before the request times out. Can be 1, 2, or 3 and it defaults to 1 second.
  5. This argument controls both the number of seconds before resetting the fail counter and the number of seconds to delay making new requests after the fail threshold is reached. Defaults to 20 and cannot be less than 10.

If you’re confused, here’s some examples that should help clarify:

// Get a URL with a 1 second timeout and cancel remote calls for
// 20 seconds after 3 failed attempts in 20 seconds have occurred
$response = vip_safe_wp_remote_get( $url );
if ( is_wp_error( $response ) )
	echo 'No data is available.';
	echo wp_remote_retrieve_body( $response );

// Get a URL with 1 second timeout and cancel remote calls for 60 seconds
// after 1 failed attempt in 60 seconds has occurred. On failure, display "N/A".
$response = vip_safe_wp_remote_get( $url, false, 1, 1, 60 );
if ( is_wp_error( $response ) )
	echo 'N/A';
	echo wp_remote_retrieve_body( $response );


WordPress’s built-in fetch_feed() function should be used for fetching and parsing feeds. It has built-in caching that defaults to 43200 seconds (12 hours). To change that value, use a filter:

function someprefix_return_900() {
	return 900;

add_filter( 'wp_feed_cache_transient_lifetime', 'someprefix_return_900' );
$feed = fetch_feed( $feed_url );
remove_filter( 'wp_feed_cache_transient_lifetime', 'someprefix_return_900' );


`wpcom_vip_wp_oembed_get()` is a wrapper for WordPress’ own `wp_oembed_get()` but with added caching.

Uncached Remote Requests

If for some reason you need to make an uncached remote request, such as to ping an external service during post publish, then you should use the powerful and flexible WordPress HTTP API rather than directly using cURL or some other method like that.

Note that uncached remote requests should never run on the front end of your site for speed and performance reasons.

cURL fopen fsockopen

Best Practices Introduction

As you know from Getting Started, in order to keep the environment safe and performing to your high standards, we review in detail all plugins and themes. We also spot review all code before deploying it.

As you can imagine, this is not always easy. In order to minimize turnaround times, we have some guidelines which will help you keep your code clean, safe, fast, and easy to review.


  • Avoid direct database queries. WordPress has built-in functionality for getting information in and out of the database. Using these functions wherever possible makes sure that you are benefiting from any optimizations or caching that is already implemented within those functions. See Database Queries for tips.
  • Avoid direct calls to PHP scripts in your theme and do not try to load the WordPress environment on your own. Don’t include wp-load.php, wp-config.php or the like. If you have scripts that need to be run like this, then make sure to create page templates or hook into the parse_request action to intercept the request. See for reference.
  • Don’t leave unused code fragments as comments in the code. You can always look up old fragments via subversion.
  • Make sure that your code and plugins do not write to the filesystem. You might want to use the exploit scanner plugin to assist you with this.
  • Ensure that none of your scripts alter the database. This includes adding or altering database fields, and creating new tables.
  • Remember that “Code is Poetry” and there is no poet who would publish his first notes. Avoid repeating code – if there is something you do more than twice, write a function for it and don’t just copy and paste the same block over and over again. This reduces review time and makes it easier to maintain for you. Try to stick to a consistent coding style and be sure to follow the WordPress Coding Standard.

WordPress Multisite

  • is a single install of WordPress Multisite with millions of blogs spread across thousands of server cores in multiple data centers using global user tables. These tables are enormous and should never be directly queried (wpdb->users). Use the get_users() and related API.
  • Users registered through VIP-hosted websites are created as users. If you’d like to be able to query user data or create custom registration fields you’ll need to use a 3rd-party registration service; if this is of interest, please get in touch, as we work with a number of registration platforms and would be happy to facilitate an integration.

Security Overview

The security of your content and the platform are matters that we take very seriously. This page provides a few notes on security at VIP.

Secure Users and Access

  • Ask all of your users to go through our User Security Best Practices checklist and confirm that they’ve taken these steps.
  • Consider having us force-enable two-step authentication for users on your site. Two-step authentication requires anyone accessing your site to use their password and a code generated on their mobile device or sent by text to their phone; without access to a user’s device, an attacker cannot login to their account.
  • Review the users who have “Administrator” access on your VIP site(s), since they can change key settings. Try to keep this number to a minimum and use other roles to grant users the minimum permissions they need for their work on your site. If an Administrator hasn’t logged in recently, downgrade their access until they need it again.
  • Remind your users to set strong passwords for their accounts and to never store their password information (or related details such as two-step backup codes) anywhere that could be accessed by others.
  • Consider enabling our New Device Notification plugin so you can be aware when a user logs in from a new or unexpected location.
  • Perform regular audits of users and their roles on your VIP site(s), removing inactive users. VIPs with larger networks of sites can request periodic CSV exports of all users/roles so they can automate user list audits.

Secure Code

Your code works, but is it safe? When writing code for the VIP environment, you’ll need to be extra cautious of how you handle data coming into WordPress and how it’s presented to the end user. Here are some tips to get your started:

  • Don’t trust user input or third-party APIs. Always make sure to validate and sanitize all data that can be populated from “outside.” Review our document on Validating, Sanitizing, and Escaping to learn how.
  • Try to exploit your code with common XSS methods as a test. Visit for a list of basic XSS attacks and make sure pasting these snippets to input fields or url parameters will not harm your site.
  • Make sure to implement nonces to avoid duplicate submission of forms and fraud activities. See
  • When adding menus or registering your plugins, make sure that you use an unique handle or slug other than __FILE__ to ensure that you are not revealing system paths.

Security Scans

We recognize that VIP sites may wish to perform security scans on their sites, or have 3rd parties do so on their behalf. While this is almost always okay, our systems team may block any traffic that is considered suspicious. This may affect your scan results.

If your team plans to run recurring or intense scans of your site, please open a support ticket in advance of the first scan to provide us with details such as:

  • the anticipated IP or block from which the scan will originate
  • the frequency and/or duration of the scan

Ready to get started?

Drop us a note.

No matter where you are in the planning process, we’re happy to help, and we’re actual humans here on the other side of the form. 👋 We’re here to discuss your challenges and plans, evaluate your existing resources or a potential partner, or even make some initial recommendations. And, of course, we’re here to help any time you’re in the market for some robust WordPress awesomeness.