Restricting Backstage URL readers with backend.reading.allow

Backstage URL readers fetch remote content on the server, so an unrestricted reader can be pointed at internal services or cloud metadata. This page explains how to limit them with backend.reading.allow, what the allow list does not cover, and what to confirm before rollout.

On this page

Why Backstage URL readers need an allow list

URL readers in Backstage fetch content from the backend, not from the user's browser. When the portal loads a catalog location, a TechDocs source, a software template or a $text reference from a URL, the backend makes that request with its own network position. If someone who can add or edit a reference points it at an internal service or a cloud metadata address, the backend will request that address and may return the response to the attacker. This is server-side request forgery, and the allow list is the control that limits which hosts the backend will contact at all.

You need the allow list on any production instance where users can register or edit URL references, which includes catalog registration, template repositories and documentation sources. Read the scope with care: an allow list constrains where readers fetch from, but it does not stop an authorized user from registering an approved host with a harmful path, and it does not replace permission policies on who can create those entries. Confirm which entity kinds and plugins in your deployment accept URL locations, since each of them uses the same reader path. The configuration is expressed through backend.reading.allow in app-config.yaml, and its exact syntax should be checked against the Backstage authentication and configuration documentation for your release, Backstage authentication documentation.

This page covers only the reader configuration and the checks that confirm it works. It does not cover the wider threat model, the permissions that govern entity registration, or network egress controls at the cluster level, which are handled in the sibling topics. Treat the allow list as one layer. Before you rely on it, ask your platform team to confirm which hosts the backend can reach from its network segment, so that a permitted host cannot be used as a relay to an internal address.

Backstage URL reader allow list options and when each fits

This table compares the ways to scope Backstage URL readers, from the default behavior to host-level and path-level allow lists. Read each row against the hosts your catalog and TechDocs actually fetch from, and treat the "what it leaves open" column as the work that remains after the option is in place.

OptionWhen it fitsWhat it leaves open
Keep the default reader behaviorOnly in a lab or proof of concept with no production credentials and no internal network reachable from the backendAny URL a catalog author or template can reference may be fetched by the backend, including internal services and cloud metadata endpoints
Allow a single source control hostA portal that only reads catalog descriptors and templates from one Git host, such as one self-hosted GitLab or GitHub Enterprise instanceAny repository or path on that host remains readable, and anyone who can write to a repo on that host can point the reader at internal endpoints it serves
Allow a documentation hostTechDocs sources or external reference docs served from one known site or internal wikiPages on that host that redirect or proxy to other addresses may still lead the backend to unintended targets; confirm redirect handling for your reader version
Allow an internal artifact or object store hostTemplates or catalog files published to a specific internal bucket or artifact server that the portal must fetchThe store may expose other buckets or paths to the reader; scope access credentials and bucket policies separately from the host entry
Narrow to paths where the configuration supports itWhen one host serves both public and internal content, and your Backstage version supports path-level restriction in the reader configurationPath rules depend on the exact configuration shape for your version; confirm the supported key names and value format in the Backstage configuration reference before relying on them
Combine host allow list with catalog registration controlsAny production portal where authors can register locations, since the reader is only one of the paths into the catalogThe allow list does not stop an author from registering a permitted URL that serves unexpected content; pair it with restrictions on who can register entities
Verify blocking of metadata and private address rangesAll production deployments, to confirm that link-local and private addresses are not reachable through permitted hostsVerification is only as good as the test cases you run; repeat tests after each upgrade because reader behavior can change between releases

Setting up the allow list step by step

