A simple HTTPS forward proxy for bypassing browser warning of Ngrok. Full support of SSL (free certificates included!), WebSocket, SSE and CORS.
Prerequisites:
- Somebody has sent you a link to an Ngrok endpoint, but it returns a warning page instead of the expected content
- You might be a web developer who is trying to use that endpoint in your code and curious how to bypass the warning page without rewriting the logic
- You have a Docker installed on your machine
OK, take me to the good part: Usage
From the ngrok docs:
To block phishing attacks using our platform, ngrok has added an interstitial page for free accounts receiving requests from browsers. When a user visits an endpoint for the first time, ngrok will serve an interstitial page letting the user know that the content is served via ngrok and that they should not enter sensitive information unless they trust the person that sent them the link. Users should only see this page once per endpoint and it can be completely bypassed by adding the
ngrok-skip-browser-warningheader to your request.
It's tedious to add this header while developing a client for the API, which is exposed behind ngrok. In some cases, e.g. using EventSource, affecting the request headers seems to be impossible.
Add a forward proxy which will add the ngrok-skip-browser-warning header to all HTTP requests:
For more features, consider getting the ngrok subscription.
Starting from 2023 July 9th, this image includes setup of a valid public CA signed SSL certificate and supports HTTPS (thanks to localhost.direct project).
Run this on the machine from where you are calling the ngrok endpoints:
$ docker run -d --rm \
-p 8443:443 \
-p 8080:80 \
-e NGROK_HOST=https://your-ngrok-domain.ngrok.io \
igops/ngrok-skip-browser-warning:latestFrom now, use https://ngrok.localhost.direct:8443 as your API webroot.
E.g., you were told to call GET https://your-ngrok-domain.ngrok.io/api/v1/whatever. Now you just call GET https://ngrok.localhost.direct:8443/api/v1/whatever instead, and get the response without the warning page!
*.localhost.direct is a wildcard record of the public DNS pointing to 127.0.0.1. You might want to customize the domain name to bind the proxy to, as well as the SSL certificates (see ENV Variables below).
If for some reason you don't want to use HTTPS relay, you can continue using http://ngrok.localhost.direct:8080 or http://localhost:8080 as your API webroot.
You can disable all SSL-related features by passing PROXY_USE_SSL=false environment variable:
$ docker run -d --rm \
-p 8080:80 \
-e NGROK_HOST=https://your-ngrok-domain.ngrok.io \
-e PROXY_USE_SSL=false \
igops/ngrok-skip-browser-warning:latestWebSocket and SSE protocols require a special handling of the Upgrade and Connection headers. This image supports both protocols out of the box.
However, to distinguish WebSocket and SSE requests from regular HTTP requests, I have implemented the conditional routing based on the request domain names:
| Protocol | Over HTTPS | Over HTTP |
|---|---|---|
| REST, GraphQL, etc. | https://ngrok.localhost.direct:8443 |
http://ngrok.localhost.direct:8080 |
| WebSocket | wss://ngrok-ws.localhost.direct:8443 |
ws://ngrok-ws.localhost.direct:8080 |
| SSE | https://ngrok-sse.localhost.direct:8443 |
http://ngrok-sse.localhost.direct:8080 |
You can customize the domain names on your own (see ENV Variables) or even the entire routing model (check out the Customization section).
If you're developing a web client which communicates with the HTTP server via multiple protocols, it's worth introducing some configurable constants, such as:

Click on the image to show the text version
Support of WebSocket and SSE was tested by running the Echo Server behind a free ngrok tunnel. Consider reporting an issue if you find any problems.
You can configure the proxy to add the Access-Control-Allow-Origin header to all responses by setting the ADD_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN environment variable:
$ docker run -d --rm \
-p 8443:443 \
-p 8080:80 \
-e NGROK_HOST=https://your-ngrok-domain.ngrok.io \
-e ADD_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN='*' \
igops/ngrok-skip-browser-warning:latestThis image uses Nginx as a proxy server, as well as Jinja template engine to generate the Nginx config on the fly.
Feel free to replace /etc/nginx/j2/default.j2.conf with your own implementation, or use my config as a basis.
For instance, you might create custom.j2.conf with a very bare minimum for your experiments:
Click on the image to show the text version
Mount it as follows:
$ docker run -d --rm \
-p 8443:443 \
-p 8080:80 \
-e NGROK_HOST=https://your-ngrok-domain.ngrok.io \
-v $PWD/custom.j2.conf:/etc/nginx/j2/default.j2.conf \
igops/ngrok-skip-browser-warning:latestJinja template variables refer to the ENV variables with the same names in SNAKE_UPPER_CASE.
| Jinja Variable | Environment Variable |
|---|---|
| ProxyHostREST | PROXY_HOST_REST |
| ProxyHostWSSupport | PROXY_HOST_WS_SUPPORT |
| ProxyHostSSESupport | PROXY_HOST_SSE_SUPPORT |
| ProxyUseSSL | PROXY_USE_SSL |
| ProxySSLCertName | PROXY_SSL_CERT_NAME |
| ProxySSLKeyName | PROXY_SSL_KEY_NAME |
| ProxyForceHTTPS | PROXY_FORCE_HTTPS |
| TargetScheme | Scheme of NGROK_HOST (typically equals to https) |
| TargetHost | Domain name of NGROK_HOST |
| AddHeaderAccessControlAllowOrigin | ADD_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN |
The simplest way to add a custom endpoint is to bind an additional *.localhost.direct subdomain with your own proxying rules:

Click on the image to show the text version
A custom location block might be as follows:

Click on the image to show the text version
| Variable | Default value | Description |
|---|---|---|
| NGROK_HOST | - | Mandatory. Your ngrok host, e.g. https://your-ngrok-domain.ngrok.io.Specifying a protocol is optional, https will be used by default.Any url parts after a domain name will be trimmed. |
| PROXY_HOST_REST | ngrok.localhost.direct |
Optional. A domain name to listen on REST API calls. |
| PROXY_HOST_WS_SUPPORT | ngrok-ws.localhost.direct |
Optional. A domain name to listen on WebSocket API calls. |
| PROXY_HOST_SSE_SUPPORT | ngrok-sse.localhost.direct |
Optional. A domain name to listen on SSE API calls. |
| PROXY_USE_SSL | true |
Optional. Enables relay over HTTPS. You can mount your own certificates to /etc/nginx/certs. If the directory is not mounted, localhost.direct certificate will be downloaded on container bootstrap. |
| PROXY_FORCE_HTTPS | false |
Optional. Forcibly redirect HTTP calls to HTTPS. |
| PROXY_SSL_CERT_NAME | localhost.direct.crt |
Optional. Override the name of the certificate file. Mount the file to /etc/nginx/certs/my-custom-cert.crt. |
| PROXY_SSL_KEY_NAME | localhost.direct.key |
Optional. Override the name of the certificate key. Mount the file to /etc/nginx/certs/my-custom-cert.key. |
| ADD_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN | (empty string) | Optional. Add custom Access-Control-Allow-Origin to all responses. |
https://github.com/igops/ngrok-skip-browser-warning
Feel free to contribute.

