SeenSecure Help

HTTP Security Headers

HTTP headers are instructions your site sends to the browser to protect against clickjacking, XSS, data theft, and more. Configure them from the SeenSecure panel.

🛡️ X-Frame-Options — Clickjacking Protection

🎯 What it does

Controls whether your site can be displayed inside an <iframe> on another website. Prevents external pages from embedding your content without permission.

⚠️ Why it matters

Without this protection, an attacker can load your site in a transparent iframe and trick users into clicking unknowingly (clickjacking). This can expose credentials, authorize payments, or change settings.

⚙️ Configuration options

  • DENY (recommended): Blocks any attempt to display your site in an iframe, no exceptions.
  • SAMEORIGIN: Only allows iframes from the same domain. Useful if you use legitimate iframes of your own.
🎯 Real example: an attacker builds a page that mimics a harmless giveaway or survey, and places your WordPress admin panel (wp-admin) inside a transparent <iframe>, positioned exactly over the "Participate" button. The victim — logged in as an administrator in another tab — thinks they're clicking "Participate", but the click actually lands on the real wp-admin button hidden underneath, such as "Delete user" or "Install plugin". Without X-Frame-Options, the browser allows your site to load inside that foreign iframe. With X-Frame-Options: DENY, the browser refuses outright to display your site inside any external iframe, leaving a blank gap on the attacker's page where the bait should be.
✅ Recommendation: Use DENY unless you have a specific reason to need your own iframes, in which case use SAMEORIGIN.

📝 X-Content-Type-Options — MIME Sniffing Prevention

🎯 What it does

Tells the browser NOT to guess the file type (MIME sniffing). Forces it to respect exactly the type declared in the Content-Type header.

⚠️ Why it matters

Without this header, a browser may interpret a text file as JavaScript and execute it. This allows attackers to upload a file with a .txt extension but with malicious code that the browser will execute.

⚙️ Configuration

The only valid value is nosniff. It is recommended to always enable it. It has no negative side effects.

🎯 Real example: a poorly validated file upload form (in a third-party plugin, for instance) lets an attacker upload a file named image.jpg that actually contains JavaScript code disguised as an image. If a visitor's browser tries to "guess" the content type instead of trusting the header the server declares, it can end up interpreting that file as a script and running it in the context of your site, instead of showing it as a harmless picture. With X-Content-Type-Options: nosniff, the browser sticks strictly to the type the server declared (image), and refuses to execute it as code no matter what its internal content suggests.
✅ HIGHLY RECOMMENDED: Enable it without hesitation. It is one of the safest headers with the least impact. It breaks nothing and protects against a common vulnerability.

🔒 X-XSS-Protection — Why We No Longer Send It

🎯 What was it?

A header that enabled a built-in anti-XSS filter in older browsers (Internet Explorer, old versions of Chrome and Safari), designed to detect and block reflected script injection attempts.

⚠️ Why we removed it

Chrome removed it entirely in 2019 — not that it just stopped having an effect, Google actually considered it more of a risk than a help, because the filter itself had documented security flaws and was fairly easy to bypass. Firefox never implemented it at all. Microsoft ended support for Internet Explorer in 2022. Keeping a header that no longer protects anything real, and that could once introduce its own problems, made no sense.

✅ Does removing it lose any protection? No. In Internet Explorer, the only browser where this header ever mattered, the filter came enabled by default even without the site sending any header — the header only let a site change that behavior (force full blocking, or disable it if it caused false positives). By not sending the header, IE simply keeps its factory behavior: nothing that was previously on gets turned off.
🎯 The real protection, today: the attack this filter tried to stop (reflected XSS: a crafted URL that makes a poorly escaped search box or field return executable code to the browser of anyone clicking a prepared link) is covered today by two layers that work the same in any browser, old or new: the firewall's WAF, which blocks the injection pattern at the request level before the HTML reaches the browser, and the Content-Security-Policy, which stops the browser from executing unauthorized scripts even if the malicious code is already sitting in the page. Neither one depends on the browser having its own heuristic filter.

🔗 Referrer-Policy — Referrer Leakage Control

🎯 What it does

Controls how much information from the origin URL is sent in the Referer header when users click a link or load an external resource.

⚠️ Why it matters

Without control, the full URL (including parameters like tokens, session IDs, or personal data) can leak to external sites. This violates user privacy and can expose sensitive data.