This sequence turns an open-ended URL reader configuration into a deliberate allow list, so the backend fetches only from hosts your organization has named and rejects everything else. Each step leaves a check behind that confirms the change took effect in every environment.

  1. Inventory every URL the portal reads

    Ask the platform owner to list each place Backstage fetches content by URL: catalog locations, TechDocs sources, software template sources, $text references in configuration, and integration endpoints for source control and identity. Include entries added by plugins, since they can read URLs too. Record the owner of each source. The output is a single inventory that the security and platform teams both sign off on, and it is complete when every catalog location and template source appears on it.

  2. List the hosts each source requires

    Reduce each inventory entry to the hostnames and, where relevant, the paths or repositories it needs. Remove sources that no longer serve a purpose, and flag any that point to internal addresses or cloud metadata ranges for separate review. Confirm with each owner which hosts they expect, because a missing host will break catalog refresh later. The check is a host list where every line traces back to an entry in the inventory.

  3. Write the allow list for each environment

    In the app configuration for each environment, set backend.reading.allow with one entry per approved host, using the host and optional path form described in the Backstage documentation for URL readers. Keep development, staging and production lists separate so that a development host never reaches production by accident. Store the files in version control so changes go through review. The check is a diff in review that matches the host list from the previous step exactly.

  4. Restart the backend and confirm it loads

    Restart the Backstage backend in each environment so the new configuration takes effect. Watch the startup logs for configuration errors and confirm the backend reports that it started without validation failures. A broken entry, such as a malformed host, often shows up here first. The check is a clean startup log in every environment and a backend that answers its health endpoint.

  5. Test an allowed fetch

    Pick one catalog location or template on an approved host and trigger a refresh from the catalog or by waiting for the next scheduled ingestion. Confirm the entity updates and that the backend log shows a successful read. This proves the allow list did not block legitimate traffic. The check is an updated entity timestamp and a successful fetch entry for the approved host.

  6. Test a blocked fetch

    Point a test location at a host that is not on the list, such as an internal address or a cloud metadata endpoint, in a non-production environment. Trigger the read and confirm the backend rejects it. Check the backend logs for an error that names the blocked URL or host. The check is a rejected read in the logs with no response body from the blocked target, and the test location is then removed.

  7. Confirm existing entries still refresh

    Review the catalog for entities that were ingested before the change and verify that their locations still refresh on schedule. Any entity that stops updating points to a host missing from the list or a path that the allow entry does not cover. Resolve each one through the inventory and the change review, not by widening the list without a recorded owner. The check is a catalog with no new ingestion errors for approved sources after one full refresh cycle.

Risks an allow list does not close alone

A host allow list decides which names the backend may read, but it does not control where those names resolve, where requests are redirected, or what the configuration says over time. The cards below cover the adjacent risks that need controls outside the Backstage configuration file, each with the team that should own it.

Cloud metadata endpoints

A backend that can reach link-local addresses may request the cloud instance metadata service and receive temporary credentials for the host it runs on. An allowed host name does not prevent this if the request path leads to a metadata address. Ask the platform team to confirm that the Backstage workload cannot reach metadata addresses, using egress rules at the network layer and, where the provider supports it, metadata service settings that require session tokens or disable access for pods. Owner: platform or cloud team.

Redirects to blocked hosts

An allowed host may answer with an HTTP redirect to an address outside the list, and the reader must not follow it into an internal service. Confirm in the Backstage version you run how redirects are handled, and test with a controlled allowed host that redirects to an internal address. Where the reader cannot be confirmed to stop redirects, the network egress rules remain the control that holds. Owner: application team with security review.

Names that resolve internally

An allowed hostname can resolve through DNS to a private, loopback or link-local address, so the request reaches an internal system even though the name looked approved. Check whether your resolver returns internal records for public-looking names, and whether egress rules block private ranges regardless of the name used. Ask your network team to confirm this behavior for the Backstage subnet rather than assuming the host list covers it. Owner: network team.

Secrets pulled through text references

A $text reference in catalog data fetches content from a URL during ingestion, so a reference to an allowed host can still expose whatever that response contains, including content an author should never have been able to place into the catalog. Review which locations use $text and which ones point at private repositories, and keep credentials out of the fetched files and out of URL query strings. Owner: catalog owners with the security team.

Unreviewed allow list changes

The value of backend.reading.allow only holds if changes to it are reviewed like any other access decision. A single added host can open a path to an internal service, and a wildcard or broad entry may go unnoticed in a long app-config file. Require pull request review for changes to the file, keep the allow list in version control, and alert on edits in the deployed configuration. Owner: Backstage platform owner.

Why URL readers bypass catalog permissions

A URL reader fetches content using the Backend's own network position, so the permission model that decides who can view or register an entity does not decide what the server is able to reach. When a user registers a location that points at a URL, the request for that URL leaves the Backstage backend, not the user's browser. Any host the backend can route to, including internal services, administrative consoles and the cloud instance metadata endpoint, becomes reachable by proxy through that single registration. This is the core reason the risk sits in server-side request forgery rather than in catalog access control.

The consequence for design is a split of responsibilities. Catalog permissions limit who can add or change a reference, which matters for integrity and accountability. The backend.reading.allow list and the network policy around the backend limit where the server can go, which matters for exposure. Treat these as two independent controls and assign each an owner. A reviewer who only checks that registration is restricted to a trusted group has not checked whether that group's requests can reach the metadata service from inside your cloud account.

