cPanel

How to Protect Your Website from Clickjacking (X-Frame-Options and CSP)

Stop other sites from loading your pages in a hidden iframe. Add the CSP frame-ancestors directive and X-Frame-Options with .htaccess, Nginx, WordPress or Cloudflare.

In a clickjacking attack, another website loads your page inside an invisible frame and tricks visitors into clicking buttons on it, such as "Delete", "Buy" or "Allow", while they think they are clicking something else.

You stop it by telling browsers that your pages may not be shown inside a frame on another site. This guide shows how to do that with two HTTP headers.

Quick answer

Add these two headers to every page your site sends. On Apache or LiteSpeed (most cPanel hosting), put this in the .htaccess file in your site's root folder:

<IfModule mod_headers.c>
    Header always set Content-Security-Policy "frame-ancestors 'self'"
    Header always set X-Frame-Options "SAMEORIGIN"
</IfModule>

This lets your own site frame its pages and blocks every other site. frame-ancestors is the modern header; X-Frame-Options covers older browsers. MDN

On Cloudflare, you can add the same two headers with a Response Header Transform Rule instead (see below).

What you need: access to your site's files (cPanel File Manager, FTP or SSH), or to the web server configuration.

Which value to use

You want Content-Security-Policy X-Frame-Options
No site may frame your pages, not even yours frame-ancestors 'none' DENY
Only your own site may frame them frame-ancestors 'self' SAMEORIGIN
Your site plus one partner site frame-ancestors 'self' https://partner.example not possible: send only the CSP header

SAMEORIGIN is the safe choice for most sites, including WordPress, because some admin screens and page builders load your own pages in a frame.

Do not use ALLOW-FROM in X-Frame-Options. It is obsolete, and modern browsers ignore the whole header when they see it. MDN

Step 1: Add the headers

Apache or LiteSpeed (.htaccess)

  1. In cPanel, open File Manager and go to your site's root folder (usually public_html).
  2. Click Settings and tick Show Hidden Files (dotfiles), so .htaccess is visible.
  3. Right-click .htaccess and choose Edit. If there is no .htaccess, create it.
  4. Add the block from the Quick answer at the top of the file. On WordPress, put it above the # BEGIN WordPress line, because WordPress rewrites everything between # BEGIN WordPress and # END WordPress.
  5. Save the file.

The <IfModule> lines stop the site from breaking with an error 500 if the mod_headers module is missing. If the headers do not appear in Step 2, ask your host to enable mod_headers.

Nginx

Add these lines to the server block of your site, then reload Nginx:

add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
sudo nginx -t && sudo systemctl reload nginx

Nginx only passes add_header lines down to a location block that has no add_header lines of its own. If a location block already sets a header, repeat these two lines inside it.

WordPress without .htaccess access

If you cannot edit .htaccess (for example on Nginx hosting without server access), WordPress can send the headers itself. Add this to your theme's functions.php, or better, to a small plugin, so a theme update does not remove it:

add_action('send_headers', function () {
    header("Content-Security-Policy: frame-ancestors 'self'");
    header('X-Frame-Options: SAMEORIGIN');
});

This only covers pages WordPress builds. Static files such as images and PDFs are not protected, which is usually fine, because those are not targets for clickjacking.

Cloudflare (no server access needed)

If your domain uses Cloudflare, Cloudflare can add the headers for you. This works whatever your web server is, and it also covers images, PDFs and other static files. Cloudflare

  1. Log in to the Cloudflare dashboard and choose your domain.
  2. Go to Rules › Overview.
  3. Click Create rule › Response Header Transform Rule.
  4. Under Rule name, enter Clickjacking protection.
  5. Under When incoming requests match, choose All incoming requests.
  6. Under Modify response header, choose Set static, then enter:
    • Header name: Content-Security-Policy
    • Value: frame-ancestors 'self'
  7. Click Set new header and add a second one, again with Set static:
    • Header name: X-Frame-Options
    • Value: SAMEORIGIN
  8. Click Deploy.

Set static replaces any header with the same name that your server already sends, so you do not end up with two copies. Add static would keep both. Cloudflare

The rule only works when the domain's DNS records are Proxied (orange cloud) in DNS › Records. Records set to DNS only (grey cloud) go straight to your server and do not get the headers.

If your site already sends a Content-Security-Policy with other rules in it, do not use Set static for Content-Security-Policy, because it would replace your whole policy. Add frame-ancestors to the policy on your server instead, and use Cloudflare only for X-Frame-Options.

Headers in a meta tag do not work

Neither header works in an HTML <meta http-equiv> tag. Browsers ignore both there; they must be sent as real HTTP headers. MDN MDN

Step 2: Check it worked

From a terminal, replace the address with your site:

curl -sI https://www.example.com/ | grep -iE 'content-security-policy|x-frame-options'

You should see both lines:

content-security-policy: frame-ancestors 'self'
x-frame-options: SAMEORIGIN

Without a terminal: open your site in Chrome or Firefox, press F12, open the Network tab, reload, click the first request and look under Response Headers.

Troubleshooting

Error 500 after editing .htaccess. There is a typo in the file. Remove the lines you added, save, and add them again carefully. Keep the <IfModule mod_headers.c> wrapper.

A header appears twice, with different values. Another layer already sends it: a security plugin, the host's server configuration, or a CDN such as Cloudflare. Keep it in one place only. With two different X-Frame-Options values, browsers may ignore the header.

Your site already sends a Content-Security-Policy header. Do not add a second one. Add frame-ancestors 'self' to the existing policy, separated from the other directives with a semicolon. frame-ancestors is not covered by default-src, so it must be listed on its own. MDN

A legitimate site can no longer embed your pages (for example a booking widget on a partner site). Add that site to frame-ancestors, as in the table above, and remove X-Frame-Options, which cannot allow a specific other site.

WordPress Customizer or a page builder stops showing a preview. You used 'none' / DENY. Change to 'self' / SAMEORIGIN.

Sources

  1. MDN: X-Frame-Options header
  2. MDN: Content-Security-Policy frame-ancestors directive
  3. Cloudflare: Create a response header transform rule in the dashboard

Written by Tharanga. Commands and procedures depend on your system and software version.

How we approach technical guides →
FOLLOW THE THREAD

Keep exploring

All guides
Linux1 min read

How to Check Disk Space in Linux

See how much disk space is free on a Linux server and find the folders using the most space, with two beginner-friendly commands: df and du.