This guide provides scenario-based tests for ServiceControl instances behind an NGINX reverse proxy. Use this to verify:
- SSL/TLS termination at the reverse proxy
- Forwarded headers handling (
X-Forwarded-For,X-Forwarded-Proto,X-Forwarded-Host) - HTTP to HTTPS redirection
- HSTS (HTTP Strict Transport Security)
- WebSocket support (SignalR)
| Instance | Project Directory | Default Port | Hostname | Environment Variable Prefix |
|---|---|---|---|---|
| ServiceControl (Primary) | src\ServiceControl |
33333 | servicecontrol.localhost |
SERVICECONTROL_ |
| ServiceControl.Audit | src\ServiceControl.Audit |
44444 | servicecontrol-audit.localhost |
SERVICECONTROL_AUDIT_ |
| ServiceControl.Monitoring | src\ServiceControl.Monitoring |
33633 | servicecontrol-monitor.localhost |
MONITORING_ |
- Docker Desktop installed and running
- mkcert for generating local development certificates
- ServiceControl built locally (see main README for instructions)
- curl (included with Windows 10/11, Git Bash, or WSL)
To enable detailed logging for troubleshooting, set the LogLevel environment variable before starting each instance:
rem ServiceControl Primary
set SERVICECONTROL_LOGLEVEL=Debug
rem ServiceControl.Audit
set SERVICECONTROL_AUDIT_LOGLEVEL=Debug
rem ServiceControl.Monitoring
set MONITORING_LOGLEVEL=DebugValid log levels: Trace, Debug, Information (or Info), Warning (or Warn), Error, Critical (or Fatal), None (or Off)
Debug logs will show detailed request processing information including forwarded headers handling and HTTPS redirection.
Windows (using Chocolatey):
choco install mkcertWindows (using Scoop):
scoop install mkcertAfter installing, run mkcert -install to install the local CA in your system trust store.
Create a .local folder in the repository root (this folder is gitignored):
mkdir .local
mkdir .local\certsUse mkcert to generate trusted local development certificates:
mkcert -install
cd .local\certs
mkcert -cert-file local-platform.pem -key-file local-platform-key.pem servicecontrol.localhost servicecontrol-audit.localhost servicecontrol-monitor.localhost localhostCreate .local/compose.yml:
services:
reverse-proxy-servicecontrol:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs/local-platform.pem:/etc/nginx/certs/local.pem:ro
- ./certs/local-platform-key.pem:/etc/nginx/certs/local-key.pem:roCreate .local/nginx.conf:
events { worker_connections 1024; }
http {
# WebSocket support: set connection to 'upgrade' if Upgrade header present
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Shared SSL Settings
ssl_certificate /etc/nginx/certs/local.pem;
ssl_certificate_key /etc/nginx/certs/local-key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# ServiceControl (Primary) - HTTPS
server {
listen 443 ssl;
server_name servicecontrol.localhost;
location / {
proxy_pass http://host.docker.internal:33333;
# WebSocket Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Forwarded Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
# ServiceControl (Primary) - HTTP (for testing HTTP-to-HTTPS redirect)
server {
listen 80;
server_name servicecontrol.localhost;
location / {
proxy_pass http://host.docker.internal:33333;
# WebSocket Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Forwarded Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
# ServiceControl.Audit - HTTPS
server {
listen 443 ssl;
server_name servicecontrol-audit.localhost;
location / {
proxy_pass http://host.docker.internal:44444;
# WebSocket Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Forwarded Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
# ServiceControl.Audit - HTTP (for testing HTTP-to-HTTPS redirect)
server {
listen 80;
server_name servicecontrol-audit.localhost;
location / {
proxy_pass http://host.docker.internal:44444;
# WebSocket Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Forwarded Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
# ServiceControl.Monitoring - HTTPS
server {
listen 443 ssl;
server_name servicecontrol-monitor.localhost;
location / {
proxy_pass http://host.docker.internal:33633;
# WebSocket Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Forwarded Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
# ServiceControl.Monitoring - HTTP (for testing HTTP-to-HTTPS redirect)
server {
listen 80;
server_name servicecontrol-monitor.localhost;
location / {
proxy_pass http://host.docker.internal:33633;
# WebSocket Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Forwarded Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}
}Add the following entries to your hosts file (C:\Windows\System32\drivers\etc\hosts):
127.0.0.1 servicecontrol.localhost
127.0.0.1 servicecontrol-audit.localhost
127.0.0.1 servicecontrol-monitor.localhost
From the repository root:
docker compose -f .local/compose.yml up -dAfter completing the setup, your .local folder should look like:
.local/
├── compose.yml
├── nginx.conf
└── certs/
├── local-platform.pem
└── local-platform-key.pem
Important: ServiceControl must be running before testing. A 502 Bad Gateway error means NGINX cannot reach ServiceControl. Note: Use
TRUSTALLPROXIES=truefor local Docker testing. The NGINX container's IP address varies based on Docker's network configuration (e.g.,172.x.x.x), making it impractical to specify a fixedKNOWNPROXIESvalue.
Verify that HTTPS is working through the reverse proxy.
Clear environment variables and start ServiceControl:
set SERVICECONTROL_FORWARDEDHEADERS_ENABLED=
set SERVICECONTROL_FORWARDEDHEADERS_TRUSTALLPROXIES=
set SERVICECONTROL_HTTPS_REDIRECTHTTPTOHTTPS=
set SERVICECONTROL_HTTPS_PORT=
set SERVICECONTROL_HTTPS_ENABLEHSTS=
cd src\ServiceControl
dotnet run --no-launch-profileTest with curl:
curl -k -v https://servicecontrol.localhost/api 2>&1 | findstr /C:"HTTP/"Expected output:
< HTTP/1.1 200 OK
The request succeeds over HTTPS through the NGINX reverse proxy.
Verify that forwarded headers are being processed correctly.
Clear environment variables and start ServiceControl:
set SERVICECONTROL_FORWARDEDHEADERS_ENABLED=true
set SERVICECONTROL_FORWARDEDHEADERS_TRUSTALLPROXIES=true
set SERVICECONTROL_HTTPS_REDIRECTHTTPTOHTTPS=
set SERVICECONTROL_HTTPS_PORT=
set SERVICECONTROL_HTTPS_ENABLEHSTS=
cd src\ServiceControl
dotnet run --no-launch-profileTest with curl:
curl -k https://servicecontrol.localhost/debug/request-info | jsonExpected output:
{
"processed": {
"scheme": "https",
"host": "servicecontrol.localhost",
"remoteIpAddress": "172.x.x.x"
},
"rawHeaders": {
"xForwardedFor": "",
"xForwardedProto": "",
"xForwardedHost": ""
},
"configuration": {
"enabled": true,
"trustAllProxies": true,
"knownProxies": [],
"knownNetworks": []
}
}The key indicators that forwarded headers are working:
processed.schemeishttps(fromX-Forwarded-Proto)processed.hostisservicecontrol.localhost(fromX-Forwarded-Host)rawHeadersare empty because the middleware consumed them (trusted proxy)
Verify that HTTP requests are redirected to HTTPS.
Clear environment variables and start ServiceControl:
set SERVICECONTROL_FORWARDEDHEADERS_ENABLED=true
set SERVICECONTROL_FORWARDEDHEADERS_TRUSTALLPROXIES=true
set SERVICECONTROL_HTTPS_REDIRECTHTTPTOHTTPS=true
set SERVICECONTROL_HTTPS_PORT=443
set SERVICECONTROL_HTTPS_ENABLEHSTS=
cd src\ServiceControl
dotnet run --no-launch-profileTest with curl:
curl -v http://servicecontrol.localhost/api 2>&1 | findstr /i locationExpected output:
< Location: https://servicecontrol.localhost/api
HTTP requests are redirected to HTTPS with a 307 (Temporary Redirect) status.
Verify that the HSTS header is included in HTTPS responses.
Note: HSTS is disabled in Development environment. You must use
--no-launch-profileto prevent launchSettings.json from overriding it.
Clear environment variables and start ServiceControl:
set SERVICECONTROL_FORWARDEDHEADERS_ENABLED=true
set SERVICECONTROL_FORWARDEDHEADERS_TRUSTALLPROXIES=true
set SERVICECONTROL_HTTPS_REDIRECTHTTPTOHTTPS=
set SERVICECONTROL_HTTPS_PORT=
set SERVICECONTROL_HTTPS_ENABLEHSTS=true
cd src\ServiceControl
dotnet run --environment Production --no-launch-profileTest with curl:
curl -k -v https://servicecontrol.localhost/api 2>&1 | findstr /i strict-transport-securityExpected output:
< Strict-Transport-Security: max-age=31536000
The HSTS header is present with the default max-age of 1 year.
The scenarios above use ServiceControl (Primary). To test ServiceControl.Audit or ServiceControl.Monitoring:
- Use the appropriate environment variable prefix (see Configuration Reference below)
- Use the corresponding project directory and hostname
| Instance | Project Directory | Hostname | Env Var Prefix |
|---|---|---|---|
| ServiceControl (Primary) | src\ServiceControl |
servicecontrol.localhost |
SERVICECONTROL_ |
| ServiceControl.Audit | src\ServiceControl.Audit |
servicecontrol-audit.localhost |
SERVICECONTROL_AUDIT_ |
| ServiceControl.Monitoring | src\ServiceControl.Monitoring |
servicecontrol-monitor.localhost |
MONITORING_ |
| Environment Variable | Default | Description |
|---|---|---|
{PREFIX}_FORWARDEDHEADERS_ENABLED |
true |
Enable forwarded headers processing |
{PREFIX}_FORWARDEDHEADERS_TRUSTALLPROXIES |
true |
Trust all proxies |
{PREFIX}_FORWARDEDHEADERS_KNOWNPROXIES |
- | Comma-separated list of trusted proxy IPs |
{PREFIX}_FORWARDEDHEADERS_KNOWNNETWORKS |
- | Comma-separated list of trusted CIDR ranges |
{PREFIX}_HTTPS_REDIRECTHTTPTOHTTPS |
false |
Redirect HTTP to HTTPS |
{PREFIX}_HTTPS_PORT |
- | HTTPS port for redirect |
{PREFIX}_HTTPS_ENABLEHSTS |
false |
Enable HSTS |
{PREFIX}_HTTPS_HSTSMAXAGESECONDS |
31536000 |
HSTS max-age (1 year) |
{PREFIX}_HTTPS_HSTSINCLUDESUBDOMAINS |
false |
Include subdomains in HSTS |
Where {PREFIX} is:
SERVICECONTROLfor ServiceControl (Primary)SERVICECONTROL_AUDITfor ServiceControl.AuditMONITORINGfor ServiceControl.Monitoring
docker compose -f .local/compose.yml downAfter testing, clear the environment variables:
Command Prompt (cmd):
set SERVICECONTROL_FORWARDEDHEADERS_ENABLED=
set SERVICECONTROL_FORWARDEDHEADERS_TRUSTALLPROXIES=
set SERVICECONTROL_HTTPS_REDIRECTHTTPTOHTTPS=
set SERVICECONTROL_HTTPS_PORT=
set SERVICECONTROL_HTTPS_ENABLEHSTS=PowerShell:
$env:SERVICECONTROL_FORWARDEDHEADERS_ENABLED = $null
$env:SERVICECONTROL_FORWARDEDHEADERS_TRUSTALLPROXIES = $null
$env:SERVICECONTROL_HTTPS_REDIRECTHTTPTOHTTPS = $null
$env:SERVICECONTROL_HTTPS_PORT = $null
$env:SERVICECONTROL_HTTPS_ENABLEHSTS = $nullIf you no longer need the hostnames, remove these entries from your hosts file (C:\Windows\System32\drivers\etc\hosts):
127.0.0.1 servicecontrol.localhost
127.0.0.1 servicecontrol-audit.localhost
127.0.0.1 servicecontrol-monitor.localhost
This error means NGINX cannot reach ServiceControl. Check:
- ServiceControl is running (
dotnet runin the appropriate project directory) - ServiceControl is accessible directly:
curl http://localhost:33333/api - Docker Desktop is running and
host.docker.internalresolves correctly
Ensure ServiceControl instances are running and listening on the expected ports:
- ServiceControl (Primary): 33333
- ServiceControl.Audit: 44444
- ServiceControl.Monitoring: 33633
- Verify
FORWARDEDHEADERS_ENABLEDistrue - Verify
FORWARDEDHEADERS_TRUSTALLPROXIESistrue(for local Docker testing) - Use the
/debug/request-infoendpoint to check current settings
- Ensure mkcert's root CA is installed:
mkcert -install - Restart your browser after installing the root CA
If using Docker Desktop on Windows with WSL2:
- Ensure
host.docker.internalresolves correctly - Check that the ServiceControl ports are not blocked by Windows Firewall
The /debug/request-info endpoint is only available when running in Development environment (the default when using dotnet run).
- Hosting Guide - Configuration reference for all deployment scenarios
- Forwarded Headers Testing - Testing forwarded headers without a reverse proxy