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:

  1. Your site must already work on HTTPS.
  2. You need reliable control over response headers.
  3. You should be confident every relevant subdomain is HTTPS-ready before using includeSubDomains.
  4. 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:

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-Policy
    • X-Content-Type-Options
    • Referrer-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:

  1. Best all-around: HTTPS Load Balancer in front of GCS, HSTS set at the load balancer
  2. Best developer ergonomics: Firebase Hosting
  3. 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.