Traffic Inspection
A comprehensive guide to using Jetty's request capture, traffic inspector, and replay features for debugging webhooks, inspecting API traffic, and reproducing production issues locally.
Overview
What is Traffic Inspection?
Traffic inspection captures metadata about HTTP requests flowing through your Jetty tunnels and displays them in a searchable dashboard. Each time a request passes through your tunnel, the CLI can optionally send a redacted summary to the Bridge, allowing you to review traffic patterns, debug webhooks, and replay requests against your local environment.
What Gets Captured
For each request, Jetty stores:
- Method (GET, POST, PUT, etc.)
- Path and query string
- HTTP status code from your local app
- Bytes in / bytes out (request and response sizes)
- Redacted headers (based on your redaction tier)
- Timestamp when the request was captured
Important: Request and response bodies are never stored. Only metadata is captured for inspection.
Why Use Traffic Inspection?
Webhook Debugging
- See exactly when GitHub, Stripe, or other services send webhooks
- Review headers and query parameters without tailing logs
- Replay webhooks locally to debug handler logic
API Testing
- Watch requests from mobile apps or third-party integrations
- Validate headers and status codes
- Share traffic snapshots with teammates via read-only links
Client Demos
- Show exactly what traffic your integration receives
- Copy
curlcommands to reproduce requests - Filter by path prefix to isolate specific endpoints
Production Issue Reproduction
- Capture failing requests in staging/production tunnels
- Replay them against local development to debug
- Review timing and response size patterns
Plan Requirements
Traffic inspection is available on paid plans only:
| Plan | Traffic Inspection | Request Samples | Replay |
|---|---|---|---|
| Dinghy (Free) | Not available | N/A | N/A |
| Captain (Coastal) | Full access | 50 per tunnel | Yes |
| Fleet | Full access | 50 per tunnel | Yes |
On the Dinghy plan, the dashboard displays an upgrade prompt where the traffic inspector would appear.
Accessing the Traffic Inspector
From the Dashboard
- Navigate to Dashboard → Tunnels (or the main dashboard home)
- If you have multiple tunnels, you'll see a list. Click on a tunnel to open the detail view.
- The "Recent requests & inspection" panel appears below the tunnel details.
The inspector updates automatically (live polling every ~8 seconds when the page is open).
Direct Tunnel URLs
You can also access a specific tunnel's inspector at:
https://your-jetty-domain.com/dashboard/tunnels/{tunnel-id}
Bookmark these URLs to quickly jump to high-traffic tunnels.
Understanding the Inspector Interface
Request List View
The traffic inspector displays recent requests in a table with the following columns:
| Column | Description |
|---|---|
| Time | Captured timestamp (app timezone, format: MM-DD HH:MM:SS) |
| Method | HTTP method (GET, POST, PUT, DELETE, etc.) |
| Path | Request path, truncated if long (hover to see full path) |
| Status | HTTP status code returned by your local app |
| In / Out | Request size / response size (e.g., 1.2 KB / 45 KB) |
| Actions | "Copy curl" button to copy a curl command |
Path Prefix Filter
Above the request table, you'll find a path filter input:
Filter by path prefix (e.g. /webhooks)
Type a path prefix to show only requests matching that prefix. This is useful for:
- Isolating webhook endpoints (
/webhooks/stripe,/api/github) - Focusing on specific API versions (
/v2/users) - Debugging a single route during development
The filter applies as you type (debounced ~300ms) and updates the table in real-time.
QR Code
Each tunnel displays a QR code next to its public URL in the dashboard. The QR code encodes the full tunnel URL, making it easy to open the tunnel on a mobile device without typing the address.
- No setup required -- the QR code appears automatically for every tunnel.
- Click to enlarge -- click the QR code to open a larger version for easy scanning from across the room.
- Use cases -- testing mobile apps, responsive designs, or sharing a tunnel URL during in-person demos without dictating a URL.
Read-Only Observer Links
When viewing a tunnel with samples, a signed URL appears that you can copy and share with teammates. This link:
- Shows the same traffic table (read-only, no actions)
- Expires after a set time (signed URL security)
- Respects your path filter (if applied when generating the link)
- Does not require the recipient to log in
Use case: Share webhook traffic with a backend engineer who doesn't have dashboard access, or send a traffic snapshot to a client for debugging.
Request Samples
Storage Limits
Each tunnel stores up to 50 request samples by default. When the 51st request arrives, the oldest sample is automatically deleted (FIFO rotation).
Customization (operators only):
- Global default:
JETTY_TUNNEL_REQUEST_SAMPLES_MAX=50in Bridge config - Per-organization override: Settable via the organization settings (requires database access)
Sample Rotation
Samples are pruned automatically after each new request is stored:
- CLI posts a new sample via
POST /api/tunnels/{id}/request-samples - Bridge stores the sample
- Bridge queries the tunnel's samples, sorted by
captured_atdescending - Any samples beyond the limit (default 50) are deleted
This ensures the inspector always shows the most recent requests, not the oldest.
Retention Policy
Samples are stored indefinitely until rotated out by newer requests or the tunnel is deleted. There is no automatic time-based expiration (e.g., "delete after 7 days").
Deleting samples:
- Delete the tunnel → all samples are cascade-deleted
- No manual "clear samples" button exists (request rotation handles cleanup)
Opting Out of Capture
To disable request sample capture entirely, set this environment variable before running jetty share:
JETTY_SHARE_CAPTURE_SAMPLES=0 jetty share 8000
The tunnel will function normally, but no traffic metadata is sent to the Bridge. This is useful for:
- Privacy-sensitive local development
- High-traffic tunnels where you don't need inspection
- Reducing API calls to the Bridge
Header Redaction
Request headers often contain sensitive credentials. Jetty redacts headers before storing samples to prevent accidental exposure in the dashboard.
Redaction Tiers
There are two redaction tiers: Standard and Strict.
Standard Tier (Default)
Always blocks these headers:
AuthorizationCookie/Set-CookieX-Api-KeyX-Auth-TokenWWW-Authenticate- Any header starting with
Proxy-(e.g.,Proxy-Authorization)
When to use: Most development and staging environments. Protects common authentication mechanisms without being overly restrictive.
Strict Tier
Blocks everything in Standard, plus:
X-CSRF-Token/X-XSRF-TokenX-Session-IDX-Forwarded-AuthorizationX-AMZ-Security-Token(AWS STS tokens)- Any operator-defined headers (via
JETTY_TUNNEL_REQUEST_SAMPLE_HEADER_BLOCKLIST_EXTRA)
When to use:
- Production tunnels (if you use them for live traffic)
- Compliance requirements (GDPR, SOC 2, etc.)
- CI/CD tokens that may expose session tokens
How Redaction Works
- Your CLI captures the request headers
- Before sending to the Bridge, the CLI includes all headers in the API call
- The Bridge (not the CLI) applies redaction based on:
- Your API token's redaction tier (if set)
- Your organization's default tier (fallback)
- Redacted headers are not stored in the database
Important: Redaction happens on the server. The CLI always sends full headers; the Bridge strips sensitive ones before persisting.
Configuring Redaction
Organization-Level (Default for All Tokens)
- Go to Dashboard → Organization → Settings
- Find the "Request header redaction" section
- Choose Standard or Strict
- Click Save
All tokens in the organization inherit this tier unless overridden.
Token-Level Override
When creating or editing an API token:
- Go to Dashboard → Tokens
- Click "Create token" (or edit an existing one)
- Expand "Header redaction tier (optional override)"
- Choose:
- Use crew default (inherits organization setting)
- Standard (override to standard, even if org uses strict)
- Strict (override to strict, even if org uses standard)
- Click Create token / Save
Use case: Create a "production CI" token with Strict redaction, and a "local dev" token with Standard to see more headers during debugging.
Viewing Current Settings
In Dashboard:
- Organization tier: Dashboard → Organization → Settings
- Token tier: Dashboard → Tokens (look for badge/label on each token)
In Database (operators):
-- Check organization default
SELECT tunnel_request_sample_redaction_tier FROM organizations WHERE id = 1;
-- Check token overrides (NULL = inherits from org)
SELECT name, tunnel_request_sample_redaction_tier FROM personal_access_tokens;
Header Value Truncation
Even non-redacted headers have their values truncated to prevent storing overly large headers:
- Default max length: 200 characters per header value
- Configurable via
JETTY_TUNNEL_REQUEST_SAMPLE_HEADER_VALUE_MAX_LENGTH(1–4096)
Long headers (e.g., verbose User-Agent strings) are truncated with ... at the end.
Using Replay
The replay feature re-executes a captured request against your local upstream (not the public tunnel). This lets you reproduce production/staging traffic in your development environment.
From the Dashboard
- Open the traffic inspector for a tunnel
- Find the request you want to replay in the table
- Click "Copy curl" to copy a
curlcommand that hits the public tunnel URL
Example copied command:
curl -X POST 'https://my-tunnel.tunnels.usejetty.online/webhooks/stripe?foo=bar'
This sends a new request through the tunnel to your local app. It does not replay the original request data (bodies are not stored).
From the CLI
For a more powerful replay that uses the original request metadata, use:
jetty replay <sample-id>
What this does:
- Fetches the sample from the Bridge (
GET /api/tunnel-request-samples/{id}) - Reads the tunnel's
local_hostandlocal_portfrom the sample metadata - Sends a request directly to your local app (bypasses the public tunnel)
- Prints the response body and HTTP status
Example:
$ jetty replay 123
{"status":"ok","user_id":456}
HTTP 200
Safety: GET and HEAD Only (by Default)
By default, replay only allows GET and HEAD requests to prevent accidental data mutation. If you try to replay a POST, PUT, or DELETE:
$ jetty replay 789
Replay only allows GET/HEAD by default. Set JETTY_REPLAY_ALLOW_UNSAFE=1 to replay other methods.
To enable unsafe methods:
JETTY_REPLAY_ALLOW_UNSAFE=1 jetty replay 789
Warning: This will re-execute the request (create a database record, send an email, charge a card, etc.). Use with caution in production data environments.
Replay vs. Curl Copy
| Feature | CLI Replay (jetty replay) |
Dashboard Curl Copy |
|---|---|---|
| Target | Direct to local_host:local_port |
Through public tunnel URL |
| Request data | Uses original path/query from sample | Empty request (no body) |
| Headers | Not replayed (redacted/not stored) | Not included |
| Body | Not replayed (not stored) | Not included |
| Use case | Fast local debugging of GET endpoints | Sending a new request through the tunnel |
Use Cases and Workflows
Debugging Stripe Webhooks
Problem: Stripe sends a webhook, but your handler crashes. You want to reproduce it locally.
Workflow:
- Open the tunnel inspector
- Filter by path:
/webhooks/stripe - Find the failing request (look for 5xx status or recent timestamp)
- Click "Copy curl" and run it in your terminal to send a new request
- Or use
jetty replay <id>to hit your local app directly (note: webhook signature validation will fail because the body isn't replayed)
Tip: For full webhook replay with body and signatures, use Stripe's webhook testing feature or capture the raw payload separately.
Monitoring Mobile App API Traffic
Problem: Your mobile app is making unexpected API calls, and you want to see what endpoints it hits.
Workflow:
- Point your mobile app to a Jetty tunnel URL
- Use the app for a few minutes
- Open the inspector and review the request list
- Look for unexpected methods or paths
- Share the read-only observer link with your mobile engineer
Reproducing a Production Issue
Problem: A production API call fails intermittently. You want to debug it locally.
Workflow:
- Run
jetty shareon your production server (or staging with production data) - Wait for the failing request to occur
- Check the inspector for a 4xx or 5xx status
- Copy the sample ID from the table (hover over the row, or check the URL when drilling down)
- On your local development machine, run:
jetty replay <sample-id> - Step through the code with a debugger to find the issue
Note: This assumes the failing endpoint is a GET or idempotent POST. For destructive operations, use a staging database.
Sharing Traffic with Clients
Problem: A client reports that their integration "isn't working," but you can't reproduce it.
Workflow:
- Ask the client to hit your tunnel URL
- Open the inspector and filter by their expected path (e.g.,
/api/oauth/callback) - See if their request appears (if not, they're hitting the wrong URL)
- Check the status code and bytes in/out to diagnose (e.g., 401 = bad auth, 0 bytes in = they're not sending a body)
- Copy the read-only observer link and send it to the client
- They can see the same table without logging in to your dashboard
Best Practices
1. Use Traffic Inspection for Debugging, Not Monitoring
The inspector is designed for development and debugging, not production monitoring. For production:
- Samples rotate after 50 requests (may lose important traffic)
- No alerting or long-term storage
- Bodies are not captured
Instead, use:
- Structured logging (
jetty sharecan append NDJSON logs viaJETTY_SHARE_DEBUG_NDJSON_FILE) - Application Performance Monitoring (APM) tools
- Dedicated webhook inspection services (for webhooks)
2. Understand Redaction Trade-offs
- Standard tier is fine for most dev/staging work
- Strict tier may hide headers you need for debugging (e.g., CSRF tokens, session IDs)
- If debugging auth issues, temporarily switch to Standard or check your app logs instead
Remember: Redacted headers are gone forever. You can't "unhide" them later.
3. Use Path Filters to Reduce Noise
High-traffic tunnels can generate hundreds of samples quickly. Use path filters to:
- Focus on webhook endpoints (
/webhooks/*) - Isolate failing routes (
/api/v2/users) - Hide health checks (
/health,/ping)
4. Opt Out for High-Traffic or Sensitive Tunnels
If you're tunneling:
- Production traffic (high volume)
- Sensitive data (even with redaction, metadata may be revealing)
- Internal tools where inspection isn't needed
Set JETTY_SHARE_CAPTURE_SAMPLES=0 to disable capture entirely.
5. Sample Limits and Rotation
With a 50-sample limit:
- Low-traffic tunnels: Samples may stay for hours or days
- High-traffic tunnels: Samples rotate in minutes
If you need a specific request, act fast or increase the limit (organization setting, if your operator allows).
6. Replay Safety
- Only replay GET/HEAD by default to avoid accidental mutations
- For
POST/PUT/DELETE, useJETTY_REPLAY_ALLOW_UNSAFE=1only if:- You're testing against a local database (no production data)
- The operation is idempotent
- You understand the side effects
Troubleshooting
No Samples Appearing
Symptoms: The inspector shows "No samples yet" even though traffic is flowing through the tunnel.
Possible causes:
-
Capture is disabled
- Check: Did you set
JETTY_SHARE_CAPTURE_SAMPLES=0? - Fix: Remove the env var and restart
jetty share
- Check: Did you set
-
Plan restriction
- Check: Are you on the Dinghy (free) plan?
- Fix: Upgrade to Captain or Fleet under Dashboard → Billing
-
CLI version too old
- Check: Run
jetty --version(request capture added in CLI v0.1.x) - Fix: Run
jetty updateto upgrade to the latest version
- Check: Run
-
Request hasn't reached the tunnel yet
- Check: Did the request actually hit the public tunnel URL? (Check CLI logs for
HTTP 200lines) - Fix: Confirm the URL is correct and the tunnel is running
- Check: Did the request actually hit the public tunnel URL? (Check CLI logs for
-
API token lacks permissions
- Check: Does your token belong to the same organization as the tunnel?
- Fix: Recreate the token under the correct organization
Samples Rotating Too Quickly
Symptoms: You see a request in the inspector, refresh the page, and it's gone.
Cause: The tunnel is receiving more than 50 requests, so old samples are pruned.
Fixes:
- Increase the limit (organization setting): Ask your organization admin to raise
tunnel_request_samples_max(requires database access) - Use path filters: Filter to the specific endpoint you care about (reduces noise)
- Capture traffic externally: Use
JETTY_SHARE_DEBUG_NDJSON_FILEto log all requests to a file
Replay Fails with "tunnel unavailable"
Symptoms: jetty replay <id> returns an error about the tunnel being unreachable.
Possible causes:
-
Tunnel is stopped
- Check: Is
jetty sharestill running? - Fix: Start the tunnel again (replay will work if the sample's
local_host:local_portis the same)
- Check: Is
-
Different machine
- Check: Are you running
jetty replayon a different machine than wherejetty shareran? - Fix: Replay only works if you can reach the
local_host:local_portin the sample (usually127.0.0.1:8000)
- Check: Are you running
-
Port changed
- Check: Did you restart
jetty shareon a different port? - Fix: The sample stores the original port. Either use the same port or fetch a new sample
- Check: Did you restart
Headers Missing in Inspector
Symptoms: You expect to see a custom header (e.g., X-My-Api-Key), but it doesn't appear.
Possible causes:
-
Redaction tier blocking it
- Check: Is the header name in the
strict_extra_blocklist? (See config file) - Fix: Switch to Standard tier, or ask your operator to remove it from the blocklist
- Check: Is the header name in the
-
Header not sent by client
- Check: Did the client actually send the header? (Check client-side code/network inspector)
- Fix: Debug the client, not the tunnel
-
Header value truncated
- Check: Is the value very long (>200 characters by default)?
- Fix: The value is truncated with
...but should still appear
"Request inspection not included on your plan"
Symptoms: The API returns a 403 error or the dashboard shows an upgrade prompt.
Cause: Your organization is on the Dinghy (free) plan, which doesn't include traffic inspection.
Fix: Upgrade to Captain or Fleet:
- Go to Dashboard → Billing
- Click "Upgrade to Captain" or "Upgrade to Fleet"
- Complete checkout
- Return to the tunnel inspector (should now work)
Related documentation
- Sharing local sites -- run
jetty shareand expose your local app - Tunnels reference -- advanced tunnel features and operator settings
- Routing rules -- route requests to different local upstreams
- Tunnel settings -- rate limits, basic auth, redaction, and webhook signature verification
- Billing and subscriptions -- plan details and upgrade instructions
Summary
Traffic inspection gives you powerful debugging tools for webhook handlers, API integrations, and client demos. Key takeaways:
- 50 request samples per tunnel (rotates automatically)
- No bodies stored, only metadata (method, path, headers, status, sizes)
- Header redaction protects sensitive credentials (Standard or Strict tier)
- Replay lets you re-execute GET/HEAD requests against your local app
- Available on Captain and Fleet plans (not Dinghy)
- Opt out with
JETTY_SHARE_CAPTURE_SAMPLES=0if needed
Use the inspector to speed up webhook debugging, validate integrations, and share traffic snapshots with your team.
Send feedback
Found an issue or have a suggestion? Let us know.