Signotaur can be deployed behind a reverse proxy, for example to centralise TLS certificate management, present a friendly hostname, or consolidate host management. This is fully supported, but the proxy must be configured correctly, because Signotaur serves two different kinds of traffic on the same hostname.
Signotaur is a code-signing service that holds private signing keys. We strongly recommend keeping it accessible only from within your organisation (from trusted internal networks or over a VPN) rather than exposing it to the public internet. A reverse proxy is well suited to simplifying that internal access; we recommend pairing it with firewall rules, allow / deny rules, or an internal-only network interface to keep access restricted.
The Signotaur web dashboard and HTTP API use ordinary HTTP/1.1. The signing service used by SignotaurTool (the command-line client), however, uses gRPC, which requires HTTP/2 from end to end: from the tool, through the proxy, to the Signotaur server.
Most reverse proxies, by default, forward requests to the backend over HTTP/1.1. That works for the dashboard but breaks gRPC: the signing endpoint rejects the downgraded request. A standard proxy_pass-style rule is therefore not sufficient on its own.
Because both kinds of traffic share a single Signotaur hostname and port, the proxy must route them separately:
/SignService/ are gRPC and must be forwarded as gRPC (HTTP/2).Signotaur listens for both HTTP/1.1 and HTTP/2 on a single HTTPS port, so no change is required on the Signotaur server itself; only the reverse proxy needs configuring.
nginx forwards gRPC traffic with the grpc_pass directive, which is separate from the proxy_pass directive used for ordinary HTTP. The example below routes gRPC and HTTP traffic to the same Signotaur backend using two location blocks.
server {
listen 443 ssl;
http2 on; # HTTP/2 must be enabled for gRPC
server_name signotaur.example.com;
ssl_certificate /etc/nginx/ssl/signotaur.crt;
ssl_certificate_key /etc/nginx/ssl/signotaur.key;
# gRPC signing traffic (SignotaurTool) — must use grpc_pass.
location /SignService/ {
grpc_pass grpcs://signotaur-backend:443;
grpc_ssl_server_name on;
grpc_ssl_name $host;
grpc_ssl_verify off;
grpc_read_timeout 300s;
grpc_send_timeout 300s;
}
# Web dashboard and HTTP API.
location / {
proxy_pass https://signotaur-backend:443;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_ssl_verify off;
}
}
Replace signotaur.example.com with the public hostname, and signotaur-backend:443 with the host name (or IP address) and port that the Signotaur server itself listens on: the same address you would use to reach the dashboard directly.
A few points to note:
http2 on; must be present. Without HTTP/2 enabled on the proxy, SignotaurTool cannot negotiate a gRPC connection at all. On nginx versions earlier than 1.25.1, enable it on the listener instead: listen 443 ssl http2;.grpcs:// (gRPC over TLS) and https:// schemes.grpc_ssl_verify off and proxy_ssl_verify off are used because the Signotaur server's own certificate is usually issued for its internal host name and will not match the public hostname. If the backend certificate is trusted and its name matches, these lines can be removed./SignService/ is the fixed path of the Signotaur gRPC service. nginx automatically routes requests under that prefix to the grpc_pass block and everything else to the proxy_pass block, regardless of the order in which the blocks are defined.allow / deny directives or firewall rules, as noted above.Nginx Proxy Manager generates a proxy_pass rule for each proxy host automatically, but does not provide a way to add a grpc_pass location through its interface. Add the gRPC location manually:
location /SignService/ { ... } block shown above.location / rule) as it is; it continues to serve the dashboard.A location block does not inherit access rules (allow / deny) from the proxy host's main configuration. If you restrict access by IP address, repeat the same rules inside the location /SignService/ block, otherwise the signing endpoint will not be protected.
Any reverse proxy can be used, provided it can forward gRPC traffic to the backend. When choosing or configuring one:
/SignService/ path; not all proxies can do this.nginx is the recommended option. Caddy and Traefik also support gRPC routing. Apache can proxy gRPC using the mod_proxy_http2 module and the h2:// scheme, though its gRPC support is less complete than nginx's. Microsoft IIS, including the Application Request Routing (ARR) module, does not forward gRPC and is not suitable in front of the Signotaur signing service.
When Signotaur runs behind a reverse proxy, requests appear to come from the proxy itself. By default, the audit log therefore records the proxy's address rather than the original client's address.
To record the original client address, add the proxy to the TrustedProxies setting in the configuration file. You can specify an IP address or address range. Restart Signotaur after making the change:
"Site": {
"TrustedProxies": [ "10.0.0.5" ]
}
An address range may be given in CIDR notation, which is useful when the proxy is one of several in a subnet:
"Site": {
"TrustedProxies": [ "10.0.0.0/24" ]
}
The setting is read when the service starts, so restart Signotaur after changing it.
The address is recorded when logging security events, and signing and certificate requests made by the client tool, logged on the Events page.
Only add proxies you control. Signotaur trusts these proxies to report the original client address, so a trusted proxy can supply any address that will then appear in the audit log.
Leaving TrustedProxies empty is the safest default: Signotaur records the address it connects to directly and does not trust a forwarded client address.
Signotaur sends a set of response headers that constrain what a browser will do with its pages. They are described in full on the Web Interface Security page. Four points matter when a proxy is in front:
Strict Transport Security is sent on the HTTPS listener. Neither includeSubDomains nor preload is sent, so a separate hostname on the same domain is unaffected. HstsMaxAgeDays in the configuration file sets the period in days; 0 withdraws a policy browsers already hold, and a negative number omits the header.
Signotaur cannot be displayed inside a frame. A proxy that embeds it in a portal page will show nothing.
The sign-in cookies are always marked Secure. Terminating TLS at the proxy and forwarding plain HTTP does not remove the flag, but browsers must still reach the proxy over HTTPS or they will not send the cookies at all.
Do not strip or rewrite these headers at the proxy.
After configuring the proxy, test signing through the proxy hostname:
signotaurtool sign --sign-server https://signotaur.example.com --api-key "<your-api-key>" yourfile.exe
If the certificate public key is fetched and the file is signed, the gRPC path is working correctly.
The following errors reported by SignotaurTool indicate a reverse proxy that is not forwarding gRPC correctly:
| Error message | Cause |
|---|---|
Request protocol 'HTTP/1.1' is not supported. |
The proxy is forwarding the request to Signotaur over HTTP/1.1 (a proxy_pass-style rule) instead of gRPC. Use grpc_pass for the /SignService/ path. |
The SSL connection could not be established, application protocol negotiation failed |
HTTP/2 is not enabled on the proxy's front-end listener, so SignotaurTool cannot negotiate a gRPC connection. Enable HTTP/2 (http2 on;). |
The web dashboard can work correctly even when signing does not. Because the dashboard uses ordinary HTTP, a proxy that only handles HTTP/1.1 will serve the dashboard but fail every signing operation. Always test signing itself after changing the proxy configuration.