The dashboard opens fine in the browser on the laptop, and the same spreadsheet script fails with an address-unavailable error. Toggling the VPN changes nothing, because the request never leaves from the laptop at all. Apps Script executes on Google's infrastructure and opens its own connection from there. That single fact decides the whole design.

Separate two questions before changing anything: which machine sends the request, and which credential the receiver accepts.

The request never starts on your laptop

A browser tab sends from the laptop, through the VPN adapter, the internal DNS suffix and into the private subnet. UrlFetchApp sends from a Google-managed host, so it sees Google's resolvers, Google's egress addresses and none of your split-tunnel routes. The two paths share a spreadsheet document, not a network location. In the application's access log, the script's attempt appears as a Google address rather than a VPN pool address, which is the fastest confirmation you will get.

That is why local fixes do nothing. Pointing the laptop at another resolver, restarting the VPN client or adding a hosts entry changes the browser's path and leaves the script's path exactly as it was.

What that rules out

An internal-only endpoint is unreachable from Apps Script, and IP allowlisting is a weak substitute for access control in this position. Google's egress ranges are broad, shared with other customers and impossible to tie to one script. The UrlFetchApp reference states that requests originate from Google's IP pool, which is the authoritative version of that claim. Opening the internal app to those ranges admits traffic you cannot attribute.

Browser sessions behave the same way. A cookie or an SSO session in the laptop's browser does not travel with the request, which is a fresh HTTPS client with no session state. Anything that depends on a browser SSO redirect ends at that redirect instead of at your data.

Two workable shapes

The first shape gives the script an approved API. Publish the internal service behind a gateway that authenticates the caller, then have the script present a credential that gateway accepts, such as a service account token. In Apps Script that means an OAuth2 library or ScriptApp.getOAuthToken(), plus a caller identity allowlist on the API side rather than an IP allowlist.

The second shape reverses the direction. An internal job queries the private system and publishes a controlled result to a Sheet, a Cloud Storage object or a BigQuery table that the script reads. A Cloud Run job, an on-premises scheduler or an existing ETL step can all be that job, and none of them needs to accept an inbound connection. The spreadsheet then consumes a dataset instead of an interface.

Which shape wins depends on freshness, field-level control, and who has to be able to audit the reader.

What the published result has to contain

A reverse-direction feed needs an explicit contract: the fields it carries, the refresh interval, the reader group, and what happens when the internal job fails. Without those four, the spreadsheet quietly shows yesterday's numbers. Add a generated timestamp column and a row count so a stale or partial publish is visible in the sheet itself, without anyone opening a log.

The API direction needs a contract too: which credential the gateway accepts, which scopes the token carries, and which caller identities are allowed. A 200 response that contains a sign-in page is not a successful call, so check the body and not just the status code.

Read the error from the right side

Apps Script reports an unreachable host as an address-unavailable exception, which is not the browser's connection-refused message. A 403 from the gateway means the request arrived and a credential or policy was rejected. An HTML sign-in page means the request reached an interactive layer that a script cannot complete. Three different failures with three different owners.

Keep the script's log beside the browser's result instead of replacing one with the other. The remaining question is then an ordinary integration decision: give that environment the access it needs, or deliver it a controlled result.

Mikanda mikatakata