Skip to content

Commit 618e840

Browse files
committed
Fix Studio 404 behind reverse proxy via ForwardedHeaders + Studio:Host override
1 parent 53a9dc8 commit 618e840

2 files changed

Lines changed: 21 additions & 0 deletions

File tree

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ ENV KESTREL__ENDPOINTS__REST__PROTOCOLS=Http1AndHttp2
4848
ENV KESTREL__ENDPOINTS__STUDIO__URL=http://*:2628
4949
ENV KESTREL__ENDPOINTS__STUDIO__PROTOCOLS=Http1AndHttp2
5050
ENV STUDIO__ENABLED=false
51+
# When Studio is accessed via a reverse proxy (e.g. Plesk / nginx), set this to
52+
# the public hostname so RequireHost() matches the proxied Host header.
53+
# Example: -e STUDIO__HOST=studio.example.com
54+
# Leave unset for direct port access (http://server:2628).
55+
ENV STUDIO__HOST=
5156

5257
# Data paths — mount a volume at /data to persist the database
5358
ENV BLITESERVER__DATABASEPATH=/data/blite.db

src/BLite.Server/Program.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using BLite.Server.Telemetry;
1717
using BLite.Server.Transactions;
1818
using Microsoft.AspNetCore.Authentication;
19+
using Microsoft.AspNetCore.HttpOverrides;
1920
using Microsoft.OpenApi;
2021
using OpenTelemetry.Metrics;
2122
using OpenTelemetry.Resources;
@@ -227,6 +228,12 @@ static string ResolveAppPath(string path) =>
227228

228229
var restHostFilter = HostFilter(app.Configuration["Kestrel:Endpoints:Rest:Url"]);
229230
var studioHostFilter = HostFilter(app.Configuration["Kestrel:Endpoints:Studio:Url"]);
231+
// If Studio is accessed via a reverse proxy, the Host header will be the proxy
232+
// domain (e.g. studio.example.com) rather than *:2628. Studio:Host overrides
233+
// the port-based filter so RequireHost() matches the proxied hostname instead.
234+
var studioHostOverride = app.Configuration.GetValue<string>("Studio:Host");
235+
if (!string.IsNullOrWhiteSpace(studioHostOverride))
236+
studioHostFilter = studioHostOverride;
230237
// Apply filters only when the two endpoints have actually different ports.
231238
bool portsAreSeparate = restHostFilter is not null
232239
&& studioHostFilter is not null
@@ -263,6 +270,15 @@ static string ResolveAppPath(string path) =>
263270
if (app.Environment.IsDevelopment())
264271
app.MapGrpcReflectionService();
265272

273+
// Forward headers from reverse proxy (X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host).
274+
// Required for correct host matching (RequireHost) and HTTPS detection when behind nginx/Plesk.
275+
app.UseForwardedHeaders(new ForwardedHeadersOptions
276+
{
277+
ForwardedHeaders = ForwardedHeaders.XForwardedFor
278+
| ForwardedHeaders.XForwardedProto
279+
| ForwardedHeaders.XForwardedHost
280+
});
281+
266282
// Static files — must precede endpoint mapping (only needed for Studio CSS/JS)
267283
if (studioEnabled)
268284
app.UseStaticFiles();

0 commit comments

Comments
 (0)