Running Rammerhead behind nginx (or another reverse proxy) requires a small amount of extra configuration. Because Rammerhead uses testcafe-hammerhead to rewrite every URL it proxies, it must know the public hostname, port, and protocol that clients see — not the internal address it binds to. Without this, hammerhead generates rewritten URLs pointing atDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/binary-person/rammerhead/llms.txt
Use this file to discover all available pages before exploring further.
localhost, breaking the proxy for end users.
Why getServerInfo matters
Hammerhead constructs all rewritten URLs from a ServerInfo object. Rammerhead builds that object by calling the getServerInfo function in config.js on every incoming request. The default implementation hard-codes localhost:8080:
Overriding with request headers
nginx forwards the originalHost header to the upstream, so you can read it from req.headers.host and return the correct public values dynamically:
Hard-code
port and protocol to the values your proxy exposes to the public. The req.headers.host approach for hostname makes the config portable across multiple domain names or subdomains without changes.Cross-domain port
Hammerhead uses a second HTTP port (crossDomainPort) to simulate cross-origin requests accurately, which matters for sites that inspect the Origin header. By default, Rammerhead listens on 8081 for this purpose.
Behind a reverse proxy, you need to either:
- Expose a second public port (e.g.,
8443) and proxy it to8081 - Or set
crossDomainPort: nullto use a single port (see the note on single-port mode below)
getServerInfo example above, crossDomainPort: 8443 tells hammerhead to include 8443 in rewritten cross-origin URLs, which nginx then proxies back to Rammerhead’s internal 8081.
Stripping reverse-proxy headers
When clients connect through Cloudflare or another CDN, the proxy injects headers such asCF-Connecting-IP and x-forwarded-for. If Rammerhead forwards these to destination sites, the sites see infrastructure-level metadata instead of the expected request. Strip them with stripClientHeaders:
Reading the real client IP
When Rammerhead sits behind a proxy,req.socket.remoteAddress resolves to the proxy’s address, not the client’s. Override getIP to read the forwarded header:
Rewriting response headers
Some destination sites return headers likeX-Frame-Options that prevent their pages from loading inside the proxy. Use rewriteServerHeaders to remove or replace them in Rammerhead’s responses to the browser:
nginx configuration
The following example terminates TLS at nginx and proxies both the main port and the cross-domain port to Rammerhead running on127.0.0.1:
SSL termination
- TLS at nginx (recommended)
- TLS at Rammerhead
Keep Rammerhead’s
ssl config as null and let nginx handle certificates. This is the most common setup and simplifies certificate renewal (e.g., with Certbot):Single-port mode
If you cannot expose a second port, setcrossDomainPort: null. Rammerhead will use a single HTTP server. Some sites that check the Origin header may behave differently, but most will work correctly:
When
crossDomainPort is null, _rewriteServerInfo in RammerheadProxy falls back to the main port for cross-domain requests, so crossDomainPort in getServerInfo can also be omitted or set to null.