Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Server Laravel Package

exposedev/server

exposedev/server is the server-side component of Expose, the open-source PHP tunneling service. Run your own tunnel server to securely expose local Laravel or PHP applications to the web, with WebSocket-based connections and a self-hosted alternative to ngrok.

View on GitHub
Deep Wiki
Context7

Getting Started

exposedev/server is the self-hosted relay half of Expose — the public-facing service that terminates TLS, accepts tunnel connections from clients, and forwards HTTP/WebSocket traffic back to a developer's machine. A Laravel developer touches it in two ways: as a consumer (pointing the client at the hosted or company server) and as an operator (running the server so the team doesn't depend on a third-party tunnel).

Minimal path:

# operator side
composer global require exposedev/server
expose serve            # first run writes config + SQLite state, prints dashboard URL

# developer side (Laravel app)
composer require --dev exposedev/laravel-expose   # the client/artisan bridge
php artisan expose

Look first at, in order:

  1. The generated config file — host, port, dashboard subdomain, certificate authority paths, database path, and the TLS/ACME block. Everything else is downstream of these.
  2. The dashboard — user accounts, per-user auth tokens, and reserved subdomains. Tokens issued here are what clients authenticate with.
  3. The client contract — server_host, server_port, and the auth token on the client side must match the server; most "it doesn't connect" reports are a mismatch here.

First use case: php artisan expose against php artisan serve / Herd / Valet, paste the resulting https://<subdomain>.<your-server> into Stripe, GitHub, or Slack, and iterate on the webhook handler on your local machine with a real TLS endpoint.

Implementation Patterns

Per-developer subdomains, server-issued tokens. Create a dashboard user per developer rather than sharing one token. Stable reserved subdomains (--subdomain=alice) mean webhook URLs in provider dashboards don't churn, and revocation is per-person.

Team server as infrastructure. Run expose serve under systemd/supervisor, persist the SQLite database and the certificate storage directory on a volume, and back them up. Treat the process as stateful — losing the cert store means re-issuing every certificate.

Two TLS topologies, pick one deliberately. Either the Expose server terminates TLS itself (needs 443 plus the tunnel port reachable, and its own cert story), or you front it with nginx/Caddy and disable the built-in TLS. Don't run both — double termination produces confusing redirect and header behaviour.

On-demand certificates (3.2.0). The server can now request certificates on demand for its own infrastructure hostnames (dashboard, API host), not just for client-submitted custom domains. Practical pattern: DNS for expose.example.com (dashboard) and *.tunnel.example.com (client subdomains) points at the server, on-demand issuance is enabled, and the allowlist/ask-style gate covers exactly those names. This removes the manual step of provisioning the dashboard cert before the dashboard is usable over HTTPS.

Config via environment in deployment. Keep the checked-in config file as defaults and override host/port/ACME contact/CA paths through env vars in the deploy manifest, so staging and production differ only in env.

Webhook fidelity. The tunnel is a byte-level proxy — raw bodies and headers arrive intact, so Stripe-Signature / X-Hub-Signature-256 verification keeps working. Do not enable any request-rewriting middleware on the tunnel path.

Custom domains per client. When a developer needs hooks.acme-client.test to hit their app, register it as a custom domain, point DNS at the server, and let on-demand issuance cover it. Keep this capability gated to trusted users only.

Gotchas and Tips

  • On-demand issuance is a rate-limit and abuse surface. Let's Encrypt enforces roughly 50 certificates per registered domain per week and 5 duplicate certificates per week. A typo'd infra hostname, or a restart loop that re-requests on every boot, can burn the weekly quota and take the dashboard down. Persist and reuse the certificate storage directory; only fall through to issuance on a cache miss.
  • Never issue on-demand for arbitrary Host headers. Without a strict allowlist (or an ACME ask endpoint that validates the requested name against your known hosts), anyone who points DNS at your IP can obtain a certificate from your server and borrow its IP reputation. The 3.2.0 change widens issuance to server-owned hosts — verify the gate covers those names too and isn't just "any host that resolves here".
  • validate_client_ips behind a proxy. If the server sits behind a load balancer, forwarded-header trust must be configured or every client is rejected with a validation error. Symptom is a clean connect then immediate close.
  • Self-signed CA trust. Testing from a phone or a native app requires installing the exported CA. Outbound HTTP from your Laravel app (Guzzle, Http:: calls) to the public tunnel URL will fail on TLS unless the CA bundle is trusted — usually better to call localhost directly and use the tunnel only for inbound.
  • Config changes need a restart. The long-running server does not hot-reload; restart after editing config, and confirm you're not looking at a cached copy.
  • SQLite contention. Single-file dashboard DB with concurrent tunnel workers is fine at team scale, but don't run multiple server instances against the same file over a network mount.
  • Debugging order: server log verbosity first, dashboard connection state second, then openssl s_client -connect host:443 -servername <subdomain> to separate DNS/cert problems from tunnel problems. A working TLS handshake with a 502 means the client disconnected, not that the server is broken.
  • Upgrade note: 3.2.0 is a purely additive minor release (no breaking config changes, no deprecations) over 3.1.x — pin ^3.2 and restart the service after upgrading so the new issuance path is loaded.
  • Extension points: custom user/auth resolution, custom certificate handling, and the ACME challenge handler are the three seams worth knowing; if your organisation has an internal CA or an existing ACME account, hook it there rather than patching the issuance code.
  • Don't run this on a laptop in production. The server is internet-facing by definition — disable open dashboard registration, keep it behind auth, and firewall the tunnel port to expected sources where possible.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib