Master the X-Frame-Options header for effective clickjacking protection
The X-Frame-Options HTTP response header is a security mechanism that helps protect your website from clickjacking attacks. It indicates whether your site should be allowed to be displayed in an iframe, frame, or object on other websites.
Originally developed by Microsoft for Internet Explorer 8, X-Frame-Options has been widely adopted by all major browsers and remains one of the most effective ways to prevent clickjacking attacks. While newer technologies like CSP frame-ancestors exist, X-Frame-Options continues to be essential for comprehensive protection.
Clickjacking attacks rely on embedding legitimate websites in invisible iframes. X-Frame-Options directly prevents this by telling browsers not to display your site in frames, effectively blocking the attack vector at its source.
The most restrictive and secure option. Completely prevents your site from being displayed in any iframe, regardless of the originating site.
X-Frame-Options: DENYAllows your site to be framed only by pages from the same origin (same domain, protocol, and port). This provides flexibility while maintaining security.
X-Frame-Options: SAMEORIGINAllows your site to be framed only by specific origins. Note: This directive is deprecated in many modern browsers and may not be supported.
X-Frame-Options: ALLOW-FROM https://trusted-site.comMost modern browsers no longer support ALLOW-FROM. Use CSP frame-ancestors instead for granular control over which sites can frame your content.
Add X-Frame-Options headers to your Apache configuration:
# In httpd.conf or .htaccess file
# Basic DENY configuration
Header always set X-Frame-Options "DENY"
# SAMEORIGIN configuration
Header always set X-Frame-Options "SAMEORIGIN"
# ALLOW-FROM configuration (deprecated)
Header always set X-Frame-Options "ALLOW-FROM https://trusted-site.com"
# Multiple headers for different environments
<If "%{HTTP_HOST} == 'admin.example.com'">
Header always set X-Frame-Options "DENY"
</If>
<Else>
Header always set X-Frame-Options "SAMEORIGIN"
</Else>Configure X-Frame-Options in your Nginx server block:
# In nginx.conf or site configuration
# Basic DENY configuration
add_header X-Frame-Options "DENY" always;
# SAMEORIGIN configuration
add_header X-Frame-Options "SAMEORIGIN" always;
# ALLOW-FROM configuration (deprecated)
add_header X-Frame-Options "ALLOW-FROM https://trusted-site.com" always;
# Conditional configuration
server {
listen 443 ssl;
server_name admin.example.com;
add_header X-Frame-Options "DENY" always;
# ... other configuration
}
server {
listen 443 ssl;
server_name app.example.com;
add_header X-Frame-Options "SAMEORIGIN" always;
# ... other configuration
}Use helmet middleware for Express.js applications:
const express = require('express');
const helmet = require('helmet');
const app = express();
// Basic helmet configuration
app.use(helmet({
frameguard: { action: 'deny' } // DENY
// frameguard: { action: 'sameorigin' } // SAMEORIGIN
}));
// Custom configuration
app.use(helmet.frameguard({
action: 'deny'
}));
// Conditional configuration
app.use((req, res, next) => {
if (req.path.startsWith('/admin')) {
res.setHeader('X-Frame-Options', 'DENY');
} else {
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
}
next();
});
// Manual header setting
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
next();
});Set headers in PHP applications:
<?php
// Basic header setting
header("X-Frame-Options: DENY");
// SAMEORIGIN
header("X-Frame-Options: SAMEORIGIN");
// ALLOW-FROM (deprecated)
header("X-Frame-Options: ALLOW-FROM https://trusted-site.com");
// Conditional setting based on page type
if ($isAdminPage) {
header("X-Frame-Options: DENY");
} else {
header("X-Frame-Options: SAMEORIGIN");
}
// WordPress functions.php
add_action('send_headers', function() {
header('X-Frame-Options: DENY');
});
// Laravel middleware
class XFrameOptionsMiddleware
{
public function handle($request, Closure $next, $option = 'DENY')
{
$response = $next($request);
$response->header('X-Frame-Options', $option);
return $response;
}
}
?>Configure in web.config or code:
<!-- In web.config -->
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="DENY" />
</customHeaders>
</httpProtocol>
</system.webServer>
// In Global.asax or Startup.cs
protected void Application_BeginRequest()
{
HttpContext.Current.Response.AddHeader("X-Frame-Options", "DENY");
}
// ASP.NET Core middleware
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Frame-Options", "DENY");
await next();
});
// Using NWebSec library
app.UseXfo(options => options.SameOrigin());X-Frame-Options is supported by all major browsers, but there are some differences in implementation and behavior:
| Browser | Version | DENY | SAMEORIGIN | ALLOW-FROM |
|---|---|---|---|---|
| Chrome | 4.0+ | ✓ | ✓ | ✗ |
| Firefox | 3.6.9+ | ✓ | ✓ | ✗ |
| Safari | 4.0+ | ✓ | ✓ | ✗ |
| Edge | All | ✓ | ✓ | ✗ |
| Internet Explorer | 8.0+ | ✓ | ✓ | ✓ |
X-Frame-Options header is set but site still loads in iframe.
DENY setting breaks legitimate iframe usage.
Multiple X-Frame-Options headers cause unpredictable behavior.
Create a test HTML file to verify iframe blocking:
<!DOCTYPE html>
<html>
<head>
<title>X-Frame-Options Test</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
iframe { border: 2px solid #333; width: 800px; height: 600px; }
.test-result { margin-top: 20px; padding: 10px; }
.success { background: #d4edda; color: #155724; }
.error { background: #f8d7da; color: #721c24; }
</style>
</head>
<body>
<h1>X-Frame-Options Test</h1>
<p>Testing iframe embedding for: https://your-site.com</p>
<iframe src="https://your-site.com"
id="test-iframe"
onload="showSuccess()"
onerror="showError()">
</iframe>
<div id="result" class="test-result">
Loading test...
</div>
<script>
function showSuccess() {
document.getElementById('result').className = 'test-result error';
document.getElementById('result').innerHTML =
'❌ Site loaded in iframe - X-Frame-Options not working!';
}
function showError() {
document.getElementById('result').className = 'test-result success';
document.getElementById('result').innerHTML =
'✅ Site blocked in iframe - X-Frame-Options working!';
}
// Fallback check after timeout
setTimeout(() => {
const iframe = document.getElementById('test-iframe');
if (iframe.style.display === 'none' ||
!iframe.contentWindow) {
showSuccess();
}
}, 5000);
</script>
</body>
</html>Free tool to check HTTP security headers including X-Frame-Options
Comprehensive security testing tool with clickjacking detection
Professional web application security testing platform
Command-line tool to check HTTP headers
Verify your X-Frame-Options header is properly configured and working
Maximum 60 characters