HTTP/S Endpoints
Overview
HTTP/S endpoints enable you to serve web services like REST APIs, web
applications, websites and websocket servers. Serving a web application is as
simple as ngrok http 80
.
Once your endpoint is running, check out:
- Traffic Policy - Add routing, authentication and traffic transformation
- Traffic Inspector - Real-time observability with request/response introspection
- Endpoint Pooling - Load balancing
Quickstart
Agent Endpoint
Agent Endpoints are the easiest way to get started with ngrok. An agent endpoint is started by a Secure Tunnels agent. The endpoint lives for the lifetime of the process and forwards traffic to a port or URL of your choosing.
Create the endpoint https://example.ngrok.app
and forward its traffic to a
local port.
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http 8080 --url https://example.ngrok.app
version: 3
endpoints:
- name: example
url: https://example.ngrok.app
upstream:
url: 8080
ssh -R example.ngrok.app:443:localhost:8080 v2@connect.ngrok-agent.com http
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
upstreamURL, _ := url.Parse("http://localhost:8080")
return ngrok.ListenAndForward(ctx,
upstreamURL,
config.HTTPEndpoint(
config.WithURL("https://example.ngrok.app"),
),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
domain: "example.ngrok.app",
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("localhost:8080", authtoken_from_env=True,
domain="example.ngrok.app")
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.http_endpoint()
.domain("example.ngrok.app")
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
Rust Crate Docs:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
k8s.ngrok.com/modules: ngrok-module-set
spec:
ingressClassName: ngrok
rules:
- host: example.ngrok.app
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
Cloud Endpoint
Cloud endpoints are created via the ngrok Dashboard or API. They are persistent and live until they are deleted. Traffic Policy controls how a cloud endpoint handles traffic.
Create an HTTPS cloud endpoint
which returns a Hello world!
200 OK response.
- Agent CLI
ngrok api endpoints create \
--url https://example.ngrok.app \
--traffic-policy "$(<traffic-policy.yml)"
traffic-policy.yml
on_http_request:
- actions:
- type: custom-response
config:
status_code: 200
headers:
content-type: text/plain
content: "Hello world!"
URL
Public
HTTP
- The hostname must be a domain with a valid public suffix.
- The port must be
80
. If you do not specify a port, the default80
will be used for you.
Examples
http://example.ngrok.app
http://example.ngrok.app:80
http://example.party
http://example.ngrok.app:81
- invalid port: port number must be80
, not81
http://example.doesnotexist
- invalid hostname:.doesnotexist
is not a public suffix domainhttp://example.internal
- invalid hostname:.internal
is not a public suffix domain
HTTPS
- The hostname must be a domain with a valid public suffix.
- The port must be
443
. If you do not specify a port, the default443
will be used for you.
Internal
Kubernetes
Valid URLs
Invalid URLs
Validation
When you create an agent endpoint, if you do not specify a complete URL, following defaults are used to construct an endpoint URL. When you create a cloud endpoint, you must always specify both a scheme and hostname.
URL Part | Default |
---|---|
Scheme | https |
Hostname | randomly selected |
Port | 443 if scheme is https 80 if scheme is http |
Consult the following table of examples of URL defaulting:
Value | Endpoint URL |
---|---|
https://example.ngrok.app | https://example.ngrok.app |
http://example.ngrok.app | http://example.ngrok.app |
example.ngrok.app | https://example.ngrok.app |
app.example.com | https://app.example.com |
https://example.internal | https://example.internal |
https://example.internal:1234 | https://example.internal:1234 |
http://example.internal | http://example.internal |
foo.internal | https://foo.internal |
{empty} | https://1eb2-181-80-12-3.ngrok.app (randomly selected) |
If you would like to listen for both http and https traffic, create two endpoints.
Domains
When you create a public endpoint, it must match a Domain on your account. Domains help you set up branded domains and manage TLS certificates. You may create endpoints with wildcard domains as well.
Endpoints with randomly-assigned hostnames are an exception and won't match an existing Domain object.
Bring your own domain
If you want to bring your own domain, first create a Domain record and set up a DNS CNAME record. Then create an endpoint on that domain by specifying a URL with a matching hostname.
For example, to create an HTTPS endpoint on https://app.example.com
, create
a Domain and follow the instructions to
set up a CNAME record. Then use the following example to start an endpoint on
your domain:
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http 80 --url app.example.com
version: 3
endpoints:
- name: example
url: https://app.example.com
upstream:
url: 8080
ssh -R app.example.com:443:localhost:80 v2@connect.ngrok-agent.com http
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
return ngrok.Listen(ctx,
config.HTTPEndpoint(
config.WithDomain("app.example.com"),
),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
domain: "example.ngrok.app",
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("localhost:8080", authtoken_from_env=True,
domain="example.ngrok.app")
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.http_endpoint()
.domain("example.ngrok.app")
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
Rust Crate Docs:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
k8s.ngrok.com/modules: ngrok-module-set
spec:
ingressClassName: ngrok
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
Wildcard endpoints
You can create an endpoint which will receive traffic for all of the subdomains
matching a given wildcard domain like *.example.com
. You must create a
wildcard domain to create a
public wildcard endpoint.
For example, if you create the wildcard endpoint https://*.example.com
, it
will receive traffic for https://foo.example.com
and
https://bar.example.com
.
- Connections to URLs which match an online wildcard endpoint will be routed to
it. For example, if you have created a wildcard endpoint
https://*.example.com
, connections tohttps://foo.bar.baz.example.com
will route to it. - Connections are routed to the most specific online endpoint. For example, if
the endpoints
https://*.example.com
andhttps://app.example.com
are both online, a connection tohttps://app.example.com
will not be routed to the wildcard endpoint.
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http 80 --url "*.example.com"
version: 3
tunnels:
example:
url: https://*.example.com
upstream:
url: 80
ssh -R '*.example.com:443:localhost:80' v2@connect.ngrok-agent.com http
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
return ngrok.Listen(ctx,
config.HTTPEndpoint(
config.WithDomain("*.example.com"),
),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
domain: "*.example.com",
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("localhost:8080", authtoken_from_env=True,
domain="*.example.com")
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.http_endpoint()
.domain("*.example.com")
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
Rust Crate Docs:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
k8s.ngrok.com/modules: ngrok-module-set
spec:
ingressClassName: ngrok
rules:
- host: *.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
Randomly assigned domains
If you run create an endpoint without specifying a domain, ngrok will
automatically assign a random domain from one of our ngrok-managed
Domains to your endpoint.
For example, the command ngrok http 80
may create an endpoint like
https://1eb2-181-80-12-3.ngrok.app
.
The following example create an HTTPS endpoint on a randomly assigned hostname that forwards to port 8080.
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http 8080
version: 3
endpoints:
- name: example
upstream:
url: 8080
ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
return ngrok.Listen(ctx,
config.HTTPEndpoint(),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("localhost:8080", authtoken_from_env=True)
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.http_endpoint()
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
Rust Crate Docs:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
k8s.ngrok.com/modules: ngrok-module-set
spec:
ingressClassName: ngrok
rules:
- host: example.ngrok.app
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
Traffic Policy
Attach Traffic Policy to endpoints to route, authenticate and transform the traffic through the endpoint.
TODO traffic policy example
Authentication
Public endpoints are accessible to the public internet unless you secure them with authentication. That's desirable if you're hosting a public website but most often you want to add authentication. You can secure your endpoints with Traffic Policy with any of the following actions:
Basic Auth Example
Adds a username and password with the Basic Auth Traffic Policy action.
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http 80 --basic-auth "username1:password1" --basic-auth "username2:password2"
version: 3
endpoints:
- name: example
url: https://your-subdomain-here.ngrok-free.app
upstream:
url: 80
traffic_policy:
on_http_request:
- actions:
- type: basic-auth
config:
credentials:
- username1:password1
- username2:password2
ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http \
--basic-auth "username1:password1" \
--basic-auth "username2:password2"
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
return ngrok.Listen(ctx,
config.HTTPEndpoint(
config.WithBasicAuth("username1", "password1"),
config.WithBasicAuth("username2", "password2"),
),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
basic_auth: ["username1:password1", "username2:password2"],
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("localhost:8080", authtoken_from_env=True,
basic_auth=["username1:password1", "username2:password2"])
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.http_endpoint()
.basic_auth("username1", "password1")
.basic_auth("username2", "password2")
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
Rust Crate Docs:
This module is not supported by the ngrok Kubernetes Operator.
Google OAuth Example
The following example enforces a browser-based OAuth redirect flow in front of your endpoint using Google as the identity provider by using the OAuth Traffic Policy action.
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http 80 --oauth google
version: 3
endpoints:
- name: example
url: https://your-subdomain-here.ngrok-free.app
upstream:
url: 80
traffic_policy:
on_http_request:
- actions:
- type: oauth
config:
provider: google
ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http --oauth=google
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
return ngrok.Listen(ctx,
config.HTTPEndpoint(
config.WithOAuth("google"),
),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
oauth_provider: "google",
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("localhost:8080", authtoken_from_env=True,
oauth_provider="google")
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.http_endpoint()
.oauth(OauthOptions::new("google"))
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
Rust Crate Docs:
---
kind: NgrokModuleSet
apiVersion: ingress.k8s.ngrok.com/v1alpha1
metadata:
name: ngrok-module-set
modules:
oauth:
google:
optionsPassthrough: false
inactivityTimeout: 4h
maximumDuration: 24h
authCheckInterval: 1h
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
k8s.ngrok.com/modules: ngrok-module-set
spec:
ingressClassName: ngrok
rules:
- host: your-domain.ngrok.app
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
Transformation
TODO
Rewriting the host
header
Some application servers expect the host
header to match a specific value
when they receive requests and some use the host
header to determine which of
many sites to display. ngrok can rewrite the host
header of incoming requests
so that your application behaves correctly.
When you rewrite the host
header, ngrok also rewrites the location
header of
HTTP responses automatically to match the hostname of your Endpoint URL.
The following example rewrites the host
header to the value localhost
using
the add-headers
Traffic Policy action.
Adding the Host
header is a special
case that replaces the existing
Host
header instead of appending a second value.
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http 8080 --request-header-add='host: localhost'
tunnels:
example:
proto: http
addr: 8080
request_header:
add: ["host: localhost"]
ssh -R 443:localhost:80 v2@connect.ngrok-agent.com http \
--request-header-add='host: localhost'
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
return ngrok.Listen(ctx,
config.HTTPEndpoint(
config.WithRequestHeader("host", "localhost"),
),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
request_header_add: "host:localhost",
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("localhost:8080", authtoken_from_env=True,
request_header_add="host:localhost")
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.http_endpoint()
.request_header("host", "localhost")
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
Rust Crate Docs:
kind: NgrokModuleSet
apiVersion: ingress.k8s.ngrok.com/v1alpha1
metadata:
name: ngrok-module-set
modules:
headers:
request:
add:
host: "localhost"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
k8s.ngrok.com/modules: ngrok-module-set
spec:
ingressClassName: ngrok
rules:
- host: your-domain.ngrok.app
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
Routing
TODO
Agent Forwarding
The ngrok agent and Agent SDKs forward traffic that your endpoints receive to upstream services. You specify a URL or port number to instruct the ngrok agent where and how to forward traffic.
HTTPS forwarding
The scheme in your upstream URL is used to determine whether to forward HTTP or
HTTPS traffic to the upstream service. If you do not specify a scheme, the
default http
scheme is chosen unless you forward to port 443
, in which
case ngrok will use https
. Consult the following table of examples.
Upstream URL | Normalized Value |
---|---|
http://localhost:1234 | http://localhost:1234 |
https://localhost:1234 | https://localhost:1234 |
localhost:1234 | http://localhost:1234 |
1234 | http://localhost:1234 |
localhost:443 | https://localhost:443 |
443 | https://localhost:443 |
ngrok assumes that the network you run the agent on is private and it does not
verify the TLS certificate presented by the upstream service. You may change
this behavior with the flags --upstream-tls-verify
and
upstream-tls-verify-cas
.
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http https://localhost:8443
version: 3
endpoints:
- name: example
url: "https://example.ngrok.app"
upstream:
url: https://localhost:8443
Forwarding to an upstream HTTPS service is not supported via SSH.
import (
"context"
"net/url"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokForwarder(ctx context.Context) (ngrok.Forwarder, error) {
backendUrl, err := url.Parse("https://localhost:8443")
if err != nil {
return nil, err
}
return ngrok.ListenAndForward(ctx,
backendUrl,
config.HTTPEndpoint(),
ngrok.WithAuthtokenFromEnv(),
)
}
For HTTP/2 Use: config.HTTPEndpoint(config.WithAppProtocol("http2"))
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: "https://localhost:8443",
authtoken_from_env: true,
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("https://localhost:8443", authtoken_from_env=True)
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
use ngrok::tunnel;
use ngrok::forwarder::Forwarder;
use url::Url;
async fn forward_ngrok() -> Result<Forwarder<tunnel::HttpTunnel>, Error> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
sess
.http_endpoint()
.listen_and_forward(Url::parse("https://localhost:8443")?)
.await
.map_err(Into::into)
}
Rust Crate Docs:
Add the k8s.ngrok.com/app-protocols
label to the Service definition
targeted by your ingress backend to instruct the Operator to use
https
when forwarding connections.
apiVersion: v1
kind: Service
metadata:
name: example-service
annotations:
k8s.ngrok.com/app-protocols: '{"example-https-port":"HTTPS"}'
spec:
ports:
- name: example-https-port
port: 443
protocol: TCP
targetPort: 8443
selector:
app-name: some-example-app-label
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
k8s.ngrok.com/modules: ngrok-module-set
spec:
ingressClassName: ngrok
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 443
Non-local forwarding
Agents don't just forward to ports on your localhost. You can forward traffic
to any address or URL reachable from the agent. For example, if you want to
forward traffic to a HTTP server running on your network at 192.168.1.2:80
:
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http 192.168.1.2:80
version: 3
endpoints:
- name: example
url: http://example.ngrok.app
upstream:
url: http://192.168.1.2
ssh -R 0:192.168.1.2:80 v2@connect.ngrok-agent.com http
import (
"context"
"net/url"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokForwarder(ctx context.Context) (ngrok.Forwarder, error) {
backendUrl, err := url.Parse("http://192.168.1.2:80")
if err != nil {
return nil, err
}
return ngrok.ListenAndForward(ctx,
backendUrl,
config.HTTPEndpoint(),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: "192.168.1.2:80",
authtoken_from_env: true,
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("192.168.1.2:80", authtoken_from_env=True)
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
use ngrok::tunnel;
use ngrok::forwarder::Forwarder;
use url::Url;
async fn forward_ngrok() -> Result<Forwarder<tunnel::HttpTunnel>, Error> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
sess
.http_endpoint()
.listen_and_forward(Url::parse("http://192.168.1.2:80")?)
.await
.map_err(Into::into)
}
Rust Crate Docs:
The ngrok Kubernetes Operator always forwards its traffic. All of our other
examples show the most common forwarding case: a Service
object that defines
a label selector of matching pods to forward traffic to.
But you can also forward to an explicit set of IP
addresses
on the same network using Service
and EndpointSlice
objects.
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: example-service-1
labels:
kubernetes.io/service-name: example-service
addressType: IPv4
ports:
- name: "http"
appProtocol: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "192.168.1.2"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
k8s.ngrok.com/modules: ngrok-module-set
spec:
ingressClassName: ngrok
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
HTTP/2 forwarding
When agents forward to upstream http/2 services, connections use HTTP/1.1 by default.
You can configure the agent, SDKs and Kubernetes Operator to instead use HTTP/2 when forwarding to your upstream service.
Forwarder | Option | Docs |
---|---|---|
Agent | --upstream-protocol | Agent CLI flags |
Agent SDKs | language-dependent | Agent SDKs |
Kubernetes Operator | appProtocol on the Tunnel CRD | Kubernetes Operator |
When http2 forwarding is enabled, all requests to your upstream service will be transmitted over HTTP/2 Cleartext since TLS was already terminated at the ngrok cloud service. We cannot use TLS-ALPN at this time. We rely on HTTP/2 with Prior Knowledge currently.
Serving file directories
The ngrok agent supports the file://
scheme in a forwarding URL. When you use
the file://
scheme, the ngrok agent serves local file system directories by
using its own built-in file server, no separate server needed. It works just
like python3 -m http.server
but it is built directly into the ngrok agent.
Paths in file://
URLs must be specified as absolute paths.
Serve files in /var/log
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http "file:///var/log"
version: 3
endpoints:
- name: example
url: "https://example.ngrok.app"
upstream:
url: "file:///var/log"
Serving directory files is not supported via SSH.
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func serveFiles(ctx context.Context) error {
l, _ := ngrok.Listen(ctx,
config.HTTPEndpoint(),
ngrok.WithAuthtokenFromEnv(),
)
http.Serve(l, http.FileServer(http.Dir("/var/log")))
}
Serving directory files is not supported in the Javascript SDK.
Serving directory files is not supported in the Python SDK.
Serving directory files is not supported in the Rust SDK.
Serving directory files is not supported in the ngrok Kubernetes Operator.
Serve files on Windows
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http "file://C:\Users\alan\Directory Name"
version: 3
endpoints:
- name: example
url: "https://example.ngrok.app"
upstream:
url: "file://C:\Users\alan\Directory Name"
Serving directory files is not supported via SSH.
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func serveFiles(ctx context.Context) error {
l, _ := ngrok.Listen(ctx,
config.HTTPEndpoint(),
ngrok.WithAuthtokenFromEnv(),
)
http.Serve(l, http.FileServer(http.Dir("C:\Users\alan\Directory Name")))
}
Serving directory files is not supported in the Javascript SDK.
Serving directory files is not supported in the Python SDK.
Serving directory files is not supported in the Rust SDK.
Serving directory files is not supported in the ngrok Kubernetes Operator.
Serve files in your current working directory
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok http file://`pwd`
Serving the current working directory is not supported via the agent configuration file.
Serving directory files is not supported via SSH.
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func serveFiles(ctx context.Context) error {
l, _ := ngrok.Listen(ctx,
config.HTTPEndpoint(),
ngrok.WithAuthtokenFromEnv(),
)
http.Serve(l, http.FileServer(http.Dir(".")))
}
Serving directory files is not supported in the Javascript SDK.
Serving directory files is not supported in the Python SDK.
Serving directory files is not supported in the Rust SDK.
Serving directory files is not supported in the ngrok Kubernetes Operator.
Traffic Observability
Traffic Inspector
TODO
Log Export Events
You can export logs of traffic to HTTP/S endpoints with ngrok's events system. The following events are published for log exporting:
Event | When |
---|---|
http_request_complete.v0 | Published when an HTTP request to an HTTP/S endpoints completes. |
tcp_connection_closed.v0 | Published when a TCP connection to an HTTP/S endpoints completes. |
Protocol
HTTP/S endpoints are standards-compliant HTTP reverse proxies.
- HTTP/S endpoints support HTTP/1.1.
- HTTPS endpoints support HTTP/1.1 and HTTP/2.
- HTTP/1.0, HTTP/3 and QUIC are not supported.
Websockets
Websocket connections are supported out-of-the-box. No configuration is required.
HTTP/2
HTTPS endpoints will automatically use HTTP/2 for all connections if the client supports it. Client support is determined via standard ALPN negotiation.
HTTP/2 is used between the client and your endpoint even even if your upstream service does not support HTTP/2.
The section on HTTP/2 agent forwarding has details on how to configure the use of HTTP/2 when sending traffic to an upstream service.
Hop by hop headers
ngrok does not forward any hop-by-hop
headers to the
upstream service. As an exception, Connection: upgrade
headers are forwarded
to support websockets.
For information on headers added automatically by ngrok, see Upstream Headers.
Persistent connections
When a connection is made to HTTP/S ngrok endpoints with HTTP/1.1, ngrok may choose to use persistent connections (aka HTTP keep-alive) to improve the performance of future requests from the same client if the client supports it.
This behavior is not guaranteed and it is not configurable.
See RFC 7230 for additional details.
TLS
ngrok automatically handles TLS (SSL) certificate management and termination for you. There is nothing to setup, configure or manage.
TLS connections to https
endpoints are terminated at ngrok's cloud service.
If you wish to terminate TLS traffic at the ngrok agent or in your upstream
application, use a TLS Endpoint instead.
Consult the following documentation for additional details on how ngrok handles TLS termination and certificiate management:
Upstream Headers
ngrok adds headers to each HTTP request with information about the original
client IP, request scheme and request host
header value.
Header | Description |
---|---|
x-forwarded-for | The IP address of the client who initiated the request. If this header exists on the original request, ngrok will append a new value. |
x-forwarded-proto | The scheme of the original request, either http or https . If this header exists on the original request, ngrok will append a new value. |
x-forwarded-host | The header from the client's request if it existed, otherwise is set to the request's Host header value. |
Because ngrok appends values to x-forwarded-for
and x-forwarded-proto
, be
sure to use the last value of the header in your application code to read the
values injected by ngrok.
You may remove these headers with the Remove Headers Traffic Policy action.
Errors
If ngrok fails to handle an HTTP request it will set the ngrok-error-code
header in the HTTP response with a unique ngrok Error Code
describing the failure.
ngrok guarantees that the upstream service may never set the ngrok-error-code
HTTP response header so you know reliably that it was set by ngrok.
ngrok may return an error under the following conditions:
- Your upstream service timed out or rejected the connection
- Your upstream service returned a response that was not valid HTTP
- A Traffic Policy action rejected the request.
- Traffic Policy execution encountered a runtime error.
- ngrok encountered an internal error
API
HTTP/S Endpoints can be created programatically. Consult the documentation on Endpoint APIs.
Pricing
HTTP/S endpoints are available on all plans. Consult the Endpoints Pricing documentation for billing details.
See Domains pricing for details on pricing for custom domains, wildcard domains and more.