⚙️ Configuration options

  • no-referrer: Never sends the Referer. Maximum privacy, but may break analytics.
  • same-origin: Only sends Referer within the same domain. Does not send to external sites.
  • strict-origin: Sends only the origin (domain), never the full URL. Only on HTTPS-to-HTTPS connections.
  • strict-origin-when-cross-origin (recommended): Sends full URL within the same domain, and only the origin to external sites. The best balance between functionality and privacy.
🎯 Real example: a logged-in user browses to a page whose URL includes a sensitive token as a parameter (for example, a freshly generated password-reset link). If that page includes a resource loaded from an external domain — a web font, an ad banner, a third-party widget — the browser could send that full URL, token included, as the Referer header to the external server, without the user or the external site's owner explicitly asking for it. With Referrer-Policy: strict-origin-when-cross-origin, the browser only shares the origin domain with external sites, never the full path with the token, and links within your own domain keep working normally.
💡 Recommendation: Use strict-origin-when-cross-origin. It is the default in modern browsers and offers the best balance between functionality and privacy.

🔐 HSTS — Enforce HTTPS Connection

🎯 What it does

Forces browsers to connect exclusively via HTTPS, even if the user types http:// or clicks an HTTP link. The browser remembers this for the configured max-age.

⚠️ Why it matters

Without HSTS, an attacker on the same network (public Wi-Fi, for example) can intercept the first HTTP connection and redirect the user to a fake site (man-in-the-middle attack). HSTS eliminates this vulnerability window.

⚙️ Configuration: max-age

  • max-age=0: Disables HSTS. Forces browsers to forget the policy.
  • max-age=3600 (1 hour): For testing. Low risk, browsers forget quickly.
  • max-age=63072000 (2 years, recommended): For production. Browsers will remember to enforce HTTPS for 2 years.
🎯 Real example: a customer connects to a coffee shop's free wifi and types your domain from memory into the address bar, without the https:// prefix. The browser first attempts the insecure HTTP connection. An attacker on that same wifi network can intercept that first HTTP request and return a fake copy of your site to steal credentials, before the normal redirect to HTTPS completes (this is known as a man-in-the-middle attack). With HSTS active and the site already visited once before over HTTPS, the browser doesn't even attempt the insecure connection: it forces HTTPS directly, locally on the device itself, denying the attacker that interception window.
🚨 CRITICAL WARNING: If you enable HSTS with a high max-age and later disable HTTPS on your server, browsers will block access to your site until max-age expires. There is no way to revoke it early. Use a low value first for testing.

🎯 Content-Security-Policy (CSP) — The Content Police

🎯 What it does

Defines exactly which sources are authorized to load scripts, styles, images, fonts, and other resources on your site. Any unauthorized resource is automatically blocked by the browser.

⚠️ Why it matters

CSP is the most powerful defense against XSS. Even if an attacker manages to inject a malicious script, CSP blocks it because the script does not come from an authorized source. It is your last line of defense.

