If you host a static site or public assets on Google Cloud Storage, HSTS sounds simple: send Strict-Transport-Security and force browsers onto HTTPS.
The catch: with GCS, whether you can actually do that depends entirely on how you serve content.
That’s the whole game.
Google Cloud Storage is great at object serving. It’s not great at acting like a full-featured web server with flexible response header control in every hosting mode. So if you want HSTS on a GCS-backed site, you need to pick the right architecture first.
The short version
Here’s the practical comparison:
| Approach | Can you set HSTS? | Good idea? | Notes |
|---|---|---|---|
GCS static website endpoint (storage.googleapis.com or website hosting endpoint) |
Usually no, not reliably for your own domain | No | Weak header control for browser security policy |
| GCS behind HTTPS Load Balancer | Yes | Yes | Best option for custom domains and HSTS |
| GCS behind CDN / proxy that injects headers | Yes | Usually | Works well if header injection is supported |
| Firebase Hosting in front of storage-backed assets | Yes | Yes for many static sites | Easier than raw GCS for header management |
If you only remember one thing, remember this: don’t rely on bare GCS website hosting if HSTS matters to you.
What HSTS needs
HSTS is just an HTTP response header:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Browsers only honor it when it’s delivered over HTTPS. That means:
- Your site must already work on HTTPS.
- You need reliable control over response headers.
- You should be confident every relevant subdomain is HTTPS-ready before using
includeSubDomains. - You should only preload if you’re really sure.
For developers, the real question isn’t “does GCS support HTTPS?” It’s “can I guarantee this header is sent on every HTTPS response for my domain?”
That’s a much stricter requirement.
Option 1: Native GCS static website hosting
Google Cloud Storage has built-in static website hosting. You point a bucket at a website config, map a domain, and serve files.
That sounds nice until you hit security headers.
Pros
- Very simple for basic public static hosting
- Cheap
- Fine for internal prototypes or low-risk content
- Good object storage semantics
Cons
- Poor control over response headers like HSTS
- Website endpoints historically focus on HTTP-style static hosting behavior, not modern browser security policy
- Custom domain HTTPS handling is not the strongest part of this setup
- Hard to enforce a clean “HTTPS only, always HSTS” model
If your plan is:
- create a bucket
- turn on static website hosting
- point DNS at it
- somehow add HSTS
…you’re probably going to be disappointed.
For a developer audience, I’ll say it plainly: raw GCS website hosting is the wrong layer for HSTS policy enforcement.
You can check what your current endpoint sends with a scanner like HeaderTest, but the bigger issue is architectural. If the serving layer doesn’t let you manage headers properly, scanning only confirms the bad news.
Option 2: GCS behind a Google Cloud HTTPS Load Balancer
This is the setup I’d recommend most of the time.
You keep GCS as the origin for static assets, but put Google Cloud’s external Application Load Balancer in front of it. Then you attach response headers at the load balancer layer.
Why this works
The load balancer is the actual HTTP edge. That’s where security headers belong anyway.
You get:
- HTTPS termination
- managed certificates
- redirects from HTTP to HTTPS
- response header injection
- a cleaner place to enforce browser-facing policy
Pros
- Proper HSTS support
- Better custom domain handling
- Centralized security policy
- Works well for production sites
- Lets GCS stay “dumb storage,” which is exactly what it should be
Cons
- More setup than basic bucket hosting
- More moving parts
- Slightly higher cost
- Requires understanding Google Cloud networking resources
Still, if you care about real-world security posture, this is the right tradeoff.
Example: setting HSTS at the edge
The exact implementation can vary depending on your load balancer generation and config style, but the goal is to inject a header like this:
Strict-Transport-Security: max-age=31536000; includeSubDomains
A conservative rollout often starts here:
Strict-Transport-Security: max-age=300
Then increase over time:
Strict-Transport-Security: max-age=86400
Strict-Transport-Security: max-age=31536000; includeSubDomains
That progression matters. I’ve seen teams jump straight to a one-year policy with includeSubDomains, then discover some forgotten admin subdomain still serves plain HTTP. That’s not a fun cleanup.
Google’s official docs for load balancer header actions and backend configuration are the docs to follow for implementation details:
- https://cloud.google.com/load-balancing/docs
- https://cloud.google.com/storage/docs/hosting-static-website
Option 3: GCS behind a CDN or reverse proxy
This is the “I want edge features without making GCS do things it was never meant to do” option.
If your CDN or proxy supports response header injection, you can add HSTS there.
Pros
- HSTS becomes easy
- Usually improves caching and performance
- Can add other headers too:
Content-Security-PolicyX-Content-Type-OptionsReferrer-Policy
- Good separation between storage and delivery
Cons
- Another vendor or service layer to manage
- Cache invalidation can get annoying
- Misconfigurations can hide origin problems
- You need to be sure HTTPS is enforced at the edge, not just “available”
This option is solid if your team already uses a CDN. If not, Google’s own HTTPS load balancer is often the cleaner choice in a GCP-native stack.
Option 4: Firebase Hosting for static sites using GCS assets
If your use case is really “host a website,” not “serve arbitrary objects,” Firebase Hosting is often a better fit than direct GCS website hosting.
Firebase Hosting gives you easier header config, HTTPS by default, and a deployment model that feels more like an actual web platform.
Pros
- Easy header configuration
- Easy HTTPS
- Good developer experience
- Better fit for frontend apps than raw bucket hosting
Cons
- Another product to learn
- Not always ideal if you need direct object-serving workflows
- May not fit every GCS-centric architecture
Example firebase.json:
{
"hosting": {
"public": "dist",
"headers": [
{
"source": "**",
"headers": [
{
"key": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains"
}
]
}
]
}
}
That’s dramatically nicer than trying to force raw object hosting into behaving like a security-aware web server.
Official docs:
Best choice by use case
If you already use GCS only for public files
Use GCS behind HTTPS Load Balancer.
That gives you HSTS without giving up GCS as origin storage.
If you’re building a normal static website
Use Firebase Hosting or HTTPS Load Balancer + GCS.
I’d avoid native GCS website hosting unless security headers don’t matter, which is rare on public internet properties.
If you already have a CDN
Inject HSTS at the CDN edge.
That’s perfectly reasonable as long as the configuration is explicit and tested.
HSTS policy recommendations
For most production sites:
Strict-Transport-Security: max-age=31536000; includeSubDomains
For cautious rollout:
Strict-Transport-Security: max-age=300
then:
Strict-Transport-Security: max-age=86400
then:
Strict-Transport-Security: max-age=31536000; includeSubDomains
I would not rush into preload unless you fully understand the consequences. Preload is sticky, and rollback is slow.
A lot of teams treat preload like a badge. It’s not a badge. It’s a commitment.
Common mistakes
1. Assuming HTTPS availability means HSTS support
Not the same thing.
You can have HTTPS and still have no practical way to set HSTS consistently.
2. Setting includeSubDomains too early
If blog.example.com is perfect but old-admin.example.com still has weird legacy HTTP behavior, you’ve just created pain for yourself.
3. Forgetting redirects
Your HTTP endpoint should redirect to HTTPS cleanly. HSTS only kicks in after the browser sees the header once over HTTPS.
4. Trying to solve this at the bucket level
GCS buckets are storage primitives. HSTS is a browser-facing delivery policy. Put it at the edge.
That mental model saves a lot of wasted time.
How I’d do it
If I were shipping a public static site on GCP today, I’d choose one of these:
- Best all-around: HTTPS Load Balancer in front of GCS, HSTS set at the load balancer
- Best developer ergonomics: Firebase Hosting
- Already on CDN: set HSTS at the CDN edge
I would not choose plain GCS static website hosting if HSTS is a requirement.
That setup is fine for simple file hosting. It’s not where I want to enforce modern browser security controls.
Verify the header
After deployment, test the actual public domain response:
curl -I https://example.com
You want to see:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Also check redirect behavior:
curl -I http://example.com
And run a browser-facing header scan with HeaderTest to confirm the edge is sending what you think it is.
Because with GCS setups, that’s the part that bites people: they configure one layer and forget another layer is actually serving the response.
If your stack is GCS-backed and you need HSTS, the answer is simple even if the implementation isn’t: use a real HTTP edge in front of storage. That’s the cleanest, safest, and least surprising way to do it.