If you have ever opened the “Network” tab in your browser’s developer tools or inspected a server response in Postman, you have likely seen them: a swarm of HTTP headers starting with the letter X.
X-Frame-Options, X-Powered-By, X-Request-ID, X-Forwarded-For.
They are everywhere, appearing in almost every web request you make. But what does the “X” actually stand for? Is it a mark of exclusion? A variable?
The answer lies in the early history of the web standards, a well-intentioned rule that backfired, and a massive course correction that changed how we build APIs today.
The Origin Story: “X” is for Experimental
In the early days of the web (specifically the era of HTTP/1.1), the Internet Engineering Task Force (IETF) needed a way to distinguish between standard headers and custom headers.
- Standard Headers: officially documented fields like
Content-Type,Host, orCache-Controlthat every browser and server must understand. - Non-Standard Headers: Custom fields created by developers for specific applications or browser vendors testing new features.
The rule was simple: If you are creating a custom or experimental header, prefix it with X-.
The logic was that once an experimental header (e.g., X-Special-Feature) became a standardized feature, the “X-” would be dropped, and it would become simply Special-Feature.
The Problem: When Experiments Become Permanent
The “X-” convention failed because of a phenomenon known as deployment inertia.
Let’s look at the classic example: X-Forwarded-For.
This header was created to identify the original IP address of a client connecting through a load balancer or proxy. It started as a non-standard “X” header. It became incredibly useful. Everyone started using it—load balancers (AWS, Nginx), proxies (Squid), and web frameworks (Express, Django).
Eventually, the standards body said, “Okay, this is useful. Let’s make it a standard.” They created a standard header called Forwarded.
But nobody switched.
Why? Because millions of lines of code, thousands of firewalls, and countless logging scripts were already looking for X-Forwarded-For. Changing the name would break the internet.
So, the “experimental” header became the de-facto standard, forever stuck with an “X” that no longer meant anything.
The Turning Point: RFC 6648
In June 2012, the IETF released RFC 6648, titled “Deprecating the ‘X-‘ Prefix and Similar Constructs in Application Protocols.”
This document officially admitted that the “X-” convention was a bad idea. The recommendation for developers today is:
- Do not use “X-” for new headers.
- If you need a custom header, just name it distinctively (e.g.,
MyCompany-Tracking-Idor simplyAuth-Token). - You do not need permission to create a new header name, provided it doesn’t conflict with existing standards.
The “Living Fossils”: X-Headers You Still Need
Even though the “X-” prefix is deprecated for new headers, many legacy “X” headers are still critical for security and functionality. You cannot ignore them.
1. X-Content-Type-Options
This is a vital security header. Setting this to nosniff prevents browsers from “MIME-sniffing” a response away from the declared content-type.
- Status: Essential.
- WebSphere Note: As mentioned in our previous article, legacy systems like IBM WebSphere often require custom properties (
com.ibm.ws.webcontainer.addXContentTypeOptions) to inject this header.
2. X-Frame-Options
Used to prevent Clickjacking attacks by stopping your site from being embedded in an <iframe> on another site.
- Status: Obsolescent. It is being replaced by the
Content-Security-Policy(CSP) directiveframe-ancestors. However, many sites still use both for backward compatibility.
3. X-Permitted-Cross-Domain-Policies
As discussed in our cross-domain policy guide, this handles permissions for Adobe products (PDF/Flash).
- Status: Niche but active for specific enterprise use cases.
4. X-Powered-By
Often sent by servers by default (e.g., X-Powered-By: Express or ASP.NET).
- Status: Harmful. This header leaks information to attackers about your technology stack. It is a best practice to remove this header in your server configuration.
Is X-Content-Security-Policy Deprecated?
Yes.
When Content Security Policy (CSP) was being developed, Firefox used X-Content-Security-Policy and Chrome used X-WebKit-CSP.
Once the standard was finalized, browsers switched to supporting the clean header: Content-Security-Policy.
If you are still using the “X-” version of CSP, you are targeting browsers that are over a decade old. You should update your configurations to use the standard name immediately.
Summary for Developers
The “X” in HTTP headers is a history lesson in how difficult it is to change protocols once they reach mass adoption.
- Reading headers: If you see an
X-header, treat it as a custom or legacy field. - Writing headers: If you are building an API today, do not start your custom headers with
X-. If you need to send a user ID, sendUser-IDorApp-User-ID, notX-User-ID.
The “X” was meant to mark the temporary, but in the world of the web, nothing is more permanent than a temporary solution that works.