Documentation for Jetty

CLI Advanced Usage

Advanced CLI workflows, environment variables, and troubleshooting. See CLI usage guide for basic setup and commands.

Debugging

Verbose Output

Get detailed information about what's happening:

# Debug agent WebSocket and HTTP forwarding
jetty share 8000 --debug-agent

# Or via environment variable
JETTY_SHARE_DEBUG_AGENT=1 jetty share 8000

This outputs JSON lines showing:

  • WebSocket connection events
  • HTTP requests received from the edge
  • Upstream curl attempts and responses
  • URL rewriting operations
  • Sample capture events

Debug URL rewriting:

JETTY_SHARE_DEBUG_REWRITE=1 jetty share 8000

Shows before/after for all URL rewrites in headers and response bodies.

Viewing Samples

Jetty captures request samples for inspection:

jetty list
# Shows sample counts for each tunnel

View samples in the dashboard:

  1. Open your Jetty dashboard
  2. Go to Tunnels
  3. Click on a tunnel
  4. Click Monitor to see captured requests

By default, samples are anonymized (sensitive data redacted). Disable capture:

JETTY_SHARE_CAPTURE_SAMPLES=0 jetty share 8000

Replaying Requests

Repeat a captured request against your local upstream:

jetty replay abc123sample

Safety restrictions:

  • Only GET and HEAD requests replay by default
  • Other methods require explicit permission:
JETTY_REPLAY_ALLOW_UNSAFE=1 jetty replay abc123sample

Use cases:

  • Debug intermittent webhook issues
  • Test edge cases
  • Reproduce bugs from production traffic

Advanced Workflows

CI/CD Usage

Use Jetty in automated testing pipelines:

GitHub Actions example:

name: E2E Tests
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install Jetty
        run: curl -fsSL "https://usejetty.online/install/jetty.sh" | bash
      - name: Start app
        run: npm run dev &
      - name: Share tunnel
        run: |
          jetty share 3000 --subdomain=ci-${{ github.run_id }} &
          JETTY_PID=$!
        env:
          JETTY_SERVER: ${{ secrets.JETTY_SERVER }}
          JETTY_TOKEN: ${{ secrets.JETTY_TOKEN }}
      - name: Run E2E tests
        run: npm run test:e2e
        env:
          BASE_URL: https://ci-${{ github.run_id }}.tunnels.usejetty.online
      - name: Cleanup
        if: always()
        run: kill $JETTY_PID

GitLab CI example:

e2e-tests:
  stage: test
  script:
    - curl -fsSL "https://usejetty.online/install/jetty.sh" | bash
    - npm run dev &
    - jetty share 3000 --subdomain=ci-$CI_PIPELINE_ID &
    - JETTY_PID=$!
    - npm run test:e2e
    - kill $JETTY_PID
  variables:
    JETTY_SERVER: $JETTY_SERVER
    JETTY_TOKEN: $JETTY_TOKEN
    BASE_URL: https://ci-$CI_PIPELINE_ID.tunnels.usejetty.online

Team Workflows

Shared reserved subdomains:

Create team-wide stable URLs for different purposes:

  • staging-api -- Staging API endpoint
  • demo -- Client demo environment
  • webhooks-alice -- Alice's webhook testing
  • webhooks-bob -- Bob's webhook testing

Reserve these in your dashboard, then team members can use them:

jetty share 8000 --subdomain=webhooks-alice

Collaboration tips:

  • Use descriptive subdomain names
  • Document who owns which subdomain
  • Clean up unused tunnels regularly
  • Use jetty list to see team activity

Framework-Specific Tips

Laravel:

# Start Laravel dev server
php artisan serve --port=8000

# In another terminal
jetty share 8000

# Jetty auto-detects Laravel and configures URL rewriting

Next.js:

npm run dev  # Usually port 3000
jetty share 3000

Rails:

rails server  # Usually port 3000
jetty share 3000

Vite:

npm run dev  # Usually port 5173
jetty share 5173

Django:

python manage.py runserver 8000
jetty share 8000

Docker Compose:

docker-compose up
jetty share 8080 --site=172.17.0.2  # Use container IP

Valet/Herd:

# Your app at https://my-app.test
jetty share 443 --site=my-app.test

Environment Variables

Most useful environment variables for daily work:

Authentication

# Your API token
export JETTY_TOKEN=your-token-here

# Server name
export JETTY_SERVER=your-server-name

# Full API URL (alternative to JETTY_SERVER)
export JETTY_API_URL=https://your-jetty.example

Tunnel Configuration

# Subdomain for stable URLs
export JETTY_SUBDOMAIN=my-app

# Auto-delete tunnel on exit
export JETTY_SHARE_DELETE_ON_EXIT=1

# Disable tunnel resume
export JETTY_SHARE_NO_RESUME=1

# Additional hosts to rewrite
export JETTY_SHARE_REWRITE_HOSTS=api.myapp.test,admin.myapp.test

Request Samples

# Disable sample capture
export JETTY_SHARE_CAPTURE_SAMPLES=0

# Allow unsafe method replay
export JETTY_REPLAY_ALLOW_UNSAFE=1

Debugging

# Debug agent WebSocket and HTTP
export JETTY_SHARE_DEBUG_AGENT=1

# Debug URL rewriting
export JETTY_SHARE_DEBUG_REWRITE=1

# Write NDJSON debug log
export JETTY_SHARE_DEBUG_NDJSON_FILE=/tmp/jetty-debug.ndjson

URL Rewriting

# Disable all body rewriting
export JETTY_SHARE_NO_BODY_REWRITE=1

# Disable JavaScript rewriting only
export JETTY_SHARE_NO_JS_REWRITE=1

# Disable CSS rewriting only
export JETTY_SHARE_NO_CSS_REWRITE=1

