When we talk about web application security, most developers think about protection at the code level: validating input, using parameterized database queries, and storing passwords properly. However, there is another critical layer of protection that is often overlooked: HTTP security headers. These headers are instructions that a server sends to a browser, telling it exactly how to handle page content and which actions are allowed or prohibited.
In 2026, as cyberattacks become increasingly sophisticated and regulators tighten requirements for protecting user data, correctly configuring security headers has ceased to be an optional recommendation and become a requirement for any serious web project.
Why security headers matter
Imagine this situation: you have developed an excellent web application, carefully checked the code for vulnerabilities, and used modern frameworks with built-in protection against common attacks. Everything seems fine. But then an attacker finds a way to inject a malicious script through user comments, or embeds your login page in an invisible iframe on a phishing site. It is in scenarios like these that security headers become the last line of defense.
Modern browsers have powerful protection mechanisms, but by default these mechanisms operate in a minimal mode to ensure backward compatibility with older sites. Security headers let you explicitly tell the browser that your site is modern and ready to use all available protection measures. It is like telling a building guard: “I know all my guests by name; don’t let anyone in unless I’ve introduced them.”
In addition to directly protecting against attacks, properly configured security headers affect SEO metrics. Google explicitly states that site security is a ranking factor, and although the direct effect of headers on rankings is debatable, their indirect effect through behavioral factors is undeniable. Users trust secure sites, spend more time on them, and more often complete desired actions.
Content-Security-Policy: controlling content sources
Content-Security-Policy, or CSP for short, is the most powerful and, at the same time, the most complex security header to configure. Its primary purpose is to prevent cross-site scripting (XSS) attacks, which remain one of the most common web security threats.
The way CSP works is elegantly simple: you explicitly tell the browser which sources are allowed to provide different types of resources. If an attacker manages to inject a malicious script into your page’s HTML, the browser will simply refuse to execute it because the script’s source is not listed among the allowed sources.
Let’s look at how this works in practice. Suppose you have an online store that loads scripts from its own domain and the jQuery CDN, styles from its own domain and Google Fonts, and images from its own domain and cloud storage. A correct CSP for this scenario would specify exactly these sources, and any attempt to load a resource from somewhere else would be blocked.
It is important to understand that deploying CSP on an existing site can be challenging. Many modern web applications use inline scripts and styles, which CSP blocks by default. Third-party widgets and analytics services can also be a problem because they require permission to run their scripts.
The recommended approach to deploying CSP consists of several stages. First, enable the policy in report-only mode using the Content-Security-Policy-Report-Only header. In this mode, the browser will not block violations, but it will send reports about them to the endpoint you specify. This lets you collect information about all the resources your site loads and configure the policy correctly without breaking functionality.
After collecting the data and compiling a list of legitimate sources, you can gradually tighten the policy. Start with basic directives that allow only the necessary sources, then add restrictions on inline scripts. For the latter, there is the nonce mechanism: one-time tokens generated by the server for each request that let you authorize specific inline scripts.
Strict-Transport-Security: enforcing HTTPS
The HTTP Strict Transport Security header, known as HSTS, addresses a problem that may seem insignificant but poses a serious security threat in practice. Even if your site runs entirely over HTTPS and redirects from HTTP, there is still a vulnerable moment: the user’s first request.
When a user types simply “example.com” into the address bar without specifying a protocol, the browser will try an HTTP connection first by default. Only after receiving a redirect will it switch to HTTPS. This brief moment creates an opportunity for a man-in-the-middle attack: an attacker who controls the network (for example, on public Wi-Fi) can intercept the HTTP request and alter the response, preventing the redirect from taking effect.
HSTS eliminates this vulnerability by telling the browser: “Remember that this domain should only be contacted over HTTPS, and don’t try to establish unencrypted connections.” Once it receives this header, the browser will automatically convert all HTTP requests to the domain into HTTPS before sending them, eliminating the possibility of interception.
The max-age parameter determines how long the browser will remember this instruction. It is recommended to set it to at least one year (31536000 seconds), so that even infrequent visitors remain protected between visits. The includeSubDomains directive extends the policy to all subdomains, which is important for preventing attacks through a compromised subdomain.
The HSTS Preload mechanism deserves special attention. It is a list of domains built directly into browsers for which HTTPS is mandatory from the very first visit. Inclusion on this list provides the highest level of protection, but requires careful preparation: removing a domain from the preload list is extremely difficult, and if for some reason you lose the ability to serve HTTPS, the site will become completely inaccessible to users.
X-Frame-Options and clickjacking protection
Clickjacking is a type of attack in which an attacker embeds your web page in an invisible iframe on their site. Deceptive interface elements are placed over the iframe to encourage the user to click in a particular spot. The user thinks they are clicking a harmless button, but their click actually lands on your page and performs an action there—for example, confirming a money transfer or changing privacy settings.
The X-Frame-Options header lets you control whether your page can be embedded in an iframe on other sites. The DENY value completely prohibits embedding; SAMEORIGIN allows embedding only on pages from the same domain. This simple mechanism effectively prevents clickjacking attacks.
In modern browsers, X-Frame-Options is gradually being superseded by the frame-ancestors directive in Content-Security-Policy, which offers more flexible configuration options. However, to ensure compatibility with older browsers, it is recommended to use both mechanisms at the same time.
X-Content-Type-Options: preventing MIME sniffing
Historically, browsers tried to be “smart” and determine content type not only from the Content-Type header but also from the file’s contents. This feature, called MIME sniffing, was created to improve the user experience on sites with incorrectly configured servers, but it opened the door to serious vulnerabilities.
Imagine this: a user uploads a file with a .txt extension to your site, but the file contains JavaScript code. The server correctly serves it as text/plain, but the browser analyzes its contents, decides that it is actually a script, and executes it. An attacker can then inject malicious code by bypassing server-side checks.
The X-Content-Type-Options header with the value nosniff tells the browser to strictly follow the declared MIME type and not try to guess the content type. This is a simple but effective protection measure that should be enabled on all web servers without exception.
Referrer-Policy: controlling referrer information
Every time a user follows a link from your site to an external resource, the browser sends the destination server a Referer header containing the URL of the page where the navigation originated. This information is useful for analytics, but can create privacy and security issues.
If your page URL contains confidential information—for example, a password reset token or session identifier—that data can leak to external sites through the referrer header. Even without explicitly secret data in the URL, the structure of your addresses can reveal the application’s internal architecture or users’ personal information.
Referrer-Policy lets you precisely configure what referrer information is sent in different situations. The strict-origin-when-cross-origin policy provides a reasonable balance: when navigating within the site, the full URL is sent; when navigating to external HTTPS sites, only the domain is sent; and when navigating to HTTP sites, no referrer is sent at all.
Permissions-Policy: controlling browser features
Modern browsers give web pages access to many device capabilities: geolocation, the camera, the microphone, the accelerometer, and many others. By default, a page can request permission to use these features, and the user decides whether to grant it.
However, if your page is embedded in an iframe on a third-party site, or if your page uses third-party scripts, those scripts can also request access to sensitive features. Permissions-Policy lets you explicitly limit which features are available to the page and its frames, regardless of the user’s decisions.
For example, if your site does not use geolocation, it makes sense to disable this feature entirely through Permissions-Policy. Even if an attacker manages to inject a malicious script, it will not be able to determine the user’s location.
Practical deployment of security headers
The configuration of security headers depends on the web server and application architecture you use. With Nginx, all headers are added using the add_header directives in the server configuration file. It is important to remember that add_header directives in child blocks completely override directives in the parent block, so it is often more convenient to put all headers in a separate file and include it using include.
For applications running behind a reverse proxy or load balancer, you need to make sure the headers are set at the right level. Some headers, such as HSTS, should only be set for HTTPS connections. If SSL termination happens at the load balancer rather than on the application server, the header-setting logic must take this into account.
After configuring the headers, it is critical to test that they work. Use the security header checker at reChecker to make sure all headers are set correctly and browsers receive the expected instructions.
Monitoring and maintenance
Configuring security headers is not a one-time task, but an ongoing process. Web applications evolve, new features and third-party services are added, and security policies need to be updated accordingly.
This is especially true of Content-Security-Policy. Every time you add a new third-party script or change how resources are loaded, you need to check whether it conflicts with the current policy. The CSP reporting mechanism lets you track policy violations in real time and respond to problems promptly.
It is also recommended to review settings regularly in light of new threats and security best practices. Web security standards evolve, new protection mechanisms emerge, and older ones become obsolete. Subscribing to web security newsletters and regularly auditing the configuration can help keep protection up to date.
Conclusion
Security HTTP headers are a powerful tool for protecting web applications. They work at the browser level and do not require changes to the application code. Properly configuring headers makes many common vulnerabilities significantly harder to exploit, even when they are present in the code.
Start with the basic headers: HSTS to enforce HTTPS, X-Content-Type-Options to prevent MIME sniffing, and X-Frame-Options to protect against clickjacking. Then gradually introduce more complex policies, such as CSP and Permissions-Policy, testing each change and monitoring for potential issues.
Remember that security is a multilayered system, and headers are just one layer of protection. They do not replace secure code, sound architecture, or other measures, but they complement them significantly by creating an additional barrier for attackers.