First establish the version and response path
Nginx sets HSTS through add_header. The browser processes the policy after receiving it over valid HTTPS. A known policy can then prevent an unencrypted HTTP connection to a covered host. The HSTS fundamentals explain first contact, host scope and subdomains.
Before making a change, establish:
- the Nginx version in use, for example with
nginx -v; - the HTTPS
serverblock and included configuration files; - existing
add_headerrules inhttp,serverand the relevantlocationblocks; - any CDN or proxy in front, and where the browser's HTTPS connection terminates;
- the affected hosts, such as
example.comandwww.example.com, and any HSTS or preload policies inherited from a parent host.
The version numbers below describe documented availability of individual directives. They are not a recommendation to continue running old Nginx versions. Nginx: command-line parameters, headers module
Browsers, CDNs and Nginx have different roles
Schematic explanation · not a measurement
Which response reaches the browser?
The request travels from the browser to the optional CDN/proxy layer and, where necessary, onwards to Nginx. The arrows below show the response travelling in the opposite direction.
Nginx at the origin
Generates the response itself or takes it from an application. The effective Nginx header rules apply here.
CDN / proxy, if present
May forward, serve from cache or generate its own response.
Final delivery layer
Also owns its redirects and error pages. An origin header does not establish their contents.
Browser
Processes HSTS from the validly received HTTPS response. This is where to check the header that actually arrived.
Without an intermediary, Nginx responds directly to the browser. This model is not a CDN measurement. Browser HSTS does not prove encryption between the CDN, Nginx and application; those connections need their own evidence.
This is a schematic arrangement, not an inventory of your website. Without an intermediary, the browser connects directly to Nginx. For each relevant response, establish which layer sets the HSTS header. Nginx: proxy module
Start with a short HSTS policy
HSTS requires working HTTPS and reliable certificate renewal. Add the following directive to the already working HTTPS server block for the relevant host:
add_header Strict-Transport-Security "max-age=300" always;The 300 seconds equal 5 minutes. This starting point contains neither includeSubDomains nor preload. A short value limits the commitment of newly learned policies; it does not shorten older entries in browsers that have not yet received the new header. The directive is a snippet, not a complete web server configuration.
always has been supported since Nginx 1.7.5. Without it, add_header adds the header only for the documented status codes. With always, the directive applies regardless of response status, including applicable 4xx or 5xx responses. This resolves neither an inheritance problem nor adds a header to a response that another layer generates independently of Nginx. Nginx: add_header
Redirect HTTP to HTTPS
If you offer HTTP on port 80, configure a redirect. The following example applies only to example.com and assumes that HTTPS works on the same host:
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}Replace the example host deliberately and handle other hosts separately. Moving from the base domain to www is an additional redirect; for a later preload submission, the initial HTTP-to-HTTPS step must stay on the same host. Send the HSTS header over HTTPS; browsers ignore it over unencrypted HTTP. Preload requirements, RFC 6797 §8.1
TLS remains a separate configuration
HSTS does not select a TLS version or cipher. RFC 8996 deprecates TLS 1.0 and 1.1; selecting modern TLS versions is part of transport configuration. For a Nginx/TLS build with the required support, an example directive is:
ssl_protocols TLSv1.2 TLSv1.3;This is not complete TLS hardening. Check build support, certificates and other TLS settings separately. HTTP/2 is not a prerequisite for HSTS, so an additional http2 on directive does not belong in this minimal HSTS example. Nginx: ssl_protocols, RFC 8996, RFC 9325 §3.1
Check header inheritance for your Nginx version
Under the standard model, a child configuration context inherits add_header directives from its parent only if it contains no add_header directive itself. A single rule for another header can therefore displace the inherited HSTS rule. always does not change this inheritance. Nginx: header inheritance
Suppose the HTTPS server block contains the initial HSTS directive. An existing child configuration contains:
location /assets/ {
add_header X-Content-Type-Options nosniff always;
}Expected under standard inheritance: this separate add_header rule prevents inheritance of the HSTS rule from the server block. The example illustrates the problem; it does not recommend replacing your existing location configuration.
One solution under this model is to bring together all headers required in this context explicitly. For the two rules shown, the snippet would be:
location /assets/ {
add_header Strict-Transport-Security "max-age=300" always;
add_header X-Content-Type-Options nosniff always;
}Preserve the delivery logic your existing location actually needs. If the HSTS value appears in several places, later changes must reach every relevant one. Also check error pages and internal redirects, which can use a different context.
From Nginx 1.29.3: add_header_inherit
Since 1.29.3, add_header_inherit can change the inheritance rules:
On narrow screens, scroll the table sideways.
| Value | Documented meaning |
|---|---|
on | Standard model; this is the default. |
off | No inheritance of add_header values from the parent level. |
merge | Values from the parent level are merged with those defined at this level. |
A possible snippet for a supported build is:
add_header_inherit merge;This setting can itself be inherited by deeper contexts. Check the entire affected hierarchy. Do not blindly add merge to a configuration that already repeats HSTS in every child context: this can create duplicate headers. Standard inheritance, explicit repetition and merge were executed on Nginx 1.30.5 in the original checks. Availability since 1.29.3 is documented by the vendor; older Nginx binaries were not executed. Nginx: add_header_inherit
Assign clear responsibility across reverse proxies and CDNs
When Nginx sits in front of an application, the application's HSTS header may already be present in the forwarded response. add_header is not a general replacement rule for an upstream header of the same name. If Nginx deliberately owns the policy centrally, a forwarded HSTS header can be suppressed in the appropriate proxy context:
proxy_hide_header Strict-Transport-Security;This directive only removes the named field from the forwarded proxy response; it does not set a new HSTS header. Your own add_header rule and its inheritance must still be correct. The snippet applies to proxy_pass responses; handle other upstream modules or CDN rules separately. Nginx: proxy_hide_header
Check the response reaching the browser to establish that the intended policy arrives unambiguously. Multiple Strict-Transport-Security fields are not combined into the strongest policy; RFC 6797 requires processing of the first field. Do not rely on the order of conflicting headers. RFC 6797 §8.1
An intermediary can also serve its own cached responses, redirects or error pages. A correctly set origin header does not establish their contents. Check the public address and the known source of a response separately.
Load the configuration and compare responses
Check the changed configuration in your isolated test environment first. nginx -t must succeed before a reload. On a system where systemd manages Nginx, you can link the syntax check and reload as follows:
sudo nginx -t && sudo systemctl reload nginxA successful reload loads the new configuration and lets old workers finish existing connections in an orderly way. If your system manages the service differently, use its intended reload mechanism. A passed syntax check does not yet prove which header reaches the browser through the CDN, proxy and application. Nginx: control and reload
Start by checking an HTTPS response without automatically following redirects:
curl --silent --show-error --dump-header - --output /dev/null https://example.com/The command makes a GET request, displays response headers and discards the body. It does not disable certificate verification. Replace the example address with a host you own or are authorised to test. In the complete output, look for the status, Location and Strict-Transport-Security; also check whether the same header appears more than once. curl: options
The following is the expected header line for the initial example; this exact value also appeared in the corresponding original local HTTPS check:
Strict-Transport-Security: max-age=300For a redirect, request the specified destination separately afterwards. Record the host at each step. Check HTTP separately if that service is offered:
curl --silent --show-error --dump-header - --output /dev/null http://example.com/Response matrix for your own checks
The table is a test template for your environment. Each status code assumes the test response described. Select the actual paths from your application; deliberately trigger error cases only in an isolated environment. The separate local check below does not replace that inventory.
Table: test template for an initial policy of max-age=300.
On narrow screens, scroll the table sideways.
| Case to test | Responsible context in the model | Expectation | Your measurement |
|---|---|---|---|
| Main HTTPS path, 200 | Nginx, effective HSTS rule | Exactly the intended HSTS policy | Complete in your environment |
HTTPS subpath, 200, with its own add_header rule | Child location | HSTS only if inheritance or an explicit definition is correct there | Complete in your environment |
| HTTPS redirect, 301 | The host sending the redirect | HSTS on this HTTPS response already; check the target host separately | Complete in your environment |
| HTTPS error page, 404 | The actual error-page context | HSTS with an effective add_header ... always rule | Complete in your environment |
| HTTPS error page, 500 | The actual Nginx/application path | HSTS with an effective add_header ... always rule; check upstream duplicates | Complete in your environment |
| Independent CDN/proxy error response, such as 502 | Intermediary; Nginx response is not used | The policy must be supplied at this layer; the Nginx rule alone is insufficient | Complete in your environment |
| HTTP request, 301 | HTTP redirect | HTTPS target on the intended host; a browser would ignore an HSTS header over HTTP | Complete in your environment |
The matrix does not prove completeness. Add other relevant hosts, paths, cache states and response-producing layers. A mismatch between expectation and response is a concrete configuration question, not an automatic diagnosis of a security vulnerability.
Check browser state and external tools as additional evidence
The network panel in browser developer tools shows the URL actually requested and its response headers. A visible header describes that response, not every HSTS rule stored by the browser.
Internal browser HSTS views can help distinguish dynamic and preloaded rules; availability and operation depend on the version. hstspreload.org points Chrome users to chrome://net-internals/#hsts. One field name or a local deletion is not a general check of every visitor's state. HSTS Preload: status
The SSL Labs Server Test is an additional external tool for SSL/TLS analysis. Its result replaces neither this response matrix nor an inventory of every subdomain.
Responses measured in the local Nginx laboratory
The following findings come from 48 HTTP requests made for the German source against six separate Nginx 1.30.5 configurations. Nginx and a synthetic upstream listened only on loopback ports. For HTTPS, the test client specifically trusted a dedicated one-day CA and verified the certificate name. There was neither a global TLS exception nor an entry in the system trust store.
On narrow screens, scroll the table sideways.
| Configuration and response | Observed HSTS fields | What the case shows |
|---|---|---|
Effective initial rule with always: HTTPS 200, 301, 404 and 500 | One max-age=300 in each response. | The generated error responses also receive the header. |
The same rule without always: 404 and 500 | No HSTS field; 200 and 301 still carry it. | Status exclusions and inheritance are different causes. |
/assets/ with its own X-Content-Type-Options rule under standard inheritance | No HSTS field despite always at the parent level. | The separate header rule prevents inheritance. |
HSTS explicitly repeated in /assets/ | One max-age=300. | The corrected child context shown contains both required rules. |
add_header_inherit merge with its own rule for a different header | One max-age=300. | The supported build combines the levels. |
merge plus a repeated HSTS rule | Two max-age=300 fields. | Blindly combining both remedies creates duplicates. |
| Upstream 200 and 500 with its own HSTS; Nginx adds its rule | max-age=600 from the upstream and max-age=300 from Nginx. | add_header does not replace the forwarded header. |
The same proxy responses with proxy_hide_header and an effective local rule | Only max-age=300. | These responses show the deliberately assigned Nginx responsibility. |
| Our HTTP redirect | 301 to the intended HTTPS target; no HSTS field. | An HTTP redirect must be tested separately from the HTTPS policy. |
The measurement covered the HTTP response, not a browser learning an HSTS policy. In particular, a loopback IP is not suitable evidence of a browser HSTS entry. Independent CDN error pages, real certificate renewal and your specific application also fall outside this check. Transfer the appropriate rule and test method, rather than treating it as a claimed overall success.
Increase retention and decide on preload separately
After successful testing, you can increase the retention period gradually, for example from max-age=300 to max-age=604800 and later max-age=2592000. These mean 5 minutes, 1 week and 30 days. Observe the affected web services and wait at least the existing duration before the next increase. A later value such as max-age=31536000 binds browsers newly receiving it for 365 days. Deployment recommendations
Consider includeSubDomains only after a separate inventory check. It covers subordinate hosts, including internal web services. A header on www.example.com does not extend the policy to example.com or sibling hosts.
Preload is not a mandatory final step. hstspreload.org no longer recommends it generally. If you make a deliberate decision to use it, you need a valid certificate, HTTPS for all affected subdomains and an HSTS header on the base domain containing at least max-age=31536000, includeSubDomains and preload. If port 80 is open, HTTP must first redirect to HTTPS on the same host; a subsequent HTTPS redirect must itself carry the header. Submission and later distribution to browsers are separate from setting the header. Preload requirements and context
Withdrawal: today's response is not every browser's state
Editing the Nginx file alone does not change delivered responses. The changed configuration becomes active only after a successful reload; existing workers may still complete established connections using the old configuration. Browsers must receive a new header before their dynamic entry changes accordingly. A max-age=0 received over valid HTTPS can delete that entry. A parent's includeSubDomains policy or a remaining preload entry may still apply.
If the TLS connection fails, the browser cannot receive the deletion instruction over valid HTTPS. A missing header does not remove a stored policy either. Preload removal needs its own process and distribution to browsers; no universal deadline can be promised. RFC 6797 §8, preload removal
Prepare a traceable operational handover
Record which Nginx version and configuration locations were checked, which hosts and paths the matrix covers, and where a CDN or proxy generates its own responses. Add the measurement time, received headers, unresolved subdomains, certificate responsibility and the last working configuration.
Name a concrete trigger for the next check: for example, changes to location blocks, error pages, proxy rules or HSTS scope. A single header finding may be enough to investigate a discrepancy. It does not establish complete TLS security, protection of the entire website against attack or regulatory compliance.
Relevant next steps
The Web Security Check can examine publicly accessible configuration characteristics. Use its limited finding as a starting point for a concrete check; it does not replace a complete matrix of your response paths.
Frequently asked questions about HSTS with Nginx
Where should I set the HSTS header?
If Nginx is responsible for the HTTPS response reaching the browser, set it in the appropriate HTTPS server block and check its effect in child contexts. With a CDN or proxy in front, account for responses it generates independently too. The header is ignored over unencrypted HTTP.
Why do I need always?
always removes the status-code restriction on add_header. This allows a valid HTTPS error response to set or renew the policy. The parameter does not repair missing inheritance or change a response generated independently by a CDN.
Why is HSTS missing only on certain paths?
Under standard inheritance, a separate add_header rule in a location can displace the HSTS rule from the parent level. Error pages, internal redirects and responses from an intermediary can also use different rules. Compare the affected response paths instead of assuming every response matches the home page.
Do I have to restart Nginx?
A reload is the intended way to load a valid changed configuration. Check it first with nginx -t and use your service manager’s reload method. A full restart is not generally necessary for an ordinary HSTS header change.
Does HSTS work with Nginx as a reverse proxy?
Yes, provided the policy reaches the browser in a valid HTTPS response. Decide whether the application, Nginx or an intermediary owns the header, and avoid conflicting duplicates. HSTS does not establish that the internal proxy connection is encrypted.
Which TLS versions should I allow?
TLS 1.0 and 1.1 should not be used. Selecting TLS 1.2 and TLS 1.3 requires a suitable Nginx/TLS build and forms part of the transport configuration. HSTS does not make that choice, and one directive is not a complete TLS audit.
Sources
Consulted specifically for the German source on 12 September 2026:
- Nginx headers module:
add_header,always, standard inheritance andadd_header_inheritsince 1.29.3. - Nginx proxy module: forwarding response headers,
proxy_hide_headerand error handling. - Nginx SSL module, command-line parameters and reload behaviour.
- RFC 6797, November 2012: HSTS processing, host scope and stored policies.
- RFC 8996, March 2021, and RFC 9325, November 2022: TLS versions and recommendations for using TLS.
- HSTS Preload: deployment, submission, limitations and removal.
- curl manual: meaning of the retrieval options shown.
Primary-source review and isolated HTTP/TLS check on 29 September 2026. The original local check used official Nginx 1.30.5 source; an existing signature verification was reused and archive bytes were compared again. No system installation, preload submission, browser HSTS state or production CDN/origin path was tested or changed. Reload through systemd and the example public curl addresses are documented operator actions, not production commands executed here.