🎯 Real example: a poorly filtered comment or form field lets an attacker inject a <script> tag that loads code from a foreign domain into one of your pages (a typical failure when a plugin doesn't correctly escape user input). Without CSP, any visitor's browser loading that page would execute the script without question, and the code could steal session cookies, redirect to a phishing site, or capture keystrokes. With a CSP that restricts script-src to your own domain (and any sources you've explicitly authorized), the browser detects that the foreign domain isn't on the allowed list and refuses to load and run the script, even though the malicious code is already sitting in the page's HTML.

⚙️ Configuration

CSP uses a free-text editor where you write the policy directly. There are no one-click template buttons.

🚨 The default value is NOT a complete policy — it's just one minimal line: the first time you enable CSP, the text field is pre-filled with only default-src 'self';. That single directive acts as a fallback for EVERY resource type that has no explicit directive of its own (scripts, styles, images, fonts...), and since it does not include 'unsafe-inline' or any external domain, it will block the inline styles and scripts that almost any WordPress theme or plugin writes directly into the HTML, plus any resource loaded from an external CDN (Google Fonts, jQuery, YouTube, etc.). Do not turn the CSP switch on with this value unedited — it will very likely break the look and functionality of a real WordPress site.
💡 What to do instead: before enabling the CSP switch, open your site with the browser console (F12) and manually add to the text field the domains your theme, plugins, and third-party scripts need (Google Fonts, YouTube, your analytics provider, etc.), following the directive syntax explained below. Always start with the firewall in Monitoring mode so you can review console errors without risking taking your live site down.
Why isn't there a one-click "WordPress-safe" template? It was tested thoroughly beforehand: there is no single CSP policy that works without breaking something across the thousands of different theme + plugin + third-party-service combinations that run on top of this plugin — there's always some case that needs unsafe-inline, a specific CDN domain, or a permission another site doesn't need. That's why the field is left blank (with the minimal fallback default-src 'self';) so you build it yourself based on what your site actually loads, instead of offering a button that invites enabling it blindly.
⚠️ CSP Risk: A misconfigured policy can break your site (block essential scripts, styles, images, or resources). Always test first with the firewall in Monitoring mode while watching the browser console. Add only what your site actually needs, directive by directive.
💡 Tip: If something stops working after enabling CSP, open the browser console (F12). The errors will tell you exactly which resource was blocked and from which domain. Simply add that domain to the corresponding directives.

✍️ How a CSP Policy Is Written (Basic Syntax)

Even though it looks like an intimidating block of text, CSP always follows one very simple, consistent structure:

directiva1 valor1 valor2 valor3; directiva2 valor1; directiva3 valor1 valor2;
  • Each directive ends with a semicolon (;). That's what separates one rule from the next — if you forget one, the next directive can end up "glued" to the previous one and stop working.
  • Within a directive, values are separated by spaces (never commas): script-src 'self' https://example.com https://other.com; authorizes three different sources for scripts.
  • Special keywords go in single quotes: 'self' (your own domain), 'none' (nothing, completely forbidden), 'unsafe-inline' (allows code written directly in the HTML). Regular domains never get quotes: https://fonts.googleapis.com, not 'https://fonts.googleapis.com'.
  • You can authorize an entire domain with a wildcard: https://*.googleapis.com allows any subdomain of googleapis.com, instead of having to list each one.
✏️ How to add a new permission, step by step: find the matching directive (e.g. script-src for a script) within the text already in the field, and add the new domain right before that directive's semicolon, separated by a space from the previous value. Example: if you had script-src 'self' 'unsafe-inline'; and need to authorize https://widget.example.com, change it to: script-src 'self' 'unsafe-inline' https://widget.example.com;. Don't delete anything that was already there, just add.

📚 The Directives You'll Actually Use (How Many There Are and What Each Is For)

The CSP specification defines more than 20 directives in total, but on a normal WordPress site you'll only ever need to touch these 12 — they're the ones already in the default value or the ones most often needed when adding a new service:

Directive What it's for (what it controls) When you touch it
default-srcFallback value: applies to any resource type that doesn't have its own specific directive below.Almost never — it's a safety net
script-srcWhere JavaScript can be loaded and run from (<script>, code in onclick attributes, etc.)When installing a chatbot, a tracking pixel, a reviews widget...
style-srcWhere CSS stylesheets and styles written directly in the HTML can be loaded fromWhen adding an external font (Google Fonts, Adobe Fonts...)
img-srcWhere images can be loaded fromIf you use an image CDN, external avatars (Gravatar), embedded maps...
font-srcWhere font files (.woff, .ttf...) can be loaded fromAlmost always goes hand in hand with style-src for the same font service
connect-srcWhich domains the page's JavaScript can connect to (fetch requests, XMLHttpRequest, WebSockets)When integrating analytics, live chat, any service that sends data in the background
frame-srcWhich external pages can be embedded inside an <iframe> on your siteWhen embedding a YouTube/Vimeo video, a Google Maps map, a form from another service
media-srcWhere audio/video files played with <audio>/<video> can be loaded fromIf you host podcasts or your own video outside your domain (e.g. on a separate CDN)
object-srcControls <object>, <embed>, and <applet> (Flash and old browser plugins)Never — always leave it at 'none', it's obsolete and insecure technology
base-uriWhich URLs the page can use as a base to resolve relative links (<base> tag)Almost never — 'self' is correct 99% of the time
form-actionWhich URLs a form on your site can submit (action=) toIf a form sends data to an external domain (e.g. a payment gateway with a redirect)
frame-ancestorsWho can embed YOUR site inside an <iframe> (anti-clickjacking protection)Almost never — 'self' already covers most cases
upgrade-insecure-requestsForces any resource requested over HTTP to automatically reload over HTTPS (takes no values, stands alone)Always leave it on if your site uses HTTPS (the norm nowadays)

🔍 How to Find the Value to Look For When Something Breaks

You have three ways to find out exactly which domain your CSP is missing, from fastest to most thorough:

  1. Browser console (F12 → "Console" tab): the most direct method. Every blocked resource shows up as a red line literally saying "Refused to load/execute... because it violates the following Content Security Policy directive: 'script-src ...'" — the final part tells you the exact directive, and right before it you'll see the full URL of the blocked resource. Copy that URL's domain and add it to that directive.
  2. Browser "Network" tab (F12 → "Network"): useful when you want to see, even before enabling CSP, which domains your page actually depends on. Reload the page with that tab open and you'll see every request with its origin domain — so you know in advance what you'll need to authorize.
  3. The external service's own documentation: if you're installing a live chat, a payment form, or a well-known third-party widget, search its help site for "CSP" or "Content Security Policy" — most serious services (Stripe, Intercom, HubSpot, etc.) publish exactly which domains you need to authorize and in which directive, because it's a very common question among their own customers.
🧩 Full step-by-step example: you install a chat widget from an external provider and, after enabling CSP, it stops appearing. You open the console (F12) and see: Refused to load the script 'https://widget.chatprovider.com/loader.js' because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'". You find script-src in the text field and add the domain: it goes from script-src 'self' 'unsafe-inline'; to script-src 'self' 'unsafe-inline' https://widget.chatprovider.com;. You save, reload the page with F12 open again — if the chat loads and no new related error shows up, it's fixed. If, once loaded, the chat also needs to connect to another domain to send messages, you'll see a second error pointing to connect-src, and you repeat the same process with that directive.

📖 Where to Consult the Full Reference

With the 12 directives in the table above you'll solve virtually any real case on WordPress. If you ever need a more specific directive that isn't here (there are more than 20 in total, some very rarely used outside complex web apps), the official and most complete reference is MDN Web Docs (Mozilla) documentation, which maintains the full list with examples and per-browser compatibility — search "MDN Content-Security-Policy" in any search engine. It's the same source browser developers themselves use.

🛠️ Also within the plugin itself: in this same Headers tab, the "Logging and Diagnostics" section lets you enable logging of which headers are actually sent and display them in a diagnostic panel — useful for confirming that the policy you saved is really the one reaching your visitors' browsers, without relying only on viewing the page source.

🔌 Permissions-Policy — Browser API Control

🎯 What it does (simple explanation)

Imagine your website is a house and each visitor comes in with their phone. This permission tells the browser: \"this site doesn't need to know where you are, see your face, or listen to you\". So even if a malicious script sneaks into your site, the browser won't lend it the camera, microphone, or location.

⚠️ Why it matters (real scenario)

Think about this: you have an online store. A hacker finds a flaw in a plugin you installed 2 years ago and manages to inject a script. That script could silently turn on the camera of the visitor browsing your products, pull their exact location, or record with the microphone. Without Permissions-Policy, the browser won't say no. With this enabled, even if the malicious script tries, the browser will respond: \"Sorry, this site doesn't have permission to use the camera\". And the visitor never even knows it was attempted.

🧠 Think of it this way: It's like telling an employee: \"You work at the counter, you don't need the keys to the safe\". Even if someone steals their identity and gets into the store, they can't open the safe because that employee never had access. Permissions-Policy does exactly that: it tells the browser \"this site doesn't need the camera, don't give access even if asked\".

🧩 What exactly does it block?

These are phone or computer features that a normal website DOES NOT need to work. If your site is a blog, a store, or a corporate site, you shouldn't need any of these:

📡 Location

Example: A bakery website doesn't need to know where you live to show you a cake. Risk: A spy script could track your visitors. Blocked ✅

🎥 Camera

Example: A recipe site doesn't need to see you to teach you how to cook. Risk: Spying on visitors by recording them without their knowledge. Blocked ✅

🎤 Microphone

Example: A digital newspaper doesn't need to hear you to show you news. Risk: Recording private conversations. Blocked ✅

💳 Payments / 🔔 Notifications

Example: Most sites use their own payment form or third-party plugins, not the browser's native API. Risk: Unauthorized scripts taking money or sending fake notifications. Blocked ✅

🔄 Sensors / 🔗 USB

Example: A news site doesn't need to know if you're walking (accelerometer) or access your USB devices. Risk: Knowing if the user is still or moving, or accessing USB files without permission. Blocked ✅

✅ Is it safe to enable? Yes, absolutely. Most WordPress sites don't use any of these features (camera, microphone, GPS...) to work. It's like closing a door that was already closed: it doesn't affect anything and protects if someone tries to open it. If your site is a blog, store, landing page, or corporate site, enable it without fear. Only if your site offers video calls or uses the user's location should you disable it.

🔐 SRI (Subresource Integrity) — CDN Resource Protection

🎯 What it does (simple explanation)

Imagine you order a pizza for delivery. The delivery person brings you a sealed box with a security seal. If the seal is broken, you know someone opened it and you don't eat it. SRI does exactly that with the files your site loads from other servers (Google Fonts, jQuery, Bootstrap...): it puts a \"digital seal\" on each file. If the file arrives modified (by a hacker, for example), the browser detects the seal is broken and rejects it. The user never knows, but they're protected.

⚠️ Why it matters (real scenario)

Almost all websites use external resources: Google Fonts for nice typography, jQuery for visual effects, Bootstrap for mobile responsiveness, Google Analytics for visitor stats... All these files are on servers you DON'T control. There have been real cases where hackers injected malicious code into well-known CDNs. Without SRI, if a CDN gets hacked, your site would load that virus without you noticing. With SRI, the browser says: \"This file doesn't match its fingerprint, I'm blocking it\".

🔍 Real case that happened: In 2018, an attacker managed to modify a JavaScript file on a CDN used by millions of sites (a library called Event-Stream). The modified file stole cryptocurrency from visitors. If those sites had SRI enabled, the browser would have detected that the file no longer matched its original fingerprint and blocked it automatically. No one would have lost money.

⚙️ How it works in 3 steps (easy)

1️⃣ Automatic scan

SeenSecure looks at your site and finds all files loaded from external servers (Google Fonts, jQuery, Bootstrap, icons, etc.). It's like taking inventory of everything that comes into your house.

2️⃣ Fingerprint is calculated

For each file, SeenSecure generates a unique \"digital fingerprint\" (a special code that identifies that exact file). It's like taking fingerprints of each resource. If the file changes even by one letter, the fingerprint changes completely.

3️⃣ Active protection

When someone visits your site, the browser compares the fingerprint of the file that arrives with the one it should have. If they match → the file runs normally. If they don't match → the file is rejected. Simple as that. The visitor sees the site correctly and never knows there was an attack attempt.

📌 How often do you need to rescan? Only when you add something new: a plugin that loads jQuery from a CDN, a theme that uses Google Fonts, a new icon. That is, almost never. And when you do, it's just one click in the panel. The rest of the time, SRI works on its own.
✅ Is it safe to enable? Yes, absolutely. SRI doesn't slow down your site (the browser of the visitor does the verification, not your server). It doesn't break anything if the files are legitimate. The only case where it would block something is if a CDN file has been modified... which is exactly what we want. Enable it and forget it.

📋 Summary & Recommendations

Not all headers have the same level of risk or urgency. This table helps you prioritize:

Enable Immediately

These headers have no negative side effects and protect against common attacks:

  • X-Frame-Options: DENY — Clickjacking
  • X-Content-Type-Options: nosniff — MIME sniffing
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy — Browser APIs
  • SRI — CDN resource integrity

Enable with Caution

These headers require prior verification. They can block the site if misconfigured:

  • ⚠️ HSTS — Start with a low max-age (3600). Only raise to 2 years when you are 100% sure HTTPS will never be disabled.
  • ⚠️ CSP — Do not enable it with the default one-line value. Build the policy yourself in Monitoring mode first, watching the browser console. If something gets blocked, adjust the policy.
🎯 Recommended activation order: 1️⃣ X-Frame-Options → 2️⃣ X-Content-Type-Options → 3️⃣ Referrer-Policy → 4️⃣ HSTS (with caution) → 5️⃣ CSP (with caution). The first 3 are safe and can be enabled together.