Before you rely on either control, check how your version of Backstage applies permission policies to entity registration and to the reader path, because the interaction is version-sensitive. Confirm this against the release notes and the permission documentation for the exact version you run, and test it with a non-privileged account that submits a location pointing at an internal address. Record the result as evidence. The Backstage authentication documentation is a useful starting point for how identities reach the catalog, but the reader behavior should be verified in your own deployment.

How the allow list differs by environment

Development environments fetch examples, sample catalogs and test fixtures from many hosts while engineers build templates and plugins. A broader host list keeps that work moving, but it should live in a local configuration file that never reaches the container image or the production deployment values. Treat the developer's list as a convenience that the production review process does not inherit.

  • Local file only: Keep the expanded backend.reading.allow entries in a local override file such as app-config.local.yaml and exclude that file from image builds and Helm values.
  • Named hosts: Add each test host by its full name and avoid wide wildcard ranges, so a developer's shortcut does not become a habit that staging copies later.
  • Internal addresses: Local testing often reaches localhost or private network addresses. Write down which of these are needed so they are not copied into staging or production.
  • Ownership: Individual engineers or the platform team maintain their own local changes. No approval queue applies here, but the list of added hosts should be easy to find in the team's documentation.
  • Build-time check: Add a pipeline step that fails when a production build references the local override file or contains development host names.

Before promoting any template or plugin, check that the production image contains no development host entries and that the fetch targets it needs are listed in the staging configuration.

Questions teams ask before restricting URL readers

How much effort does restricting Backstage URL readers take?

The effort depends mostly on how many URL sources the portal reads, how many environments need their own allow list, and how many teams add url: references to catalog descriptors and templates. A portal that reads from one source control host in one environment is a small change to backend.reading.allow. A portal that pulls from several hosts, object storage buckets, documentation sites and internal wikis across development, staging and production takes longer, because each source needs its own host, path and network check and each environment needs its own verification. Ask any partner to break the quote down by these three drivers so you can see which one dominates.

Can our internal team restrict URL readers without a partner?

Yes, a capable internal team can make the configuration change, but the work touches three skill areas that often sit in different groups. Backstage configuration is handled by the platform team that owns app-config.yaml. Network and cloud administration is needed to confirm which hosts are reachable from the backend, to block metadata endpoints at the network layer, and to check egress rules. Security review is needed to test that a crafted reference to an internal address is refused. If one of these skills is missing, a partner can cover that gap, but the team should still own the final configuration and the test evidence.

What do we need to give a partner before work starts?

Prepare an inventory of every URL source the portal reads today, including the host, the path pattern, the team that owns it and whether it is public or internal. Add current network diagrams showing where the Backstage backend runs, which egress routes exist and where cloud metadata services are reachable. Provide read access to the app-config files for each environment and to the deployment pipelines that inject them, so the partner can see how configuration reaches production. Access to a non-production instance for testing is also needed. Missing inventory items are the most common reason a scope changes after kickoff.

Does restricting URL readers change existing catalog data?

Restricting readers changes which hosts the backend will fetch from, not the content of entities already in the catalog. Entities that point to hosts outside the new allow list will stop refreshing, and the catalog will show ingestion errors for them until the source is either added to the allow list or the reference is moved to an approved host. Existing entities are not deleted by the change, but you should check for stale or orphaned entries once the allow list is live. Plan a review of ingestion errors in each environment before you roll the change to production.

Why do URL readers need a separate review from permissions?

URL readers run on the backend and fetch content with the server's network position, so a permission check on the user who submitted a reference does not limit where the request can go. An allow list therefore has to be treated as its own control, reviewed alongside network egress rules. Teams that skip this review often find that a reference they believed was harmless can still reach an internal address. Include the allow list in your security review cycle rather than assuming existing role settings cover it.

What should a quote for this work include?

A quote should list the in-scope URL sources and environments, the configuration changes to backend.reading.allow, and the network changes the client must make or approve. It should state what testing is included, such as attempts to reach internal addresses and cloud metadata endpoints from a catalog reference, and what evidence is delivered, such as configuration files, test output and a change record. Ask whether ongoing reviews are included when new teams add URL references, and whether the quote assumes access to existing pipelines. Ask for the assumptions in writing, since they determine the final scope. DeployOpen agrees scope, deliverables and timelines in writing before work starts, and the quote follows from that written scope.

