Strict-Transport-Security (HSTS) header set on server
Issue No: 136
Category: HTTP Security Headers
Issue type: Issue
Priority: IMPORTANT
Description
Verifies that the server responds with a valid Strict-Transport-Security (HSTS) HTTP response header when accessed over HTTPS to enforce secure connections and prevent SSL-stripping attacks.
How do we capture it
The XeoPix crawler evaluates this metric purely at the HTTP response layer. Since HSTS relies entirely on HTTP response headers, there are no browser rendering or JavaScript execution requirements for this audit.
- Protocol Validation: Identify the protocol of the target URL. If the initial or final redirected URL protocol is http://, log the state. (HSTS headers sent over plain HTTP are ignored by conforming browsers, but the presence of the header on HTTP or the lack of upgrade to HTTPS must be tracked).
- Request Execution: Execute a standard HTTP GET (or HEAD) request to the target HTTPS URL using the crawler's configured User-Agent.
- Response Header Extraction: Extract the raw headers from the HTTP response payload. Look for the Strict-Transport-Security header key (performing a case-insensitive search).
- Header Evaluation: If the header is missing, flag
has_hsts_headeras FALSE. If multiple Strict-Transport-Security header instances are returned in the response, flaghsts_is_validas FALSE with an error message, as browsers must ignore HSTS if duplicate headers are present. - Directives Parsing: Parse the tokenized header value using a semicolon (;) delimiter. Extract the max-age directive. Use a regular expression or string-parsing utility to match
max-age=\s*(\d+). Ensure that the value parses to a non-negative integer. Look for the standalone directivesincludeSubDomainsandpreload(case-insensitive). - Validation Rules: Set
hsts_is_validto TRUE if the max-age directive is successfully parsed and no duplicate headers are detected. Otherwise, set to FALSE and populatehsts_error_message.
What to store
| Field | Type | Comment |
|---|---|---|
is_https | BOOLEAN | True if the final resolved request protocol is HTTPS. |
has_hsts_header | BOOLEAN | True if the Strict-Transport-Security header is present in the response headers. |
hsts_header_value | VARCHAR | The raw string value of the Strict-Transport-Security header. |
hsts_max_age | INTEGER | The parsed value of the max-age directive in seconds; NULL if missing or unparseable. |
hsts_include_subdomains | BOOLEAN | True if the includeSubDomains directive is explicitly declared. |
hsts_preload | BOOLEAN | True if the preload directive is explicitly declared. |
hsts_is_valid | BOOLEAN | True if the HSTS header syntax complies with RFC 6797. |
hsts_error_message | VARCHAR | Detailed parsing error message if the HSTS validation fails. |
Condition for trigger
The following rules evaluate when the issue triggers:
-
Trigger Rule 1: HSTS Header Missing on HTTPS Connection
- Target Field:
has_hsts_header - Evaluation Logic:
is_https = TRUE AND has_hsts_header = FALSE - Severity: WARNING
- Diagnostic Message: "The Strict-Transport-Security (HSTS) HTTP header is missing from the secure HTTPS response."
- Target Field:
-
Trigger Rule 2: HSTS Max-Age Too Low
- Target Field:
hsts_max_age - Evaluation Logic:
is_https = TRUE AND has_hsts_header = TRUE AND (hsts_max_age < 31536000 OR hsts_max_age IS NULL) - Severity: WARNING
- Diagnostic Message: "The HSTS max-age directive is set to less than 31536000 seconds (1 year) or is missing, which is insufficient for secure preloading."
- Target Field:
-
Trigger Rule 3: HSTS Sent Over Insecure HTTP Connection
- Target Field:
is_https - Evaluation Logic:
is_https = FALSE AND has_hsts_header = TRUE - Severity: SUGGESTION
- Diagnostic Message: "The Strict-Transport-Security (HSTS) header is returned over an insecure HTTP connection. Browsers will ignore this configuration."
- Target Field:
-
Trigger Rule 4: Invalid HSTS Configuration or Duplication
- Target Field:
hsts_is_valid - Evaluation Logic:
has_hsts_header = TRUE AND hsts_is_valid = FALSE - Severity: CRITICAL
- Diagnostic Message: "The Strict-Transport-Security (HSTS) header is syntactically invalid or duplicated, causing client-side failure."
- Target Field:
Sources
- MDN Web Docs - Strict-Transport-Security: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
- RFC 6797 (HTTP Strict Transport Security): https://datatracker.ietf.org/doc/html/rfc6797
- OWASP HSTS Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html
- Google Search Central - HTTPS Best Practices: https://developers.google.com/search/docs/crawling-indexing/https-seo-overview
Long description
HTTP Strict Transport Security (HSTS), standardized under RFC 6797, is a crucial security mechanism that web servers use to declare that web browsers should only interact with them over secure HTTPS connections. When a browser receives this header over an HTTPS connection, it records the policy. For any subsequent requests within the specified max-age window, the browser will automatically upgrade all insecure http:// links to secure https:// requests client-side before any network traffic is sent. This effectively mitigates Man-in-the-Middle (MITM) downgrade exploits such as SSL stripping.
Common failure scenarios checked by the crawler include:
- No HSTS Implemented: Sites running on HTTPS that fail to serve the HSTS header expose users to redirection hijacking when navigating from an insecure asset link.
- Insufficient Max-Age Values: Setting the max-age value to a small duration (e.g., less than 31536000 seconds) prevents the domain from qualifying for HSTS preload inclusion in major browsers, leaving a window of vulnerability during the first-ever connection to the domain.
- Incorrect Connection Protocol: Serving HSTS over non-encrypted connections. The HSTS specification dictates that the header must be ignored by browsers over HTTP because a network attacker could simply strip or modify it.
- Syntax Violations: The occurrence of multiple HSTS headers in a single response, which invalidates the safety assumptions of browsers and leads them to ignore the protection entirely.
How to Fix
To fix this issue, configure the edge proxy, load balancer, CDN, or web server to send a single, well-formed Strict-Transport-Security header on all HTTPS responses.
- Ensure the web server first forces a redirect from HTTP to HTTPS.
- Once the secure HTTPS connection is established, serve the HSTS header.
- Configure the directive to use a secure duration, typically 1 year or more:
Strict-Transport-Security: max-age=31536000 - If all subdomains on the domain also securely support HTTPS, append the includeSubDomains directive:
Strict-Transport-Security: max-age=31536000; includeSubDomains - If you plan to submit the site to the browser-embedded HSTS preload list, append the preload directive:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload - Verify that your configuration does not send duplicate headers or have syntax typos.