Every piece of data that enters your WordPress application from the outside world — form submissions, URL parameters, REST API requests, shortcode attributes — is a potential attack vector. SQL injection, cross-site scripting (XSS), and file inclusion attacks almost always begin with unsanitised user input that reaches a database query, an echo statement, or a file path. WordPress ships with a comprehensive set of sanitisation and escaping functions that cover virtually every use case, and using them consistently is the single most effective coding habit you can develop. The golden rule is: sanitise on the way in, escape on the way out. Sanitisation strips or normalises data before it is stored; escaping converts special characters before data is displayed in a specific context. For text fields, sanitize_text_field() removes tags and extra whitespace. For email addresses, sanitize_email() rejects invalid formats. For integers like post IDs, absint() converts to a non-negative integer and prevents negative-number injections. For HTML content from trusted users (administrators), wp_kses_post() strips disallowed tags while preserving formatting. For output to HTML attributes use esc_attr(), for URLs use esc_url(), and for JavaScript contexts use esc_js(). These principles apply equally when you write custom database queries with $wpdb and when you build shortcodes with user-supplied attributes. This guide covers the most common input scenarios with correct code for each.
Problem: You are handling user-submitted data in WordPress and need to know which sanitisation and escaping function to use for each data type to prevent XSS and injection attacks.
Solution: Use the correct function for each data type as shown below in your functions.php or plugin file:
// SANITISATION (input/storage)
$text = sanitize_text_field( $_POST['name'] ?? '' ); // strips tags & whitespace
$email = sanitize_email( $_POST['email'] ?? '' ); // strips invalid email chars
$url = esc_url_raw( $_POST['website'] ?? '' ); // sanitises URL for DB storage
$int = absint( $_POST['count'] ?? 0 ); // non-negative integer
$slug = sanitize_title( $_POST['slug'] ?? '' ); // URL-safe slug
$key = sanitize_key( $_POST['key'] ?? '' ); // lowercase alphanumeric + dashes
$html = wp_kses_post( $_POST['content'] ?? '' ); // allowed HTML tags only
$textarea = sanitize_textarea_field( $_POST['bio'] ?? '' );// multiline text, strips tags
// ESCAPING (output/display)
echo esc_html( $text ); // output inside HTML content
echo esc_attr( $text ); // output inside an HTML attribute
echo esc_url( $url ); // output inside href / src
echo esc_js( $text ); // output inside a JavaScript string
echo esc_textarea( $textarea ); // output inside <textarea>
echo wp_kses_post( $html ); // output rich HTML content safely
// $wpdb EXAMPLE — always use prepare()
global $wpdb;
$safe_query = $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_author = %d AND post_status = %s LIMIT %d",
absint( $user_id ),
'publish',
10
);
$results = $wpdb->get_results( $safe_query );
// NONCE VERIFICATION for forms
// In your form:
// wp_nonce_field( 'helloadmin_save_settings', 'helloadmin_nonce' );
if ( ! isset( $_POST['helloadmin_nonce'] )
|| ! wp_verify_nonce( $_POST['helloadmin_nonce'], 'helloadmin_save_settings' ) ) {
wp_die( 'Security check failed' );
}
NOTE: Never use PHP’s native addslashes() or mysql_real_escape_string() in WordPress — always use $wpdb->prepare() for database queries. Also, do not trust the $_SERVER['REMOTE_ADDR'] value unconditionally if your site is behind a proxy or CDN; an attacker can spoof HTTP_X_FORWARDED_FOR headers. Verify your proxy setup and whitelist trusted proxy IPs before using forwarded IP headers for rate-limiting or logging.