Who keeps the allow list correct after the first rollout?

The allow list needs an owner who approves new hosts before teams add references to them. In practice, the platform team owns the configuration, and each team that wants a new URL source submits a request with the host, path pattern and business reason. Review the list whenever a source changes address or a new integration is planned, and rerun the blocked-address tests after each Backstage upgrade. Ask any partner whether ongoing review and testing are part of a retainer, since DeployOpen offers ongoing support, upgrades and continuous testing as retainers.

Limits to confirm for your Backstage URL readers

URL reader allow-lists only protect what they are configured to see. The items below are the questions to settle with the Backstage version you run, your cloud provider and your network team before you rely on backend.reading.allow as a control. Each one should end with a written answer and a test.

Exact configuration shape for your version

Confirm the key names, the allow-list entry format and the default behavior in the Backstage version you run, because the reader configuration has changed across releases. Check the backend.reading.allow entries against the reading documentation for that exact release, not an older example. Record whether an empty or missing list means deny, allow or fall back to a default, and test that behavior on a staging instance. Keep the answer with your change record.

Plugins that call the URL reader

List every plugin and processor that fetches remote content through the URL reader, including catalog locations, TechDocs sources, scaffolder fetch actions and any community plugin. The allow-list applies to these callers, and a plugin that makes its own HTTP requests will bypass it. Ask each plugin owner whether it uses the shared reader or its own client, and record the answer in your plugin inventory.

Metadata service version and hop limit

Confirm with your cloud provider whether the instance metadata service requires session tokens and what hop limit applies to the nodes that run Backstage. A permissive setting lets a request that passes the allow-list still reach credentials through a proxy or container hop. Ask the platform team to show the current setting for each node pool and to document who can change it.

Egress enforcement outside the cluster

Determine which layer blocks traffic to link-local and internal ranges: Kubernetes network policies, a firewall, a proxy or the cloud security groups. The allow-list only filters what Backstage requests, so an egress policy at the network layer is the second control. Ask the network team to confirm the rules apply to the Backstage pods and that a denied destination fails with a logged event.

Redirects and DNS rebinding

Check whether the HTTP client in your deployment follows redirects, and whether a redirect can move a request from an allowed host to an internal address. Confirm how name resolution works in your cluster, since a host name that resolves to an internal address at request time can defeat a check made on the name alone. Test both cases with a host you control that redirects to a private address, and record the result.

Testing changes to the list

Decide how a change to the allow-list gets reviewed and tested before it reaches production. Keep a set of negative tests that attempt metadata endpoints, private ranges and redirect chains, and run them on every change. Assign an owner for approving entries and set a review cadence so that old hosts are removed when a source is retired.

How DeployOpen scopes and delivers URL reader hardening

DeployOpen treats URL reader hardening as a scoped piece of work on your own Backstage deployment, not a generic setting change. We start from the hosts your catalog and templates really need, then build, test and document the allow list so your team can operate and change it after handover.

How we scope

  • Inventory every URL reader location in your catalog, templates and $text references, with owners for each host
  • Agree in writing which hosts, schemes and paths are allowed, and which internal ranges and metadata endpoints must be blocked
  • Define the test cases, deliverables and timeline in a fixed-scope statement before any configuration changes begin
  • Confirm which environments (development, staging, production) and which Backstage version line the work covers

How we build

  • Write the backend.reading.allow configuration in your app-config files, with secrets kept out of the file
  • Test each allowed host and confirm that disallowed hosts, redirects and cloud metadata addresses are refused
  • Check that the scaffolder and catalog processors fail closed when a URL falls outside the list
  • Review which plugins and actions still read URLs outside the reader, since those calls bypass the allow list and their permissions

How we hand over

  • Document the allow list, its owners, the change request process and the reasons behind each entry
  • Provide test scripts that your team can rerun after upgrades or when a new host is requested
  • Walk your platform and security engineers through the configuration, the test results and the open risks
  • Record residual items, such as reader-bypassing plugins, with recommended owners and next steps

How we support

  • Engagements are fixed-scope, and ongoing support, upgrades and continuous testing are available as a retainer
  • Re-test the allow list when Backstage moves to a new release line or when your catalog adds new source hosts
  • Confirm current security advisories and backport status with the Backstage project before each change window
  • Quote follows from the written scope, so ask for the cost of initial hardening separately from any recurring retainer

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.