If you host on Azure Static Web Apps, HSTS looks deceptively simple. You want one header:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Done, right?
Not quite.
Azure Static Web Apps is great for shipping frontend apps fast, but once you care about security headers, especially HSTS, you run into a design constraint: you do not get full control over the edge like you would with a custom reverse proxy, Nginx, or a tuned CDN setup.
So the real question is not “should I use HSTS?” For any production HTTPS site, yes, you probably should. The real question is: what is the least painful way to enforce HSTS on Azure Static Web Apps, and what tradeoffs come with each option?
Quick refresher: what HSTS actually does
HSTS tells browsers: “for this domain, always use HTTPS for a period of time.”
Example:
Strict-Transport-Security: max-age=31536000; includeSubDomains
That means:
max-age=31536000: remember this rule for 1 yearincludeSubDomains: apply it to subdomains toopreload: ask browsers to hardcode your domain into preload lists
HSTS protects against protocol downgrade and cookie leakage over HTTP. It is one of those headers that gives solid security value with almost no runtime cost.
The catch is that HSTS is sticky. If you set it wrong, browsers keep enforcing it until max-age expires. If you add includeSubDomains and one forgotten subdomain does not support HTTPS, you just created a problem for yourself.
The Azure Static Web Apps reality
For Azure Static Web Apps, your practical HSTS options usually fall into three buckets:
- Set HSTS directly in
staticwebapp.config.json - Put Azure Front Door in front and manage HSTS there
- Use another reverse proxy or CDN layer in front of Static Web Apps
If your goal is “simple, native, and good enough,” option 1 is where I would start.
Option 1: HSTS in staticwebapp.config.json
Azure Static Web Apps supports custom response headers in route rules. That gives you a native way to return Strict-Transport-Security without adding extra infrastructure.
Example:
{
"globalHeaders": {
"Strict-Transport-Security": "max-age=31536000; includeSubDomains"
}
}
Or with routes:
{
"routes": [
{
"route": "/*",
"headers": {
"Strict-Transport-Security": "max-age=31536000; includeSubDomains"
}
}
]
}
If you can use globalHeaders, I prefer that. Less room for mistakes.
Official docs for config behavior live here:
Pros
- Native to Azure Static Web Apps
- No extra cost
- No extra hop or infrastructure
- Easy to version in Git
- Works well for straightforward frontend deployments
Cons
- Header control is not as flexible as a dedicated edge layer
- If you need more advanced traffic policies, this will feel limiting
- Rollback still has HSTS’s normal browser caching problem
- You need to be careful with environment differences, especially preview environments and custom domains
When I would choose this
I would use this for most small to medium sites on Azure Static Web Apps, especially if:
- you have one production custom domain
- all subdomains are already HTTPS-only, or you are not using
includeSubDomains - you do not need complex edge logic
Safe rollout advice
Do not jump straight to a preload-style policy unless you are absolutely sure.
Start with something conservative:
{
"globalHeaders": {
"Strict-Transport-Security": "max-age=300"
}
}
Then move to:
{
"globalHeaders": {
"Strict-Transport-Security": "max-age=86400"
}
}
Then:
{
"globalHeaders": {
"Strict-Transport-Security": "max-age=31536000; includeSubDomains"
}
}
And only consider preload if you actually meet preload requirements and want that level of commitment.
Option 2: Put Azure Front Door in front
If you want stronger edge control, Azure Front Door is the cleanest Azure-native upgrade path. Static Web Apps stays as the origin, and Front Door becomes the public entry point.
That lets you centralize headers, redirects, WAF policy, caching behavior, and custom domain handling.
Official docs:
Pros
- Much better control at the edge
- Easier to standardize headers across multiple apps
- Pairs nicely with WAF and redirect policy
- Good fit for organizations already using Front Door
- Lets you keep security policy separate from app code
Cons
- More cost
- More moving parts
- More configuration overhead
- More places to misconfigure hostnames, TLS, and caching
- Can be overkill for a simple marketing site or SPA
When I would choose this
I would pick Front Door when:
- multiple apps need the same security header policy
- the site is business-critical
- you already need WAF, custom routing, bot mitigation, or advanced caching
- your platform team wants centralized control instead of per-repo config
My opinionated take
If your team already knows Azure networking, Front Door is a solid answer. If not, adding it solely for HSTS is usually too much ceremony. I have seen teams spend way more time debugging edge behavior than the header was worth.
Option 3: Another proxy/CDN in front
You can also place another reverse proxy or CDN in front of Azure Static Web Apps and inject HSTS there. This could be useful if your company already standardizes on a specific edge provider.
Pros
- Potentially the most flexible option
- Can unify security policy across non-Azure workloads
- Lets you add HSTS even if app platform controls are awkward
Cons
- Extra cost and operational burden
- More DNS and TLS complexity
- Troubleshooting becomes less fun very quickly
- You are now depending on two platforms for one website path
When I would choose this
Only when the edge layer already exists for other reasons. I would not introduce a new CDN or proxy just to set HSTS for one Static Web App.
Comparison table
Here is the practical breakdown:
| Option | Best for | Pros | Cons |
|---|---|---|---|
staticwebapp.config.json |
Most single-app deployments | Simple, cheap, Git-managed, native | Limited edge features |
| Azure Front Door | Enterprise or multi-app setups | Centralized control, WAF, richer edge policy | Cost, complexity |
| External proxy/CDN | Existing standardized edge layer | Maximum flexibility | Operational overhead |
My default recommendation:
- Use
staticwebapp.config.jsonfirst - Use Front Door when you already need edge features
- Avoid extra proxy layers unless they already exist
What about preload?
A lot of developers see preload and think “more secure, so I should add it.”
Slow down.
A preload-ready header looks like this:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
That is a strong commitment. To safely use it, you need confidence that:
- your apex domain always supports HTTPS
wwwalways supports HTTPS if relevant- all subdomains support HTTPS because of
includeSubDomains - you are not going to need an HTTP-only subdomain later
For many teams, includeSubDomains is already the risky part. preload raises the stakes.
My advice: if you manage a clean, tightly controlled domain portfolio, preload can be great. If your organization has random legacy subdomains from 2017 that nobody owns, do not touch it yet.
Recommended config patterns
Conservative production setup
{
"globalHeaders": {
"Strict-Transport-Security": "max-age=86400"
}
}
Good for first rollout.
Strong production setup
{
"globalHeaders": {
"Strict-Transport-Security": "max-age=31536000; includeSubDomains"
}
}
Good when you are confident all relevant subdomains are HTTPS-only.
Preload-oriented setup
{
"globalHeaders": {
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload"
}
}
Use only when you have done the domain hygiene work.
How to verify HSTS is actually being sent
After deployment, check the response headers in your browser dev tools or with curl:
curl -I https://yourdomain.com
You should see:
Strict-Transport-Security: max-age=31536000; includeSubDomains
You can also run a quick scan with HeaderTest to confirm HSTS and other headers are present.
If you are using a proxy in front, verify the header on the public hostname, not the internal Azure hostname.
Common mistakes
Setting HSTS on a domain before HTTPS is fully ready
This is the classic self-own. Make sure certificates, redirects, and custom domain bindings are correct first.
Using includeSubDomains too early
If blog.example.com, old-api.example.com, or status.example.com is not HTTPS-clean, browsers will enforce HSTS there too.
Assuming preview environments should use the same policy
Be careful with non-production hostnames. HSTS on temporary or preview domains can create confusion during testing.
Forgetting that browsers cache HSTS
Removing the header does not instantly undo the policy for users who already received it.
My recommendation
For Azure Static Web Apps, the best default is:
- configure HSTS directly in
staticwebapp.config.json - start with a small
max-age - increase gradually
- add
includeSubDomainsonly after checking your domain estate - treat
preloadas a deliberate, high-confidence decision
If your app already sits behind Azure Front Door, then manage HSTS there and keep edge policy centralized. That is cleaner operationally.
But for most developer teams, native config wins. Fewer moving parts, fewer surprises, and one less “platform architecture” meeting nobody wanted.
If you want the smallest useful starting point, use this:
{
"globalHeaders": {
"Strict-Transport-Security": "max-age=300"
}
}
Then verify, wait, and scale it up.
That is the right kind of boring security change.