This guide provides scenario-based tests for ServiceControl instances behind an NGINX reverse proxy. Use it 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)
| 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 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 (Git ignores this folder):
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 (for example172.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
- Local testing of forwarded headers without NGINX: Testing forwarded headers without a reverse proxy