# Disable header rewriting
export JETTY_SHARE_NO_LOCATION_REWRITE=1

Notifications

# Enable Telegram notifications
export JETTY_TELEGRAM_BOT_TOKEN=your-bot-token
export JETTY_TELEGRAM_CHAT_ID=your-chat-id

For a complete list, see the Tunnels Reference.

Tips and Tricks

Shell Aliases

Add these to your ~/.zshrc or ~/.bashrc:

# Quick share on common ports
alias j8='jetty share 8000'
alias j3='jetty share 3000'
alias j5='jetty share 5173'

# Share with auto-delete
alias jshare='jetty share --delete-on-exit'

# Laravel-specific
alias jlaravel='jetty share 8000 --subdomain=laravel-dev'

# List with formatting
alias jlist='jetty list | grep -E "^(ID|http)"'

# Quick config
alias jconfig='cat ~/.config/jetty/config.json'

One-Liners

Share and copy URL to clipboard (macOS):

jetty share 8000 | grep -oE 'https://[^ ]+' | head -1 | pbcopy

Share and open in browser:

jetty share 8000 & sleep 3 && open $(jetty list | grep -oE 'https://[^ ]+' | head -1)

Share with notification when ready:

jetty share 8000 && osascript -e 'display notification "Tunnel ready!" with title "Jetty"'

Temporary 1-hour tunnel:

timeout 3600 jetty share 8000 --delete-on-exit

Productivity Tips

1. Use subdomain patterns

Create a naming convention for your team:

  • {project}-dev -- Development
  • {project}-staging -- Staging
  • {feature}-{username} -- Feature branches

2. Keep a tunnel running in tmux/screen

# Start tmux session
tmux new -s jetty

# Inside tmux
jetty share 8000 --subdomain=my-stable-tunnel

# Detach with Ctrl+B, D
# Reattach anytime with: tmux attach -t jetty

3. Project-specific configs

Create jetty.config.json in each project:

{
  "share": {
    "subdomain": "my-project",
    "delete_on_exit": true
  }
}

4. Git hooks for automatic tunnels

Create .git/hooks/post-checkout:

#!/bin/bash
if [ -f ".jetty-auto" ]; then
    jetty share 8000 --subdomain=$(git branch --show-current) &
fi

5. Use curl to test tunnel without browser

# Start tunnel
jetty share 8000 --subdomain=test

# Test from another terminal
curl -I https://test.tunnels.usejetty.online

Troubleshooting

"Tunnel unavailable" errors

Symptom: Browser shows "tunnel unavailable" even though jetty share is running.

Causes:

  1. Multiple jetty share processes for the same tunnel (since v0.1.19, a lockfile prevents this)
  2. Edge server can't reach your Jetty bridge
  3. Network issue between edge and your machine

Solutions:

# Check for duplicate processes
ps aux | grep jetty

# Kill duplicates
killall jetty

# Restart fresh
jetty share 8000

Laravel redirects to localhost

Symptom: Browser jumps from https://xyz.tunnels.usejetty.online to http://localhost:8000.

Cause: Laravel isn't respecting forwarded headers.

Fix:

  1. Trust proxies in app/Http/Middleware/TrustProxies.php:
protected $proxies = '*';

protected $headers = 
    Request::HEADER_X_FORWARDED_FOR |
    Request::HEADER_X_FORWARDED_HOST |
    Request::HEADER_X_FORWARDED_PORT |
    Request::HEADER_X_FORWARDED_PROTO;
  1. Force root URL in app/Providers/AppServiceProvider.php:
use Illuminate\Support\Facades\URL;

public function boot()
{
    if (request()->headers->has('X-Forwarded-Host')) {
        URL::forceRootUrl(request()->getSchemeAndHttpHost());
    }
}

Vite/Next.js assets not loading

Symptom: Page loads but CSS/JS return 404.

Cause: Dev server might be binding to a specific host.

Fix for Vite:

// vite.config.js
export default {
  server: {
    host: '0.0.0.0', // or true
    port: 5173
  }
}

Fix for Next.js:

# Start with -H flag
next dev -H 0.0.0.0

# Or in package.json
"dev": "next dev -H 0.0.0.0"

WebSocket connection drops

Symptom: Share command shows "WebSocket closed, retrying..."

Causes:

  1. Firewall blocking WebSocket connections
  2. Network proxy with short timeouts
  3. Edge server restarted

Solutions:

# Increase ping interval for strict proxies
JETTY_SHARE_WS_PING_INTERVAL=12 jetty share 8000

# Check edge server logs (if you operate the server)
journalctl -u jetty-edge -f

"Token invalid" errors

Symptom: jetty commands fail with authentication errors.

Check current config:

jetty config

Reconfigure:

jetty config set token new-token-here

Or use environment variable:

export JETTY_TOKEN=new-token-here
jetty share 8000

Rate limiting or body size errors

Symptom: Some requests fail with 429 or 413 errors.

Cause: Edge server limits (Sprint 2 features).

Contact your operator to adjust:

  • JETTY_EDGE_DEFAULT_RATE_LIMIT_RPS -- Requests per second per tunnel
  • JETTY_EDGE_DEFAULT_MAX_BODY_BYTES -- Maximum request body size
  • JETTY_EDGE_IP_RATE_LIMIT_RPS -- Per-IP rate limit

Stale connection ("verify rejected")

Symptom: Reconnect fails with "verify rejected" message.

Cause: Another machine attached to your tunnel, rotating the agent token.

Solution: Jetty automatically refreshes the token. If it persists:

# Delete and recreate
jetty delete tunnel-id
jetty share 8000

See Also


Need help? Check your Jetty dashboard for team-specific documentation and support resources.

Send feedback

Found an issue or have a suggestion? Let us know.