Merge pull request #33742 from backstage/freben/host-discovery-baseurl-warnings

feat(backend-defaults): warn on localhost or invalid backend.baseUrl in HostDiscovery
This commit is contained in:
Fredrik Adelöw
2026-04-03 15:34:27 +02:00
committed by GitHub
3 changed files with 76 additions and 0 deletions
@@ -0,0 +1,5 @@
---
'@backstage/backend-defaults': patch
---
`HostDiscovery` now logs a warning when `backend.baseUrl` is set to a localhost address while `NODE_ENV` is `production`, and when `backend.baseUrl` is not a valid URL.
@@ -413,6 +413,56 @@ describe('HostDiscovery', () => {
);
});
describe('backend.baseUrl warnings', () => {
const env = process.env as Record<string, string>;
const originalNodeEnv = env.NODE_ENV;
afterEach(() => {
if (originalNodeEnv) {
env.NODE_ENV = originalNodeEnv;
} else {
delete env.NODE_ENV;
}
});
it('warns when backend.baseUrl is a localhost URL and NODE_ENV is production', () => {
env.NODE_ENV = 'production';
const logger = mockServices.logger.mock();
HostDiscovery.fromConfig(
new ConfigReader({
backend: {
baseUrl: 'http://localhost:7007',
listen: { port: 7007, host: 'localhost' },
},
}),
{ logger },
);
expect(logger.warn).toHaveBeenCalledWith(
`backend.baseUrl is set to a localhost URL and NODE_ENV is 'production'. This is likely a misconfiguration — localhost URLs are not reachable by other services in a deployed environment. Prefer setting it to a routable URL that can be resolved and reached both by your app and by other plugin deployments / services.`,
);
});
it('warns when backend.baseUrl is not a valid URL', () => {
const logger = mockServices.logger.mock();
HostDiscovery.fromConfig(
new ConfigReader({
backend: {
baseUrl: 'not-a-valid-url',
listen: { port: 7007, host: 'localhost' },
},
}),
{ logger },
);
expect(logger.warn).toHaveBeenCalledWith(
`backend.baseUrl config value 'not-a-valid-url' does not appear to be a valid URL.`,
);
});
});
it('only accepts SRV URLs in the internal target', async () => {
expect(() =>
HostDiscovery.fromConfig(
@@ -152,6 +152,27 @@ export class HostDiscovery implements DiscoveryService {
};
static fromConfig(config: RootConfigService, options?: HostDiscoveryOptions) {
// The getExternalBaseUrl implementation relies on the backend base URL
// being a valid, non-local URL that others will be able to route to.
const baseUrl = config.getString('backend.baseUrl');
try {
const { hostname } = new URL(baseUrl);
const isLocalhost =
hostname === 'localhost' ||
hostname === '127.0.0.1' ||
hostname === '::1' ||
hostname === '::';
if (isLocalhost && process.env.NODE_ENV === 'production') {
options?.logger?.warn(
`backend.baseUrl is set to a localhost URL and NODE_ENV is '${process.env.NODE_ENV}'. This is likely a misconfiguration — localhost URLs are not reachable by other services in a deployed environment. Prefer setting it to a routable URL that can be resolved and reached both by your app and by other plugin deployments / services.`,
);
}
} catch {
options?.logger?.warn(
`backend.baseUrl config value '${baseUrl}' does not appear to be a valid URL.`,
);
}
const discovery = new HostDiscovery(new SrvResolvers());
discovery.#updateResolvers(config, options?.defaultEndpoints);