Tunnels
Inspect requests and usage
Follow live requests, find failing ones, capture text bodies on purpose, and read retention and offline gaps correctly.
Follow live requests
#Open a tunnel to see Live requests, newest first. On a phone, open the Requests tab. Filter by method, path or status, or by status class such as 5xx, and select a row to see its details.
Pause freezes the rows on screen while traffic keeps flowing to your app. Resume shows the requests that arrived meanwhile.
If your workspace membership is removed or the workspace is suspended, the feed stops receiving new rows. Rows already on screen remain there; they do not give you continuing access to saved request details.
Request feeds opened with a CLI sign-in session also stop when that session expires or you run groundhog logout. Sign in again with groundhog login to start a new session.
Search a workspace’s requests
#Choose Search (Ctrl K, or ⌘K on a Mac) to find saved requests across every tunnel in the workspace by path, method, request ID or status. For example, POST /webhooks 5xx finds failed webhook deliveries.
Search covers roughly the last hour and reads a limited number of saved requests, so in a busy workspace it can miss older matches.
Read a request’s details
#Details show headers, timings, sizes, the host, the visitor’s address and a request ID. Match the X-Groundhog-Request-Id header to your app’s logs.
Paths are saved without their query strings. A WebSocket appears once it closes, with status 101 and how long it was open.
Capture only the bodies you need
#Body capture is off by default. An owner or the tunnel’s creator can turn on Keep bodies in the header of the tunnel’s Live requests feed. It applies to new requests only.
Groundhog keeps the start of each text body, 32 KB by default, for 1 hour. Larger bodies still reach your app in full, and the details mark the saved part as truncated. Binary bodies are measured but not kept.
The inspector decodes complete gzip, Brotli (br), and deflate captures, then shows at most the same size limit of decoded text. Sizes describe bytes on the wire, so a compressed body can expand into a truncated text preview. Complete valid JSON is formatted for reading.
UTF-8 is the default character set. A declared UTF-16, ASCII, or ISO-8859-1 character set is also supported. Unsupported encodings or character sets, malformed data, and incomplete compressed captures show an explanation instead of scrambled text. Use a smaller sample or turn compression off in your app while debugging; forwarding still sends the original bytes.
Read retention and connection gaps correctly
#| Record | Kept for | What to know |
|---|---|---|
| Request details and saved bodies | 1 hour by default | They survive a service restart, then expire. A row in a paused feed can outlive its details. |
| Connection history | 30 days | Shaded spans show recorded offline periods, not zero traffic. History can be incomplete after storage failures or heavy reconnect activity. |
| Daily usage for the workspace and each tunnel | 30 days | Days and months are in UTC. Workspace totals include tunnels you have since deleted. |
Expired details can’t be opened, and a link or a paused feed does not keep them longer.
Connection history is best effort. Heavy reconnect activity or a storage outage can lose observations before they are saved. Missing connection history cannot be recovered from the chart; check your client’s logs when investigating a gap.
A tunnel’s Activity charts show the last 24 hours by default. Choose Last hour, 3 days, 7 days or 30 days above the charts to change the window; the choice stays in the page address, so a refresh or a shared link keeps it. Longer windows add requests into wider bars, for example an hour each for 7 days. The figures above the charts, such as Requests, last hour and its 5xx share, don’t change with the window.
Traffic charts are best effort too. Heavy traffic across many tunnel names or a storage outage can lose pending counters. A retry after a lost storage response can count some traffic twice. Charts are useful for diagnosis; they are not an exact billing record. Configured monthly allowances use separate accounting.
The live feed keeps up to 200 recent requests for a tunnel. Shared history limits and request retention can shorten that list. If live updates reach stream capacity, close extra tunnel tabs or clients and retry shortly. Renaming or deleting a tunnel closes its existing live streams. Use request search to find retained details independently of the live feed.
On Usage, This month, by tunnel shows each tunnel’s data totals. On a narrow screen, use Tab to focus the tunnel usage table, then the Left and Right arrow keys to reach the columns outside the screen.
Limits shows your tunnel count against its ceiling and the service’s enforced ceilings for requests per minute, requests in progress, client connections and request body size. Open streams and WebSockets count as requests in progress. Each client’s pool uses client-connection slots until its sockets finish closing. Shared service limits can also refuse traffic; these ceilings do not reserve capacity or promise throughput. If limits cannot be loaded, the page shows an error instead of treating them as unlimited.
If the operator configures monthly allowances, Limits also shows the requests and data counted against them for the UTC calendar month, with a bar that turns amber at 80% and red when the allowance is reached. Accounting begins while an allowance is configured; earlier traffic is not added retroactively. Failed target requests can consume admissions, and failed transfers can consume data charges. These counters differ from the traffic charts. A reached request allowance returns 429; a data allowance can interrupt an open response or WebSocket. Ask the service operator to review your limits.
Know the forwarding limits
#Groundhog has no monthly data or request cap; Usage is a record, not a quota. To keep the shared service fair, it enforces these limits:
| Limit | Value |
|---|---|
| Stored tunnels | 50 per workspace |
| New requests per minute | 6,000 per workspace, and 30,000 across the service |
| Requests in progress at once | 200 per tunnel and per workspace, and 1,000 across the service |
| Client connections | 256 per workspace, and 2,048 across the service |
| Request body size | 100 MB |
| Wait for your app to start a response | 100 seconds |
A request over a per-minute limit gets a 429 response with a Retry-After header, and a request over an in-progress limit gets a 503 busy response; see the public address does not serve my app. Streaming responses and open WebSockets count as requests in progress until they end, but the 100-second wait doesn’t apply to them. Limits on the Usage page shows the limits that apply to your workspace.
Tell app errors from forwarding errors
#- local-unreachable: the client could not reach your app on your computer. Check the app is running on the port or address the client uses.
- timeout: your app did not start a response in time.
- Any other 4xx or 5xx: your app sent it. Investigate it in your app.
Start with the local address, then the client and the public address, then match the request ID in your logs. Continue with Configure callbacks, hosts and HTTPS or Troubleshooting your connection.