A lightweight, flexible HTTP server for local development testing. Perfect for testing webhooks, API integrations, or any application that needs to hit HTTP endpoints during development.
- Default 200 OK: Responds to all requests with 200 OK by default
- Custom Routes: Define specific behavior for certain endpoints
- Wildcard Paths: Support for path patterns like
/webhook/* - Method Matching: Optionally match specific HTTP methods (GET, POST, etc.)
- Custom Responses: Configure status codes, headers, and body content
- File or Inline Bodies: Response bodies can be inline strings or loaded from files
- Request Logging: See all incoming requests with headers, body, and query parameters
- Body Truncation: Large request bodies are automatically truncated to 1KB in logs
- Graceful Shutdown: Server shuts down gracefully on Ctrl+C, allowing in-flight requests to complete
- Request Timeouts: Built-in protection against slow clients with 15-second read/write timeouts
- Port Validation: Validates port numbers are in the valid range (1-65535)
go build -o servOr using the Makefile:
make buildStart a server that responds 200 OK to all requests on port 8080:
./serv./serv --port 3000Respond to POST requests to /webhook/github with a custom status and JSON body:
./serv --method POST \
--path "/webhook/github" \
--status 201 \
--headers "Content-Type:application/json,X-Webhook-Id:123" \
--body '{"status":"received"}'Load response body from a file:
./serv --method POST \
--path "/webhook/*" \
--status 200 \
-f --body response.jsonThe -f flag tells Serv to treat --body as a file path. The Content-Type header will be automatically inferred from the file extension (.json, .html, .txt).
Use wildcards to match multiple paths:
./serv --path "/api/*/callback"This matches:
/api/users/callback/api/users/v1/callback/api/orders/callback
Suppress request/response logging:
./serv --quiet# Start Serv to accept GitHub-style webhooks
./serv --method POST \
--path "/webhook/github" \
--status 201 \
--headers "Content-Type:application/json" \
--body '{"status":"success","message":"Webhook received"}'
# In another terminal, send a test webhook
curl -X POST http://localhost:8080/webhook/github \
-H "Content-Type: application/json" \
-d '{"event":"push","repository":"myrepo"}'# Start Serv with a wildcard route
./serv --path "/api/*" \
--status 200 \
-f --body api-response.json
# Test different endpoints - all will use the same response
curl http://localhost:8080/api/users
curl http://localhost:8080/api/orders
curl http://localhost:8080/api/products/123If a request doesn't match your custom route, Serv returns 200 OK:
# Define custom route for /webhook/*
./serv --path "/webhook/*" --body "Webhook received"
# This matches the custom route
curl http://localhost:8080/webhook/test
# Response: Webhook received
# This doesn't match, falls back to default
curl http://localhost:8080/other/path
# Response: OKWhen not in quiet mode, Serv logs all incoming requests:
[2025-12-14 10:30:15] POST /webhook/test?foo=bar&id=123
Headers:
Content-Type: application/json
User-Agent: curl/8.7.1
Body (38 bytes):
{"event":"user.created","user_id":456}
Response: 200 OK
---
Request bodies larger than 1KB are truncated in the logs.
Serv handles shutdown signals (Ctrl+C, SIGTERM) gracefully:
- When you stop the server, it will display "Shutting down server..."
- In-flight requests are given up to 5 seconds to complete
- New connections are rejected immediately
- Once all requests complete (or the timeout expires), the server exits cleanly
This ensures that webhook deliveries or API requests in progress aren't interrupted abruptly.
For custom responses, only these content types are supported:
text/*(text/plain, text/html, text/css, etc.)application/json
This is by design to keep Serv focused on API and webhook testing.
Serv validates configuration at startup and provides clear error messages:
# Invalid port number
./serv --port 99999
Error: configuration error: invalid port: 99999 (must be 1-65535)
# Invalid header format
./serv --headers "Content-Type"
Error: configuration error: invalid header format: "Content-Type" (expected Key:Value, got 1 parts after splitting on ':')
# Invalid status code
./serv --status 999
Error: configuration error: invalid status code: 999 (must be 100-599)| Flag | Description | Default |
|---|---|---|
--port |
Port to listen on | 8080 |
--method |
HTTP method for custom route | (any) |
--path |
Path pattern for custom route (supports wildcards) | (any) |
--status |
HTTP status code for custom response | 200 |
--headers |
Response headers as "Key:Value,Key2:Value2" | (none) |
--body |
Response body (inline string or file path) | (none) |
-f, --file |
Treat --body as file path | false |
--quiet |
Suppress request/response logging | false |
go test -v ./...make devmake buildMIT