When a request hits the server, Varnish looks at the User-Agent header, classifies the device, and then does one of two things. It either serves a cached version from the correct device bucket, or it passes the device type directly to PHP via an HTTP header.
Quick note on how to enable this: This feature relies on the Breeze caching plugin provided by Cloudways. To make it work, go to your Breeze settings in WordPress and ensure that “Varnish Caching” is active, and check the specific options for mobile caching.
How the Detection Actually Works
$_SERVER['HTTP_X_DEVICE_TYPE']
The value will be “mobile”, “tablet”, or “desktop”.
Because PHP knows the device type before rendering begins, you get optimization opportunities that standard CSS media queries simply cannot provide. Reading the Device Type in PHP
Here is the simplest approach to grab the device type:
function get_device_type(): string {
$header = $_SERVER['HTTP_X_DEVICE_TYPE'] ?? '';
$type = strtolower(trim($header));
if (str_contains($type, 'mobile')) return 'mobile';
if (str_contains($type, 'tablet')) return 'tablet';
if (str_contains($type, 'desktop')) return 'desktop';
return 'desktop'; // fallback
}
If you are running a more complex stack, you might want to check multiple headers. For example, Cloudflare uses $_SERVER['HTTP_X_DEVICE_TYPE'] (if enabled in Transform Rules), and CloudFront uses boolean flags:
$headers_to_check = [
'HTTP_X_DEVICE_TYPE', // Cloudways / Breeze
'HTTP_CF_DEVICE_TYPE', // Cloudflare
'HTTP_X_UA_DEVICE', // Generic CDNs
];
foreach ($headers_to_check as $key) {
if (!empty($_SERVER[$key])) {
// normalize and return your device string here
}
}
Practical Use Cases
1. Add a Class to the Tag
add_filter('body_class', function($classes) {
$device = get_device_type(); // mobile | tablet | desktop
$classes[] = 'device-' . $device;
return $classes;
});
body class="... device-mobile" or body class="... device-desktop" Now you can write device-specific CSS that is completely cache-safe:
.device-desktop .hero {
min-height: 100vh;
}
.device-mobile .hero {
min-height: 60vh;
}
.device-tablet .sidebar {
display: none;
}
2. Load Different Scripts
You can easily skip heavy desktop-only scripts on mobile devices:
add_action('wp_enqueue_scripts', function() {
$device = get_device_type();
if ($device === 'desktop') {
wp_enqueue_script('cursor-effects');
wp_enqueue_script('parallax-animations');
wp_enqueue_script('fluid-background');
}
if ($device === 'mobile' || $device === 'tablet') {
wp_enqueue_script('touch-navigation');
}
});
3. Preload the Right Assets
add_action('wp_head', function() {
if (get_device_type() === 'mobile') {
echo '';
} else {
echo '';
echo '';
}
}, 1);
4. Skip Rendering Entirely
Don’t just hide elements with CSS – skip rendering them completely to save DOM nodes:
function render_desktop_widget() {
if (get_device_type() !== 'desktop') {
return '';
}
return '...';
}
5. Pass Device Type to JavaScript
add_action('wp_head', function() {
$device = get_device_type();
echo "";
}, 1);
Then in your JS files:
if (window.serverDeviceType === 'desktop') {
initHeavyAnimations();
}
if (window.serverDeviceType === 'mobile' || window.serverDeviceType === 'tablet') {
document.body.classList.add('touch-device');
}
Elementor: Conditional Visibility
should_render filter to prevent elements from loading based on the device:
add_filter('elementor/frontend/widget/should_render', function($should, $widget) {
$settings = $widget->get_settings();
$device = get_device_type();
// Check a custom control like 'hide_on_mobile'
if ($device === 'mobile' && ($settings['hide_on_mobile'] ?? '') === 'yes') {
return false; // The element won't render at all
}
return $should;
}, 10, 2);
The element doesn’t get a display: none rule – it simply isn’t printed in the HTML. That means zero bytes downloaded for that specific device.
The Catch: Caching and CDNs
The Vary Header
Without a Vary header, CDNs and proxies might cache one version and serve it to everyone. You usually need to tell the browser and proxies to vary by User-Agent:
add_filter('wp_headers', function($headers) {
$headers['Vary'] = isset($headers['Vary'])
? $headers['Vary'] . ', User-Agent'
: 'User-Agent';
return $headers;
});
The Cloudflare Gotcha
Cloudflare does NOT automatically respect device-based caching.
If Cloudflare sits in front of your Cloudways server, things can get messy:
- The first visitor after a cache clear determines the cached version.
- If you visit from a laptop first, everyone gets the desktop version.
- This broken state remains until the cache expires or is purged manually.
Furthermore, Cloudflare strips the Vary: User-Agent header by default to prevent cache fragmentation.
How to fix this:
- Disable Cloudflare HTML caching entirely (cache only static assets like images and CSS).
- Configure Cloudflare Cache Rules to customize the cache key based on device type (available on specific Cloudflare plans).
- Use Cloudflare Workers to detect the device and pass the
CF-Device-Typeheader properly.
Testing Checklist
- Clear all caches (Varnish, Cloudflare, local browser).
- Visit the site from a real mobile phone (Chrome DevTools is not always reliable for this).
- Check the
<body>class or inspect theX-Device-Typeheader. - Visit from a desktop and verify you see a different class.
- Wait 5 minutes, revisit from mobile – it should still serve the correct version.
Pros & Cons
Pros
- Three warmed caches: Each device type gets its own fully optimized version ready to go.
- Smaller payloads: You don’t ship heavy desktop JS or large DOM trees to phones.
- Better LCP/CLS: Preload the exact images you need and skip layout shifts caused by hidden elements.
- True conditional render: Zero HTML output for skipped elements, not just hidden with CSS.
- SEO-safe: Google allows dynamic serving as long as the core content remains equivalent.
Cons
- Cache misconfiguration risks: A wrong setup means the wrong version is served to everyone.
- User-Agent sniffing isn’t perfect: Foldables, tablets with keyboards, and desktop touch screens can create edge cases.
- Three caches to warm: Cold starts take 3x longer to fully prime.
- CDN complexity: Every cache layer in your stack must understand the device splitting logic.
What This IS NOT
It is not a replacement for CSS media queries. Device type does not equal viewport width. A tablet in landscape mode has laptop-like dimensions, and a foldable phone can easily exceed 767px. Your layout should still be CSS-driven.
It is not for building separate templates. This is an optimization technique within one single codebase, not a reason to maintain two different sites.
It is not pixel-perfect targeting. User-Agent sniffing is inherently fuzzy. Use it for broad categories (like skipping heavy 3D assets on handhelds) rather than precise layout conditions.
Wrapping Up
Cloudways Device Detection gives you three main advantages:
- Access to
$_SERVER['HTTP_X_DEVICE_TYPE']before the page renders. - Three separate cache buckets (mobile, tablet, desktop).
- Powerful optimization hooks for conditional scripts and render skipping.
The main tradeoff is infrastructure complexity. Every cache layer – Varnish, Cloudflare, and the browser – must be configured to respect the split. If misconfigured, it will silently break your layout for users.
Use this feature for performance tuning and device-specific preloads, but keep your responsive breakpoints and visual adjustments strictly in CSS.
