HTTPS Is Only the Starting Point
Most website administrators assume that once they install a TLS certificate and enable HTTPS, their site is protected. It is an understandable mistake: the green padlock in the browser bar conveys trust. But HTTPS only guarantees one thing -- that traffic between the visitor's browser and your server travels encrypted. Nothing more.
HTTPS does not prevent an attacker from injecting malicious scripts into your page (XSS). It does not stop your site from being loaded inside a fraudulent iframe (clickjacking). It does not control which external resources can execute within your domain's context. And it does not limit what sensitive information leaks through referrer headers.
This is where HTTP security headers come in: directives your server sends to the browser, telling it how to behave when rendering your site. They are the second line of defense, and without them, HTTPS provides a false sense of security.
The 6 Essential Security Headers
1. Strict-Transport-Security (HSTS)
What it does: Tells the browser to only connect to your domain over HTTPS, even if the user manually types http://. It also prevents the user from accepting invalid certificates.
What attack it prevents: SSL stripping, where an attacker on the same network intercepts the initial HTTP request (before the HTTPS redirect) and keeps the connection in plain text.
Correct configuration:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
The value max-age=63072000 equals two years. includeSubDomains extends protection to all subdomains. preload allows your domain to be included in browsers' preload lists, which eliminates even the first insecure request.
Common mistake: Setting a max-age that is too short (for example, 3600 seconds) or omitting includeSubDomains, which leaves subdomains vulnerable to protocol downgrade attacks.
2. Content-Security-Policy (CSP)
What it does: Defines an allowlist of authorized origins for each resource type (scripts, styles, images, fonts, iframes). The browser blocks any resource that does not match the declared policy.
What attack it prevents: Cross-Site Scripting (XSS). If an attacker manages to inject a <script> tag pointing to an external domain, CSP prevents the browser from executing it.
Correct configuration:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'
The default-src 'self' directive restricts everything to the same origin by default. Specific exceptions are then opened for each resource type.
Common mistake: Adding 'unsafe-inline' or 'unsafe-eval' to script-src to "stop console errors." This effectively nullifies all CSP protection against XSS, as it allows inline script execution and dynamic code evaluation.
3. X-Frame-Options
What it does: Controls whether your page can be loaded inside an <iframe>. This prevents an attacker from displaying your site in an invisible layer on top of another page to capture user clicks.
What attack it prevents: Clickjacking. The attacker overlays your site (for example, a "Confirm Payment" button) on top of a seemingly harmless page, and the user clicks without realizing they are interacting with your site.
Correct configuration:
X-Frame-Options: DENY
If your site needs to be embedded in iframes from your own domain, use SAMEORIGIN instead of DENY.
Common mistake: Using ALLOW-FROM https://example.com, which is deprecated and not supported by modern browsers. Those who configure it believe they are protected, but the directive is silently ignored.
4. X-Content-Type-Options
What it does: Prevents the browser from "guessing" the MIME type of a resource. Without this header, a browser may interpret a text file as executable JavaScript if the content looks like it.
What attack it prevents: MIME sniffing. An attacker uploads a file with a .txt extension but JavaScript content. Without this header, the browser could execute it.
Correct configuration:
X-Content-Type-Options: nosniff
It has only one possible value. It is the simplest header to implement and one of the most frequently forgotten.
Common mistake: Simply not setting it. Many servers do not include it by default, and since it is an "invisible" header (it produces no obvious errors), it goes unnoticed for months or years.
5. Referrer-Policy
What it does: Controls what information is included in the Referer header when a user navigates from your site to another. This can contain the full URL (including parameters with tokens, session IDs, or other sensitive data) or just the origin.
What attack it prevents: Sensitive data leakage through the URL. If your page has parameters like ?token=abc123&email=user@example.com, an external link would receive all that information in the Referer header.
Correct configuration:
Referrer-Policy: strict-origin-when-cross-origin
This policy sends the full URL for same-origin navigation but only the domain (without path or parameters) for cross-origin requests. It is the optimal balance between privacy and functionality.
Common mistake: Setting no-referrer, which removes the header entirely. Although it is the most restrictive option, it breaks web analytics tools and some OAuth authentication flows that depend on referrer information.
6. Permissions-Policy
What it does: Controls which browser APIs your site can use: camera, microphone, geolocation, fullscreen, accelerometer, payments, and many more. It allows you to disable features your site does not need.
What attack it prevents: Browser feature abuse. If an injected script attempts to access the camera or microphone, Permissions-Policy will block it as long as you have disabled those permissions.
Correct configuration:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
The empty parentheses () mean "no origin has permission." Only enable the features your application actually needs.
Common mistake: Not configuring this header at all. By default, any script running on your page can request access to the user's camera, microphone, or geolocation.
How to Check Your Headers
You can manually inspect response headers from any browser's developer tools (Network tab). However, a one-time check does not guarantee the configuration persists after each deployment.
UareSafe automatically evaluates all 8 security header controls as part of the Technical Security dimension (which accounts for 30% of the total score). The analysis detects missing headers, insecure values, and deprecated configurations, generating a detailed report with actionable recommendations.
Quick Implementation Guide
Nginx
server {
listen 443 ssl;
server_name example.com;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}
Next.js (next.config.js)
const securityHeaders = [
{ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
{ key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
];
module.exports = {
async headers() {
return [{ source: '/(.*)', headers: securityHeaders }];
},
};
Express
const helmet = require('helmet');
app.use(helmet());
// Or manually:
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload');
res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self'");
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
next();
});
Conclusion
HTTPS is the floor, not the ceiling. Encrypting traffic is a minimum requirement, not a complete security strategy. The six headers described in this article take less than 15 minutes to configure and dramatically improve the security posture of any website.
This is not about complicating your infrastructure. It is about telling the browser how to protect your users. If your server does not send these instructions, the browser will make default decisions -- and those decisions rarely prioritize security.
Configure the headers. Verify them with every deployment. And if you want automated, continuous evaluation, UareSafe analyzes your site across the six security dimensions that matter, including the headers we just reviewed.