Skip to content

Commit 3e3cb78

Browse files
committed
fix: prefer https for public swagger urls
1 parent da78f7e commit 3e3cb78

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

src/docs/swagger.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,20 @@ const getForwardedHeaderValue = (
2525
return value.split(",")[0]?.trim() || undefined;
2626
};
2727

28+
const isLocalHost = (host: string): boolean => {
29+
const normalizedHost = host.toLowerCase();
30+
31+
return (
32+
normalizedHost.startsWith("localhost") ||
33+
normalizedHost.startsWith("127.0.0.1") ||
34+
normalizedHost.startsWith("[::1]")
35+
);
36+
};
37+
2838
export const resolveSwaggerBaseUrl = (
2939
request: SwaggerRequestLike,
3040
): string => {
31-
const protocol =
41+
const requestedProtocol =
3242
getForwardedHeaderValue(request, "x-forwarded-proto") ?? request.protocol;
3343
const host =
3444
getForwardedHeaderValue(request, "x-forwarded-host") ?? request.get("host");
@@ -37,6 +47,14 @@ export const resolveSwaggerBaseUrl = (
3747
return LOCAL_SWAGGER_SERVER.url;
3848
}
3949

50+
// Public proxy platforms can forward traffic to the container over plain HTTP
51+
// even when the external URL is HTTPS. Prefer HTTPS for non-local hosts so
52+
// Swagger "Try it out" targets the public origin instead of an internal hop.
53+
const protocol =
54+
requestedProtocol === "http" && !isLocalHost(host)
55+
? "https"
56+
: requestedProtocol;
57+
4058
return `${protocol}://${host}`;
4159
};
4260

tests/contracts/swagger.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ describe("swagger spec", () => {
6666
expect(baseUrl).toBe("https://auth-api-production-a97b.up.railway.app");
6767
});
6868

69+
it("prefers https for non-local public hosts when the proxy reports http", () => {
70+
const baseUrl = resolveSwaggerBaseUrl({
71+
protocol: "http",
72+
get(header: string) {
73+
return header === "host"
74+
? "auth-api-production-a97b.up.railway.app"
75+
: undefined;
76+
},
77+
});
78+
79+
expect(baseUrl).toBe("https://auth-api-production-a97b.up.railway.app");
80+
});
81+
6982
it("falls back to the local server when the host header is unavailable", () => {
7083
const baseUrl = resolveSwaggerBaseUrl({
7184
protocol: "https",

0 commit comments

Comments
 (0)