A service to help test the behaviour of HTTP clients like browsers, libraries, developer tools or anything else. Inspired by httpbin. Built because httpbin lacked some things I needed, like:
/mix endpoint, and the Mixer, with powerful ingredients like:
slack directive.
t directive.
/run endpoint, and the Runner. (Beta).
x-www-form-urlencoded payloads.
/get/ works just like /get.
--root-is-any enabled.
★ Star this project on GitHub.
/mix/s=400/h=x-custom-header:some-valueThe
s= and h= are directives that /mix understands. Supported directives are:
s: HTTP response status code.
h: Set a response header, in the form name:value.
c: Set a cookie, in the form name:value.
cd: Delete a cookie with the given name.
r: Set a redirect URL. Uses status code 307. To change, use s= directive.
b64: Set the response body to the base64 decoded value.
d: Delay the response by the given number of seconds, up to 10.
t: The base64 decoded value of this, is rendered as a Golang text template, and the result is
used as the response body.
end: Takes no value, marks the end of directive processing. Path segment after this is ignored
by Httpbun.
When using r or a Location header, relative redirect targets continue to work, but
absolute targets are restricted to http/https URLs on an allowlist. By default this
includes httpbun.com and example.com. When self-hosting, set
HTTPBUN_ALLOWED_REDIRECT_DOMAINS to a comma- or whitespace-separated list like
example.com httpbun.com *.github.io.
Note: Status code 101 and the Connection / Upgrade
headers are hop-by-hop semantics that reverse proxies (like the one in front of
httpbun.com) handle per HTTP spec — they will be stripped or rewritten by the proxy.
To test upgrade-like responses, run a local instance (docker run -p 80:80 sharat87/httpbun)
and hit it directly without a proxy in front.
curl https://httpbun.com/get
curl -X POST -d 'one=1' https://httpbun.com/post
curl -X POST -d '{"one": 1}' -H 'content-type:application/json' https://httpbun.com/post
curl -X PUT https://httpbun.com/put
curl -X PATCH https://httpbun.com/patch
curl -X DELETE https://httpbun.com/delete
/any is also accepted.headers which is an object of all the headers in
the request, as keys and values. If a header repeats in the request, its value is an array of all the values
it was given.
curl -H 'x-custom: custom header value' https://httpbun.com/headers
Content-Type header as the request and the body of the request as is.
Only the first 10,000 bytes of the body are read, so larger bodies are truncated. This applies to all other
endpoints that echo the request body too, like /any.
curl -H 'Content-Type: text/plain' -d 'plain body' https://httpbun.com/payload
curl -H 'Content-Type: application/json' -d '{"a": 1}' https://httpbun.com/payload
username and password as the credentials.
curl -H 'Authorization: Basic c2NvdHQ6dGlnZXI=' https://httpbun.com/basic-auth/scott/tiger
Authorization header in the request, that takes the
form Bearer expectedToken. If no Authorization header is present in the request, or
the token doesn't match, this results in a 401 response. The expectedToken is required, and
/bearer without it responds with a 404, and a hint on how to use it.
curl -H 'Authorization: Bearer expected_token' https://httpbun.com/bearer/expected_token
/digest-auth/auth/scott/tiger requires to be authenticated with
the credentials scott and tiger as username and password. The implementation is based
on this example from Wikipedia. The value of qop can be one of auth
(default), auth-int or auth,auth-int.
response_type: Must be code.client_id: Your application's client ID (any non-empty string).redirect_uri: The URI to redirect to after authorization.state: An opaque value to maintain state between request and callback.scope: Requested scope (displayed on consent page, not validated).redirect_uri with code and state parameters.
On denial, redirects with error=access_denied.
Absolute redirect_uri values are currently restricted to http/https URLs on
an allowlist. By default this includes httpbun.com and example.com. When self-hosting,
set HTTPBUN_ALLOWED_REDIRECT_DOMAINS to a comma- or whitespace-separated list like
example.com httpbun.com *.github.io.
https://httpbun.com/oauth2/authorize?response_type=code&client_id=my-app&redirect_uri=https://example.com/callback&state=abc123
grant_type: Must be authorization_code.code: The authorization code received from the authorize endpoint.client_id: Your application's client ID (must match the authorization request).client_secret: Your application's client secret (any non-empty string).redirect_uri: Must match the redirect_uri from the authorization request.access_token, token_type, and expires_in.
curl -X POST https://httpbun.com/oauth2/token \ -d 'grant_type=authorization_code' \ -d 'code=AUTH_CODE_HERE' \ -d 'client_id=my-app' \ -d 'client_secret=my-secret' \ -d 'redirect_uri=https://example.com/callback'
# Response:
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600
}
Authorization header.
email.
curl -H 'Authorization: Bearer ACCESS_TOKEN_HERE' https://httpbun.com/oauth2/userinfo
# Response:
{
"email": "user@example.com"
}
origin, with the client's IP Address for value.
If-Modified-Since or If-None-Match header, returns a 304
response. Otherwise, it behaves the same as /get for GET requests,
/post for POST requests, etc.
Cache-Control header for age seconds.codes. It can be a comma-separated list of multiple
status codes, of which a random one is chosen for the response.
/response-headers?one=two, there is a header called One, whose value is
two.
The response body contains all the headers again, in the form of a JSON object. (This JSON object in the
response should be considered deprecated, and may be removed in the future.)
If you set a Location header here, relative values are allowed, but absolute values are restricted to
http/https URLs on httpbun.com and example.com.
text. The first two letters of the
text are also shown at the center of the circle. Examples:
svg/bun,
svg/foo.
encoded text with base64 encoding scheme. Both the standard alphabet (with
+ and /) and the URL-safe one (with - and _) are accepted,
with or without the trailing = padding. Defaults to SFRUUEJVTiBpcyBhd2Vzb21lciE=.
count random bytes in the response. The Content-Type header is set to
application/octet-stream. The randomness is not cryptographically secure.
The maximum number of bytes can be set with --endpoint-bytes-size-limit
CLI argument, defaults to 90.
seconds seconds. The seconds parameter can be a positive
integer or floating point number.
*
character. The following query params can be used to configure this endpoint:
duration: Total number of seconds over which to stream the data, up to two decimal places,
like 1.5. Default: 2.
numbytes: Total number of bytes to stream, up to 10 MB. Default: 10.
code: The HTTP status code to be used in their response. Default: 200.
delay: An initial delay, in seconds, up to two decimal places. Default: 2.
/drip-lines, a newline character is written after every piece of data.
count links, which in turn respond with HTML documents with links
again. You mostly want to use the first version (i.e., without offset). The value of
count is capped to 200.
count bytes, where each byte is its own offset, wrapping around after 255. That is, the
bytes are 0, 1, 2, …, 255, 0, 1, …. The value of count is capped to 1000.
Range header, like bytes=10-19, bytes=10-, or
bytes=-10, responding with 206 Partial Content. Since each byte is its own offset,
it's easy to check that a partial response starts at the right place. A range starting after the end gets a
416, and multiple ranges are ignored, sending the whole content.
curl -H 'Range: bytes=300-309' https://httpbun.com/range/1000 | od -An -tu1
name to value.url query param. If a status
query param is also given, it is used as the HTTP Status code in the response. Otherwise, 302 is used.
Relative redirect targets continue to work, but absolute targets are restricted to http/https
URLs on an allowlist. By default this includes httpbun.com and example.com. When
self-hosting, set HTTPBUN_ALLOWED_REDIRECT_DOMAINS to a comma- or whitespace-separated list like
example.com httpbun.com *.github.io.
count times. For example, /redirect/3 will redirect three times before
settling on a response. The redirect URLs specified in the Location header will be
relative URLs.
count times. For example, /absolute-redirect/3 will redirect three times
before settling on a response. The redirect URLs specified in the Location header will be
absolute paths, like /absolute-redirect/2, without the scheme and host.
base_url to https://httpbun.com/llm, pass any dummy API key, and you're all set. No auth,
no keys. Responses are deterministic with a plausible usage block, and the stream
flag is honored using the right SSE protocol for each provider. Request bodies can be up to 1 MB, and larger
ones get a 413 response.
httpbun field to the request body to override the mock
output:
{
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello!"}],
"httpbun": {"content": "Your custom response here"}
}
If the SDK rejects unknown fields, pass it via extra_body instead, like
extra_body={"httpbun": {"content": "..."}} in the Python SDK. Without it, you get a default
placeholder response.
model, messages, max_tokens, temperature,
n, stream, stop, user, and returns a
chat.completion response.
# Basic request
curl -X POST https://httpbun.com/llm/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4", "messages": [{"role": "user", "content": "Hello!"}]}'
# With custom response
curl -X POST https://httpbun.com/llm/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4", "messages": [...], "httpbun": {"content": "Custom reply!"}}'
# Streaming
curl -N https://httpbun.com/llm/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4", "messages": [...], "stream": true}'
# Python with OpenAI SDK
from openai import OpenAI
client = OpenAI(
base_url="https://httpbun.com/llm/v1",
api_key="not-needed",
)
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Hello!"}],
extra_body={"httpbun": {"content": "Hi! How can I help?"}},
)
print(response.choices[0].message.content)
# Output: Hi! How can I help?
model, prompt (string or list), max_tokens,
temperature, n, stream, stop, user,
suffix, and returns a text_completion response.
curl -X POST https://httpbun.com/llm/v1/completions \
-H "Content-Type: application/json" \
-d '{"model": "gpt-3.5-turbo-instruct", "prompt": "Hello!"}' \
| jq '.choices[0].text'
model, input (string or a list of messages), stream,
user, and returns a response object. Customize output with
httpbun.output_text.
curl -X POST https://httpbun.com/llm/v1/responses \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4.1-mini", "input": "Hello!", "httpbun": {"output_text": "Custom reply!"}}' \
| jq '.output_text'
model, messages, max_tokens, stream,
temperature, top_p, top_k, stop_sequences, and returns an
Anthropic message. Customize output with httpbun.content. Streaming uses Anthropic's
event types (message_start, content_block_delta, message_stop,
etc.).
# Python with the Anthropic SDK
from anthropic import Anthropic
client = Anthropic(
base_url="https://httpbun.com/llm",
api_key="not-needed",
)
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
extra_body={"httpbun": {"content": "Hi! How can I help?"}},
)
print(response.content[0].text)
# Output: Hi! How can I help?
With Docker: docker run -p 80:80 sharat87/httpbun
From source, with task installed: task run
If using for your project's CI, please consider running a self-hosted version using the Docker container. An example of this is in the container-run.yml workflow.
localhost:3090, which configures the server
to listen on TCP port 3090 on localhost.HTTPBUN_BIND environment variable.
the-one,
then the /get endpoint will be available on /the-one/get. Similarly, all other
endpoints are also prefixed with the value of this argument.
/any. This means that
when this option is given, all HTML pages will also become inaccessible. Like the homepage, Mixer UI, help pages
etc. A hosted instance with this option enabled is available at
any.httpbun.com.
/bytes endpoint./any, like --root-is-any, but only for those
hosts. Comma separated. Also HTTPBUN_ANY_HOSTS.
Strict-Transport-Security header on HTML pages served over https. Off by default. Also
HTTPBUN_HSTS=true.
/info endpoint, along with the server's
hostname. Useful to tell instances apart, for example by setting HTTPBUN_INFO_REGION. No other
environment variables are exposed.
Behind a proxy, httpbun needs to be told which headers carry the client's IP and scheme, and which of the proxy's
headers to leave out of responses like /headers.
| Env variable / flag | Default | Purpose |
|---|---|---|
HTTPBUN_CLIENT_IP_HEADER--client-ip-header | X-Httpbun-Forwarded-For | Client's IP |
HTTPBUN_SCHEME_HEADER--scheme-header | X-Httpbun-Forwarded-Proto | Client's scheme, http or https |
HTTPBUN_HIDE_HEADERS--hide-headers | None | Headers to leave out, with * wildcards |
The client IP and scheme headers are left out too. For example:
HTTPBUN_CLIENT_IP_HEADER=X-Real-Ip HTTPBUN_SCHEME_HEADER=X-Forwarded-Proto HTTPBUN_HIDE_HEADERS=X-Forwarded-* HTTPBUN_ANY_HOSTS=any.example.com
Only set these when the proxy always overwrites these headers, or clients can fake them.
Httpbun is distributed with the Apache-2.0 License. Please refer to the LICENSE and NOTICE files present in the source distribution of this project.
The bun icon was generated using the following graphics from Twitter Twemoji:
1fad3.svg.