Security Headers for Domain Owners: CSP, HSTS, and Referrer-Policy Made Simple

Security headers represent one of the most impactful yet underutilized tools in website protection. These HTTP response headers instruct browsers on how to handle your content, providing defense against common attack vectors without requiring changes to your application code. For many domain owners, implementing security headers offers the best return on investment for improving site security.
What Security Headers Actually Do
Every time a browser requests a page from your website, your server responds with both the content and a set of headers. These headers contain metadata about the response: the content type, caching instructions, and various other directives.
Security headers are specific directives that tell browsers to enforce particular security policies. Unlike firewall rules or server-side validation, these protections run in the user's browser, creating a defense layer even if your server-side security has gaps.
The browser reads these headers and adjusts its behavior accordingly. For example, a security header might tell the browser to refuse loading any resources that don't come from your domain, or to always use encrypted connections when communicating with your site.
HSTS: Enforcing HTTPS Connections
HTTP Strict Transport Security (HSTS) addresses a fundamental vulnerability in how browsers handle secure connections. When someone types your domain into their browser without specifying a protocol, the browser typically attempts an HTTP connection first, then upgrades to HTTPS if redirected. This initial unencrypted request creates an opportunity for attackers to intercept the connection.
HSTS solves this by instructing browsers to always use HTTPS for your domain, even if the user types "http://" or omits the protocol entirely. Once a browser receives an HSTS header, it remembers this preference and automatically upgrades all future requests to HTTPS.
A basic HSTS header looks like this:
Strict-Transport-Security: max-age=31536000
The max-age directive specifies how long (in seconds) the browser should remember this policy. The value 31536000 represents one year, a common choice that balances security with flexibility to change your policy if needed.
Two optional directives enhance HSTS protection:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
The includeSubDomains directive extends HSTS protection to all subdomains. This is crucial because attackers often target forgotten or poorly maintained subdomains as an entry point. However, before enabling this, verify that all your subdomains support HTTPS. A single subdomain that only works over HTTP will become inaccessible to users once this directive is active.
The preload directive is an opt-in to have your domain included in browsers' built-in HSTS lists. This provides protection even on the very first visit to your site, before the browser has received your HSTS header. To use preload, your domain must meet specific requirements, and you need to submit it to the HSTS preload list maintained by Chromium, which other browsers also use.
Implementing HSTS Safely
Rolling out HSTS requires careful planning. Start with a short max-age value, perhaps 300 seconds (five minutes), and test thoroughly:
Strict-Transport-Security: max-age=300
This gives you a quick escape route if you discover issues. Monitor your site for problems over several days. Are all resources loading correctly? Do all features work over HTTPS? Are there any mixed content warnings in the browser console?
Once confident, gradually increase the max-age value. Move to one day (86400), then one week (604800), and finally one year (31536000). Only after operating successfully with a year-long max-age should you consider adding includeSubDomainsor pursuing preload status.
Before implementing HSTS, ensure you have reliable SSL certificates in place. When you're setting up secure infrastructure for your domain, having properly configured SSL certificates becomes essential, as HSTS makes your site completely inaccessible if certificates expire or are misconfigured.
Content Security Policy: Controlling Resource Loading
Content Security Policy (CSP) is the most powerful and most complex security header. It allows you to specify exactly which sources the browser should trust for various types of content: scripts, stylesheets, images, fonts, and more.
The primary attack CSP defends against is Cross-Site Scripting (XSS). In an XSS attack, an attacker injects malicious scripts into your site, which then run in users' browsers with full access to your site's data and functionality. CSP prevents this by restricting which scripts can execute.
A simple CSP header might look like:
Content-Security-Policy: default-src 'self'
This policy tells the browser to only load resources (scripts, images, styles, etc.) from your own domain. Attempts to load content from other origins will be blocked. While restrictive, this provides strong protection against many common attacks.
Building a Practical CSP
Most real-world sites need more flexibility. You might use a CDN for images, load fonts from Google Fonts, or embed videos from YouTube. A more realistic CSP accommodates these needs while maintaining security:
Content-Security-Policy: default-src 'self'; img-src 'self' https://cdn.example.com; font-src 'self' https://fonts.gstatic.com; style-src 'self' https://fonts.googleapis.com; script-src 'self'
This policy breaks down as follows:
- default-src 'self' sets the baseline: everything must come from your domain unless specified otherwise
- img-src 'self' https://cdn.example.com allows images from your domain and your CDN
- font-src 'self' https://fonts.gstatic.com permits fonts from your domain and Google's font CDN
- style-src 'self' https://fonts.googleapis.com allows stylesheets from your domain and Google Fonts
- script-src 'self' explicitly restricts JavaScript to your domain only
The Inline Script Challenge
One of CSP's biggest practical challenges involves inline scripts and styles. Many sites include JavaScript directly in HTML:
<button onclick="doSomething()">Click me</button>
By default, CSP blocks these inline scripts because attackers commonly use them in XSS attacks. You have several options:
Move all inline scripts to external files. This is the most secure approach and aligns with modern web development practices.
Use nonces, which are randomly generated values included in both your CSP header and inline script tags:
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m123'
<script nonce="r4nd0m123">
// Your script here
</script>
The nonce must be unique for each page load and impossible for attackers to guess. This approach requires server-side generation of nonces and template integration.
Use hashes, where you calculate a cryptographic hash of your inline script and include it in the CSP:
Content-Security-Policy: script-src 'self' 'sha256-abc123...'
Hashes work for static inline scripts but become cumbersome with dynamic content.
Testing CSP with Report-Only Mode
CSP includes a report-only mode that logs violations without enforcing the policy:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reports
This allows you to deploy a CSP, observe what it would block, and refine your policy before enforcement. Browsers send JSON reports to your specified endpoint whenever they would have blocked a resource.
Set up report-only mode and monitor reports for at least a week. You'll likely discover resources you forgot about: third-party analytics scripts, embedded widgets, or content from partner sites. Update your policy to accommodate legitimate resources, then switch to enforcement mode by changing Content-Security-Policy-Report-Only to Content-Security-Policy.
Referrer-Policy: Controlling Information Leakage
When users click a link on your site to visit another site, browsers typically send a Referer header (yes, it's misspelled in the standard) containing your page's URL. This tells the destination site where the visitor came from.
While sometimes useful for analytics, the referrer can leak sensitive information. If your URLs contain session tokens, user IDs, or other sensitive data, these could be exposed to third parties.
Referrer-Policy gives you control over what information is sent:
Referrer-Policy: strict-origin-when-cross-origin
This policy (now the default in modern browsers) sends the full URL for same-origin requests, but only the origin (domain) for cross-origin requests. If you're on an HTTPS page linking to an HTTP page, no referrer is sent at all.
Other useful policies include:
no-referrer sends no referrer information at all. This maximizes privacy but may break analytics or affiliate tracking that relies on referrer data.
same-origin sends referrer information only to your own domain, treating all external links as if no referrer exists.
strict-origin sends only the origin, never the full path, even for same-origin requests.
For most sites, strict-origin-when-cross-origin or same-origin strikes a good balance between functionality and privacy protection.
X-Frame-Options: Preventing Clickjacking
Clickjacking attacks trick users into clicking something different from what they perceive. Attackers embed your site in an invisible iframe over a decoy page, causing users to unknowingly interact with your site while thinking they're clicking something else.
X-Frame-Options prevents this by controlling whether your site can be embedded in frames:
X-Frame-Options: DENY
This completely prevents your site from being framed. If you need to allow framing on your own site (for example, if you have an admin panel that embeds other parts of your application), use:
X-Frame-Options: SAMEORIGIN
Note that CSP provides a more modern and flexible alternative through its frame-ancestors directive:
Content-Security-Policy: frame-ancestors 'none'
Or to allow same-origin framing:
Content-Security-Policy: frame-ancestors 'self'
If you implement CSP with frame-ancestors, you don't need X-Frame-Options, though many sites include both for older browser support.
X-Content-Type-Options: Preventing MIME Sniffing
Browsers sometimes try to be helpful by guessing content types when servers don't specify them correctly or when there's ambiguity. This "MIME sniffing" can create security vulnerabilities if a browser interprets uploaded files or user content as executable scripts when they shouldn't be.
The X-Content-Type-Options header disables this behavior:
X-Content-Type-Options: nosniff
This simple header tells browsers to strictly follow the Content-Type specified by your server. If you serve a file with Content-Type: text/plain, the browser won't try to execute it as JavaScript even if the file contains script code.
This header is straightforward and should be enabled on virtually all websites, as there's rarely a legitimate reason to want MIME sniffing behavior.
Permissions-Policy: Controlling Browser Features
Permissions-Policy (formerly Feature-Policy) allows you to control which browser features and APIs your site can use. This is particularly relevant for protecting user privacy and limiting the capabilities of third-party content embedded on your site.
A basic Permissions-Policy might look like:
Permissions-Policy: geolocation=(), microphone=(), camera=()
This disables access to location services, microphone, and camera for your site and any embedded content. If your site doesn't need these features, disabling them prevents both accidental misuse and malicious exploitation.
For sites that do need certain features, you can grant specific permissions:
Permissions-Policy: geolocation=(self), microphone=(), camera=()
This allows your own domain to access location services while blocking all other features.
Common features you might want to control include:
- geolocation for location services
- microphone and camera for media capture
- payment for Payment Request API
- usb for WebUSB API
- autoplay for video and audio autoplay
Deployment Checklist
When you're ready to implement security headers, follow this systematic approach:
Phase One: Assessment
- Document your current header configuration using browser developer tools or online header checking services
- Identify which resources (CDNs, third-party services, embedded content) your site relies on
- Review your application for inline scripts, styles, and event handlers
- Test your site thoroughly with HTTPS to ensure SSL certificates are properly configured
Phase Two: Initial Deployment
- Start with the simplest headers: X-Content-Type-Options and X-Frame-Options
- Implement HSTS with a short max-age value (300-3600 seconds)
- Deploy a basic Referrer-Policy
- Set up CSP in report-only mode with a liberal policy
Phase Three: Refinement
- Monitor CSP reports to understand what resources your site loads
- Gradually tighten your CSP based on actual usage patterns
- Increase HSTS max-age progressively as confidence grows
- Add includeSubDomains to HSTS only after verifying all subdomains support HTTPS
Phase Four: Maintenance
- Establish a process for updating headers when adding new third-party services
- Monitor for browser console errors that might indicate header-related blocks
- Keep security headers synchronized across all environments (development, staging, production)
- Review and update policies quarterly or when making significant site changes
Common Implementation Methods
The specific steps for deploying security headers depend on your hosting environment and infrastructure.
For sites with direct server access, headers are typically configured in the web server. Apache users add headers in the .htaccess file or virtual host configuration:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'"
Nginx users add headers in their server block configuration:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
For sites using managed hosting platforms, many providers offer header configuration through control panels or configuration files. When setting up hosting for your domain, check what options are available for customizing HTTP headers, as this can significantly impact your ability to implement security best practices.
Application-level frameworks often allow header configuration in code. This approach ensures headers are set correctly regardless of server configuration, though it requires application deployment to change policies.
Troubleshooting Common Issues
When implementing security headers, certain problems appear frequently:
Mixed content warnings occur when an HTTPS page tries to load resources over HTTP. Before enabling HSTS, ensure all resources (images, scripts, stylesheets) use HTTPS or protocol-relative URLs.
Third-party widgets breaking often happens with CSP. Social media sharing buttons, chat widgets, and advertising scripts frequently inject scripts from multiple domains. Document all third-party services you use and check their CSP requirements before deploying a strict policy.
Mobile app issues can arise with HSTS. Native mobile applications that load your content in webviews may have problems with HSTS, particularly if they use certificate pinning or custom networking stacks. Test thoroughly across all platforms.
Cached policies causing problems sometimes occur because browsers cache HSTS settings. If you need to roll back an HSTS policy, you can't force users' browsers to forget it. Starting with short max-age values mitigates this risk.
Measuring Success
After deploying security headers, several indicators help measure their effectiveness:
Browser developer tools show when CSP blocks resources. While some blocks indicate the policy working correctly, unexpected blocks might reveal legitimate resources that need authorization.
Security scanning tools and online header analyzers provide grades and recommendations. Regular scans help ensure headers remain properly configured as your site evolves.
User reports can indicate problems. If users suddenly can't access certain features or see increased error messages, security headers might be blocking necessary functionality.
Server logs may show increased errors for certain resources if your CSP is blocking them server-side (though most CSP enforcement happens client-side).
Beyond Basic Headers
Once you've mastered the fundamental security headers, additional headers provide more specialized protection:
Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy provide additional isolation for your site, particularly important if you need to enable powerful features like SharedArrayBuffer.
X-XSS-Protection was historically important but is now largely superseded by CSP. Modern browsers recommend against using it, as it can sometimes create vulnerabilities rather than preventing them.
Cross-Origin-Resource-Policy controls which origins can load resources from your server, complementing CSP which controls which resources your pages can load.
These advanced headers serve specific use cases and aren't necessary for most sites, but understanding they exist helps as your security requirements evolve.
Keeping Headers Current
Security header best practices evolve as new threats emerge and browsers add capabilities. The CSP specification continues to expand with new directives. Permissions-Policy covers an ever-growing list of browser features.
Subscribe to web security resources and browser update notes to stay informed. Major changes to security header recommendations typically happen gradually, giving site owners time to adapt.
Test your headers periodically, even if your site hasn't changed. Browser updates sometimes change how headers are interpreted, and what worked correctly last year might need adjustment.
Security headers represent a powerful tool in protecting both your site and your users. While implementing them requires careful planning and testing, the protection they provide is well worth the effort. Starting with basic headers and progressively enhancing your policy as you gain experience provides a practical path to robust security.
NameSilo Staff
The NameSilo staff of writers worked together on this post. It was a combination of efforts from our passionate writers that produce content to educate and provide insights for all our readers.


