Skip to content

Web frameworks: hosting:channel:deploy creates broken preview channels when the SSR function lacks public invoker IAM #10631

@dxdc

Description

@dxdc

[REQUIRED] Environment info

firebase-tools: 15.19.1

Platform: macOS

[REQUIRED] Test case

A minimal Next.js app deployed through the Hosting web frameworks integration
("source" + "frameworksBackend" in firebase.json), where the generated
SSR function's Cloud Run service is missing the public invoker IAM binding
(allUsers with roles/run.invoker).

Note: the app needs at least one page using next/image (or some other
reason for a backend, like a dynamic route). A fully static app never gets an
SSR function created, so there is nothing to reproduce against. Image
optimization is the easiest trigger and also what surfaces the failure on
otherwise prerendered pages.

We hit this state in production: the IAM policy on our ssr<site> service
was completely empty (just an etag). We don't know exactly how it got that
way. The repro below sidesteps that question by
removing the binding manually, which puts the service in the same state.

// firebase.json
{
  "hosting": {
    "source": ".",
    "frameworksBackend": { "region": "us-central1" }
  }
}

[REQUIRED] Steps to reproduce

  1. npx create-next-app@latest repro and add a page that renders a
    statically imported image with next/image. This is required, otherwise
    no SSR function is created and the deploy is fully static.

  2. firebase init hosting (web frameworks), then deploy live with
    firebase deploy --only hosting. Everything works.

  3. Put the service in the broken state:

    gcloud run services remove-iam-policy-binding ssr<site> \
      --member=allUsers --role=roles/run.invoker --region us-central1
  4. Deploy a preview channel:

    firebase hosting:channel:deploy preview --only <site>

    The command succeeds and prints the channel URL.

  5. Open the channel URL and load a page with an image.

[REQUIRED] Expected behavior

The channel deploy should check that the rewrite target is publicly
invokable, and either re-assert the invoker binding (live frameworks deploys
do this when they redeploy the function) or fail loudly. Even a one-line
warning ("the ssr function is not publicly invokable, dynamic routes on this
channel will return 403") would save a lot of debugging time.

[REQUIRED] Actual behavior

The channel deploy reports success, but the channel is broken for all
dynamic content:

  • Prerendered pages load fine, since they come from the Hosting CDN.

  • Anything that has to invoke the SSR function, including /_next/image,
    returns Google's generic 403 page ("Error: Forbidden. Your client does not
    have permission to get URL ...") with nothing in the function logs. The
    request is rejected by IAM before the container runs, so there is no
    invocation to log. For example:

    https://<site>--preview-<hash>.web.app/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F1.479aff49.jpg&w=1080&q=75
    -> 403 Forbidden
    https://<site>--preview-<hash>.web.app/about
    -> 200 (prerendered, served from CDN)
    
  • Meanwhile the live channel looks healthy the whole time, because the
    popular _next/image URLs are already in the Hosting CDN cache. That makes
    this look like a preview-channel-specific problem and makes it very hard to
    search for.

What finally surfaced it:

$ gcloud run services get-iam-policy ssr<site> --region us-central1
etag: ACAB

That's an empty policy, no allUsers binding at all. Restoring it fixed every
channel immediately, no redeploy needed:

gcloud run services add-iam-policy-binding ssr<site> \
  --member=allUsers --role=roles/run.invoker --region us-central1

Running the channel deploy with --debug shows no IAM-related warning or
error anywhere, which is the core of the issue. Happy to attach the full
debug logs if that helps.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions