Frameguard: X-Frame-Options Middleware
Frameguard is an Express.js middleware designed to enhance web application security by setting the `X-Frame-Options` HTTP header. This header primarily helps mitigate clickjacking attacks by restricting whether a browser can render a page in an `<frame>`, `<iframe>`, `<embed>`, or `<object>` tag. The current stable version is 4.0.0, and its release cadence is generally tied to the broader Helmet.js project, of which it is a part, receiving updates alongside Helmet's release cycle. While the `X-Frame-Options` header is largely superseded by the more robust `frame-ancestors` Content Security Policy (CSP) directive in modern browsers, Frameguard remains valuable for providing a layer of protection against clickjacking in older browser environments that may not fully support CSP. It differentiates itself by offering a simple, focused implementation for the most common and secure directives: `DENY` (preventing any framing) and `SAMEORIGIN` (allowing framing only from the same origin).
Common errors
-
TypeError: frameguard is not a function
cause Attempting to call `require('frameguard')()` directly without resolving the middleware function, or incorrect ESM default import.fixFor CommonJS, use `const frameguard = require('frameguard'); app.use(frameguard());`. For ESM, use `import frameguard from 'frameguard'; app.use(frameguard());`. -
Refused to display 'http://example.com/page' in a frame because it set 'X-Frame-Options' to 'DENY'.
cause This is not an error from the middleware but a browser security message indicating `DENY` is working as intended, potentially blocking a legitimate use case.fixIf you need to allow framing from the same origin, change the action to `sameorigin`: `app.use(frameguard({ action: 'sameorigin' }));`. If framing from other origins is required, consider using `Content-Security-Policy: frame-ancestors` instead. -
Header 'X-Frame-Options' not found in response headers.
cause The `frameguard` middleware was not correctly applied to the Express app or was overridden by another middleware.fixEnsure `app.use(frameguard(...))` is called before any routes that require the header. Check for other security middleware that might be removing or conflicting with the `X-Frame-Options` header.
Warnings
- gotcha The `X-Frame-Options` header is largely superseded by the more modern and flexible `Content-Security-Policy: frame-ancestors` directive. While `frameguard` is useful for older browsers, for comprehensive security, `frame-ancestors` should be preferred or used in conjunction.
- breaking The `ALLOW-FROM` directive for `X-Frame-Options` is not supported by this middleware. Attempting to use it will result in an invalid header or no header being set, compromising expected security.
- breaking When migrating from Helmet v4 to v5, `frameguard` is no longer included by default as a Helmet middleware. You must explicitly import and use `frameguard` as a standalone middleware.
- gotcha The default action for `frameguard()` when no `action` option is provided is `sameorigin`. This means pages will be frameable by content from the same origin, which might not be the most secure default for all applications.
Install
-
npm install frameguard -
yarn add frameguard -
pnpm add frameguard
Imports
- frameguard
const frameguard = require('frameguard').default;import frameguard from 'frameguard';
- frameguard
import frameguard from 'frameguard';
const frameguard = require('frameguard'); - FrameguardOptions
import { FrameguardOptions } from 'frameguard';import type { FrameguardOptions } from 'frameguard';
Quickstart
import express from 'express';
import frameguard from 'frameguard';
const app = express();
const port = 3000;
// Option 1: Prevent all framing (most secure default)
app.use('/deny', frameguard({ action: 'deny' }));
// Option 2: Allow framing only from the same origin
app.use('/sameorigin', frameguard({ action: 'sameorigin' }));
// Option 3: Default to sameorigin (if no action specified)
app.use('/default', frameguard());
app.get('/deny', (req, res) => {
res.send('This page cannot be framed.');
});
app.get('/sameorigin', (req, res) => {
res.send('This page can only be framed by same origin.');
});
app.get('/default', (req, res) => {
res.send('This page defaults to same origin framing.');
});
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
console.log('Try visiting http://localhost:3000/deny in a frame from another origin.');
});