Security Best Practices for WordPress Theme Development
Learn how to develop secure WordPress themes. Implement proper escaping, validation, and security controls in your theme code.
WordPress themes execute on every page load, making theme security critical for site protection. Insecure themes can expose sites to XSS, data leaks, and other vulnerabilities affecting all visitors.
Theme Security Fundamentals
Key Principles
- Escape everything output to the browser
- Sanitize all user-provided data
- Use WordPress functions for all operations
- Never trust any external data
- Keep themes focused on presentation
Output Escaping in Templates
Escaping Functions
// HTML content
// HTML attributes
// URLs
// JavaScript
// Translation with escaping
Post Content
// Post content is pre-sanitized, use the_content()
// For excerpts
// Custom fields need escaping
ID, 'custom_field', true)); ?>
Template Data Handling
Safe Template Tags
// Use WordPress template tags
// Safe - escaped internally
// Safe
// Custom queries need escaping
Query Variables
// Never use $_GET/$_POST directly in templates
// WRONG
// CORRECT
Theme Options Security
Customizer Settings
// Register with sanitization
$wp_customize->add_setting('theme_color', array(
'default' => '#ffffff',
'sanitize_callback' => 'sanitize_hex_color'
));
$wp_customize->add_setting('theme_text', array(
'default' => '',
'sanitize_callback' => 'sanitize_text_field'
));
$wp_customize->add_setting('theme_url', array(
'default' => '',
'sanitize_callback' => 'esc_url_raw'
));
Using Theme Options
// Always escape when outputting
$color = get_theme_mod('theme_color', '#ffffff');
echo '';
$url = get_theme_mod('custom_url', '');
echo 'Link';
JavaScript Security
Localizing Data
// Safely pass data to JavaScript
wp_localize_script('theme-script', 'ThemeData', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('theme_nonce'),
'post_id' => get_the_ID()
));
Inline Scripts
// Use wp_add_inline_script for safety
wp_add_inline_script('theme-script',
'var postId = ' . absint(get_the_ID()) . ';'
);
Form Handling
Theme Forms
Form Processing
add_action('admin_post_theme_form_handler', function() {
// Verify nonce
if (!wp_verify_nonce($_POST['theme_form_nonce'], 'theme_form_action')) {
wp_die('Security check failed');
}
// Sanitize input
$input = sanitize_text_field($_POST['user_input']);
// Process...
wp_redirect(wp_get_referer());
exit;
});
File Includes
Safe Template Parts
// Use WordPress functions for includes
get_template_part('template-parts/content', get_post_type());
// With data (WP 5.5+)
get_template_part('template-parts/card', null, array(
'title' => esc_html($title),
'link' => esc_url($link)
));
Avoid Direct Includes
// WRONG - vulnerable to path traversal
include($_GET['template'] . '.php');
// CORRECT - whitelist approach
$allowed = array('header', 'footer', 'sidebar');
$template = sanitize_file_name($_GET['template']);
if (in_array($template, $allowed)) {
get_template_part($template);
}
SVG and Image Security
SVG Handling
// Sanitize SVG uploads if allowed
function sanitize_svg($svg) {
// Remove potentially dangerous elements
$svg = preg_replace('//is', '', $svg);
$svg = preg_replace('/onw+="[^"]*"/i', '', $svg);
return $svg;
}
// Better: use a library like SVG Sanitizer
REST API in Themes
// Secure REST endpoint for theme
add_action('rest_api_init', function() {
register_rest_route('theme/v1', '/data', array(
'methods' => 'GET',
'callback' => 'theme_get_data',
'permission_callback' => function() {
return current_user_can('read');
}
));
});
function theme_get_data() {
// Return escaped data
return array(
'title' => esc_html(get_bloginfo('name')),
'url' => esc_url(home_url())
);
}
Security Checklist
- All dynamic output escaped
- No direct superglobal usage
- Customizer options sanitized
- Forms use nonces
- AJAX requests verified
- No executable file includes
- Translation functions used properly
Conclusion
Theme security centers on proper output escaping and avoiding direct use of user input. Use WordPress escaping functions consistently and sanitize all Customizer settings for secure theme development.
Written by Sarah Chen
WP Folder Shield Team