Product overview
One server between the network and your application.
Webship is a self-hosted Rust edge and static web server. One runtime terminates modern protocols, applies edge policy, serves files, and proxies application requests.
Modern transport
Accept HTTP/1.1, HTTP/2, and HTTP/3, with TLS 1.3 and an optional WebTransport endpoint.
Static and proxied delivery
Serve static files with validators and precompressed sidecars, or proxy application traffic through bounded upstream pools.
Secure defaults
Begin with the WAF, DDoS controls, bot challenge, response security headers, API Shield, and dot-file protection enabled.
Observable operations
Use authenticated statistics, Prometheus metrics, request IDs, graceful reloads, and the optional MCP control plane.
Quick start
From release binary to healthy listener.
Start on loopback, validate everything before binding, and verify the built-in health response before adding public traffic.
Prepare files
Place the release binary, its TOML configuration, the static root, and any configured TLS certificate and key files on the host.
Validate and inspect
Run both configuration commands. Fix the first error and inspect the redacted effective result before startup.
Start privately
Start Webship with the selected TOML file. Add a complete certificate pair or automatic TLS when the site is ready for secure public traffic.
Verify the runtime
Call GET /health locally. Then test static paths, TLS, proxy routes, security rules, and authenticated monitoring.
listen = "127.0.0.1:4433"
workers = 4
root = "./public"/usr/local/bin/webship --check-config --config /etc/webship/production.toml
/usr/local/bin/webship --print-effective-config --config /etc/webship/production.toml/usr/local/bin/webship --config /etc/webship/production.toml
curl --http3-only --insecure https://127.0.0.1:4433/healthQuick Start Guide for AI Agents
Connect an AI agent to Webship in five lines.
Connect Claude Code, OpenAI, DeepSeek, or any compatible MCP client through a private SSH tunnel. The agent receives an authenticated operations surface without sharing the public listener or exposing control credentials to internet traffic.
{
"mcpServers": {
"webship": { "url": "https://localhost:19443/mcp",
"headers": { "Authorization": "Bearer <token>" } }
} }Static delivery
Serve a directory with protocol-aware defaults.
Set a root globally or per site. TLS sites default to HTTP/3 only; cleartext sites default to HTTP/1.1 and H2C. Override HTTP/1.1, HTTP/2, and HTTP/3 independently for each site.
[[sites]]
domain = "app.example.com"
root = "/srv/app"
listen = "0.0.0.0:443"
[sites.protocols]
h1 = true
h2 = true
h3 = trueThe built-in router supports GET and HEAD, byte ranges, conditional requests, validators, and .br, .zst, and .gz sidecars. Dot-file paths are refused by default; .well-known remains available.
Application traffic
Route requests to one or more upstreams.
Enable reverse proxying, match a host and path, then define a final unconditional policy. Webship supports bounded pools, health checks, load balancing, circuit breakers, safe bodyless retries, WebSockets, and caching.
[reverse_proxy]
enabled = true
[[reverse_proxy.routes]]
domain = "app.example.com"
path_prefix = "/api"
strip_path_prefix = true
upstreams = ["127.0.0.1:8080", "127.0.0.1:8081"]
[[reverse_proxy.policies]]
name = "default"
hosts = []
path_prefixes = ["/"]
methods = []
max_body_bytes = 1048576
total_timeout_ms = 30000Webship 1.5.0 Layer 4
Proxy TCP and UDP traffic without an HTTP route.
Layer 4 is disabled by default. Define named upstreams, independent TCP and UDP policies, listeners, and routes. TCP supports connection limits, health checks, load balancing, optional PROXY protocol, and TLS pass-through or termination. UDP uses bounded flows and batched receive workers.
[layer4]
enabled = true
[[layer4.tcp_policies]]
name = "edge-tcp"
max_connections = 10000
idle_timeout_ms = 300000
[[layer4.udp_policies]]
name = "edge-udp"
max_flows = 20000
receive_batch_size = 32
[[layer4.upstreams]]
name = "origin-a"
address = "127.0.0.1:9443"
weight = 100
[[layer4.tcp]]
name = "tls-tunnel"
listen = "127.0.0.1:10443"
policy = "edge-tcp"
tls_mode = "passthrough"
[[layer4.tcp.routes]]
name = "default"
default = true
upstreams = ["origin-a"]
load_balancing = "weighted-peak-ewma"
[[layer4.udp]]
name = "datagrams"
listen = "127.0.0.1:10443"
policy = "edge-udp"
[[layer4.udp.routes]]
name = "default"
default = true
upstreams = ["origin-a"]Per-route delivery
Build a bounded CDN-style cache without crossing site boundaries.
Cache is configured on each reverse-proxy route, not globally. Choose safe methods and statuses, TTL bounds, CDN-Cache-Control behavior, and query-key normalization for that site and path. Authenticated, personalized, private, and no-store responses remain uncacheable by default.
[[reverse_proxy.routes]]
domain = "assets.example.com"
path_prefix = "/assets"
upstreams = ["127.0.0.1:8080"]
[reverse_proxy.routes.cache]
enabled = true
cacheable_methods = ["GET", "HEAD", "QUERY"]
cacheable_statuses = [200, 203, 204, 206, 301, 404, 410]
default_ttl_ms = 60000
max_ttl_ms = 86400000
honor_cdn_cache_control = true
query_mode = "ignore-listed"
ignored_query_parameters = ["utm_*", "fbclid"]Local traffic context
Apply GeoIP rules without a hosted lookup service.
Load country, city, and ASN data from local MaxMind-compatible databases. GeoIP is disabled by default; keep the database files current and decide whether lookup failures should deny or bypass geo-dependent policy before enabling it.
[geoip]
enabled = true
country_db = "/var/lib/webship/geo/country.mmdb"
city_db = "/var/lib/webship/geo/city.mmdb"
asn_db = "/var/lib/webship/geo/asn.mmdb"
failure_mode = "deny"Mail protocols
Route mail sessions with protocol-aware controls.
Use the separate mail data plane for SMTP submission, IMAP, POP3, and related protocol-aware routing. Listeners can use pass-through, implicit TLS, or a bounded STARTTLS upgrade. Keep new listeners on loopback until upstream identity, certificate, authentication, and TLS policy are verified.
[mail]
enabled = true
[[mail.upstreams]]
name = "submission-a"
address = "127.0.0.1:2465"
protocol = "smtp-submission"
security = "plain"
[[mail.listeners]]
name = "submissions"
listen = "127.0.0.1:1465"
protocol = "smtp-submission"
client_security = "passthrough"
upstreams = ["submission-a"]
load_balancing = "weighted-least-requests"TLS and HTTP/3
Use manual certificates or let Webship manage them.
Since Webship 1.4.0, every site can select its certificate mode independently. HTTP/3 uses the matching UDP listener; enable HTTP/1.1 or HTTP/2 when the same TLS 1.3 site also needs TCP compatibility.
listen = "0.0.0.0:443"
[automatic_tls]
enabled = true
directory_url = "https://acme-v02.api.letsencrypt.org/directory"
cache_dir = "/var/lib/webship/acme"
contacts = ["mailto:ops@example.com"]
accept_terms_of_service = true
[[sites]]
domain = "app.example.com"
root = "/srv/app"
certificate_mode = "per_site" # default public ACME certificate
[[sites]]
domain = "legacy.example.com"
root = "/srv/legacy"
certificate_mode = "shared" # legacy public multi-SAN certificatePer-site certificate architecture
Choose certificate trust and scale independently for every site.
Webship 1.4.0 moves certificate_mode onto each [[sites]] entry. Public per-site ACME, public DNS-01 fleet shards, the embedded private CA, the legacy shared certificate, and manual certificate files can coexist in one self-contained process.
per_site — public certificate
The default. Order one browser-trusted public ACME certificate for the site's exact name with TLS-ALPN-01.
fleet — public DNS-01 shards
Use public DNS-01 issuance for many third- and fourth-level names under explicit registered domains. Names remain in stable, batched certificate shards.
embedded — private CA
Issue a separate certificate in-process from Webship's private CA. No public ACME account, DNS challenge, registrar integration, or inbound port 443 is required.
shared — legacy multi-SAN
Keep the legacy public multi-SAN group for deployments that require it. This is not the default and remains subject to public-CA identifier limits.
[automatic_tls]
enabled = true
directory_url = "https://acme-v02.api.letsencrypt.org/directory"
cache_dir = "/var/lib/webship/acme"
contacts = ["mailto:ops@example.com"]
accept_terms_of_service = true
[automatic_tls.fleet]
shard_size = 64
batch_window_ms = 300000
weekly_certificate_limit = 50
emergency_certificate_reserve = 5
registered_domains = ["example.com"]
[automatic_tls.fleet.dns]
listen = "0.0.0.0:53"
nameservers = ["ns1.example.net"]
addresses = ["192.0.2.10"]
propagation_timeout_ms = 120000
resolver_url = "https://dns.google/resolve"
challenge_ttl_seconds = 900
[[sites]]
domain = "app.example.com"
root = "/srv/app"
certificate_mode = "fleet"
[[sites]]
domain = "media.app.example.com"
root = "/srv/media"
certificate_mode = "fleet"[automatic_tls]
enabled = true
cache_dir = "/var/lib/webship/acme"
[acme_ca]
state_dir = "/var/lib/webship/acme-ca"
leaf_validity_days = 90
[[sites]]
domain = "internal.example.com"
root = "/srv/internal"
certificate_mode = "embedded"Edge policy
Keep the secure baseline intact.
Webship enables its main protection layers by default. Tune limits for your workload and validate after every rule or header-policy change.
[ddos]
enabled = true
mode = "normal"
requests_per_minute = 600
burst = 100
block_seconds = 300
[security.response_headers]
enabled = true
nosniff = true
frame_deny = true
referrer_no_referrer = true
hsts = "max-age=31536000; includeSubDomains"
content_security_policy = "default-src 'self'; frame-ancestors 'none'"Use normal mode for regular traffic, under_attack for stricter active-attack handling, and lockdown when only probes and explicitly permitted paths should remain available.
Private diagnostics
Inspect the edge without exposing the control plane.
Statistics and Prometheus metrics run on a separate authenticated listener. Instrumentation must be enabled whenever either endpoint is active.
[observability]
instrumentation = true
stats = true
metrics = true
listen = "127.0.0.1:9090"
token = "replace-with-at-least-32-random-printable-ascii-characters"Safe operations
Reload deliberately. Keep rollback close.
Configuration reload
Send SIGHUP after editing a file-backed configuration. Webship validates the replacement before installing it and retains the running configuration when validation fails.
Upgrade and rollback
Install the new binary beside the previous version, validate the production configuration with it, then verify health, TLS, proxying, and metrics. Keep the previous binary until every gate passes.
kill -HUP "$(pidof webship)"
/usr/local/bin/webship update --config /etc/webship/production.toml
sudo systemctl daemon-reload
sudo systemctl enable --now webship
sudo systemctl status webshipProfile-guided optimization
Collect target-native profiles without confusing training and production.
Every Webship 1.5.0 target has a separate instrumented CLI for collecting target-native LLVM profile data under your representative traffic. Use the exact version and target triple, exercise the routes and protocols that matter, and stop the process gracefully so it can flush every .profraw file.
Select the exact target
Download the PGO training CLI whose release version and Rust target triple exactly match the runtime you intend to optimize. Verify its published SHA-256 first.
Capture representative traffic
Set LLVM_PROFILE_FILE to a writable directory, start the training CLI with a validated copy of the real configuration, replay representative direct and reverse-proxy traffic, then stop Webship gracefully.
Merge raw profiles
Use llvm-profdata from the compiler generation recorded for the release. Merge every emitted .profraw file into one sparse webship.profdata file.
Rebuild and gate
Apply the merged profile only to the exact source, compiler, crypto provider, feature set, and target that generated it. Run correctness and performance gates before promotion.
mkdir -p ./profiles
export LLVM_PROFILE_FILE="$PWD/profiles/webship-%p-%m.profraw"
./webship-pgo-training-1.5.0-<target> --config ./webship.tomlNew-Item -ItemType Directory -Force ./profiles
$env:LLVM_PROFILE_FILE = "$PWD/profiles/webship-%p-%m.profraw"
.\webship-pgo-training-1.5.0-<target>.exe --config .\webship.tomlllvm-profdata merge -sparse ./profiles/*.profraw -o ./webship.profdataCommand-line reference
Small surface, explicit startup.
webship [OPTIONS] | webship update [OPTIONS]
-c, --config PATH- Select the TOML file. If it does not exist, Webship creates it with a private localhost TLS identity.
--check-config- Validate the complete configuration and exit without starting listeners.
--print-effective-config- Print the merged effective configuration with secrets removed.
update- Verify the signed community manifest, select this exact platform target, and install a newer version when one exists.
--help / --version- Print command help or the installed Webship version.
Common failure modes
Start with configuration, then move outward.
- Run --check-config and correct the first reported error; unknown TOML fields are rejected.
- Confirm the configured TCP and UDP ports are available and allowed by the firewall.
- Confirm the certificate and key exist, are readable by the service account, and form a matching pair.
- For automatic TLS, confirm every configured domain resolves to the Webship host.
- Call /health on the local application listener before testing through DNS or an external load path.
- Enable authenticated observability temporarily when runtime evidence is required.