go-httpbin
Overview
This guide covers deploying and using go-httpbin 2.26.0 on Ubuntu 24.04 LTS from SecureAnyCloud on Microsoft Azure.
go-httpbin is a Go implementation of httpbin: a server you point HTTP clients at when you need a predictable, controllable other end. It echoes back what you send (/get, /post, /anything, /headers) and returns any status code you ask for (/status/418). It also has endpoints for redirects, delays, slow drips, streams, server-sent events, gzip, deflate and brotli responses, cookies, caching headers, images, auth challenges and a WebSocket echo.
Use it to test HTTP clients and SDKs, retry and timeout handling, proxies, API gateways, load balancer health probes and CI pipelines, without depending on a shared public service. This SecureAnyCloud image locks go-httpbin down so it is safe to run on a network.
What you get:
- HTTPS from the first boot. nginx serves go-httpbin on port 443 with a certificate generated on your VM's first boot. Port 80 redirects to HTTPS.
- A password gate in front of everything. go-httpbin has no login of its own and reflects whatever it is sent. The whole service therefore sits behind an HTTP Basic Auth prompt, with a password generated on your VM's first boot. A fresh VM is never an open reflector on the internet.
- No default login. The image contains no password. First boot writes the gate's user name and a random 24-character password to
/var/lib/sac/data/go-httpbin-credentials.txt(root only). nginx stores only a SHA-512 crypt hash, and common guesses are refused. - The gate password never reaches go-httpbin. nginx removes the
Authorizationheader before passing a request on./headers,/anythingand/dump/requestcan never echo your password back, and a web page on another site cannot read it through go-httpbin's CORS answers either. go-httpbin never logs it. - Real client address and host name. go-httpbin sees the address the connection really came from and the VM's configured name, whatever
X-Forwarded-For,HostorForwardedheaders a request carries. - Redirects stay on the VM.
/redirect-toonly sends a client to a path on this VM or to this VM's own name, so it cannot be used as an open redirector. Step 7 shows how to allow other names. - Bounded requests. Request and response bodies are capped at 1 MiB, and every timed endpoint at 10 seconds. nginx also limits each client address to 64 connections and 50 requests a second.
- Other web sites cannot act with your login. A browser re-sends the gate password to any site that asks. nginx therefore refuses every state-changing request (POST, PUT, DELETE and the like) and every WebSocket upgrade whose browser
Originis not this VM. - No route to the Azure metadata service. nginx, go-httpbin and the first-boot service are blocked at the kernel level from the Azure Instance Metadata Service and the WireServer. The Azure VM agent keeps its access.
- A current, checked build. go-httpbin 2.26.0 is the release that fixes a WebSocket denial of service in 2.13 to 2.25. It is built on the image from the official source with Go 1.27.3, which also fixes a
crypto/x509advisory in the Go release the official binary was built with. The build is reproducible and its checksum is pinned. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: go-httpbin 2.26.0 (/usr/local/bin/go-httpbin, listening on 127.0.0.1:8080 only), nginx, Ubuntu 24.04 LTS, cloud-init and the Azure Linux agent.
Default user: the admin user you choose (azureuser in the examples) | Architecture: x64, Gen2 | Recommended size: Standard_B2s
Prerequisites
- An Azure subscription, an SSH public key, and a virtual network with a subnet in your region.
- A network security group that allows TCP 22 (SSH), TCP 443 (go-httpbin) and TCP 80 (redirects to HTTPS). Open these ports to the networks your clients and test runners use, because a test endpoint rarely needs to be reachable from the whole internet. go-httpbin needs no other port.
Deploy from the Azure portal
Search the Azure Marketplace for go-httpbin, choose the SecureAnyCloud offer and select Create.
Pick your subscription, resource group and region. Choose size Standard_B2s, add an SSH public key for the admin user, and allow inbound ports 22, 80 and 443. Then review and create the VM.
Deploy from the Azure CLI
RG="go-httpbin-rg"; LOCATION="eastus"; VM_NAME="go-httpbin1" IMAGE_URN="sac1647283583153:go-httpbin-ubuntu-24-04:default:latest" az group create --name "$RG" --location "$LOCATION" az vm image terms accept --urn "$IMAGE_URN" az vm create --resource-group "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--size Standard_B2s --admin-username azureuser --ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 443 --priority 1001 az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 80 --priority 1002az vm create opens port 22 for you. Where possible, narrow the rules to your own networks with az network nsg rule update --source-address-prefixes <your-cidr>.
Connect and check the services
SSH to the VM as azureuser:
ssh azureuser@<public-ip>On first boot, the VM reads its public IP from the Azure Instance Metadata Service, sets the served name and certificate, and creates the gate password. Then go-httpbin and nginx start. Confirm that the units are active:
systemctl is-active go-httpbin nginx go-httpbin-firstboot go-httpbin-address
sudo test -f /var/lib/sac/data/go-httpbin-firstboot.done && echo FIRSTBOOT_DONEThe output shows all four units active and the first-boot marker present:
active active active active FIRSTBOOT_DONEList the listening sockets. Only SSH and nginx listen on the network, and go-httpbin listens on the loopback address:
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort0.0.0.0:22 users:(("sshd"),("systemd")) 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) 127.0.0.1:8080 users:(("go-httpbin")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx"))Run the built-in self-test, which checks the whole path through the gate:
sudo go-httpbin-selftestSELFTEST-OK https 301 + healthz + gate 401 + 6 wrong gate credentials refused + /get echo + no Authorization reaches go-httpbin + 418 + /json + local redirect 302 + off-site redirect refused + 403 cross-origin POST + 415 compressed body + 444 unknown host + preflightGet your URL and password
Read the credentials file:
sudo cat /var/lib/sac/data/go-httpbin-credentials.txtThe file is readable by root only and holds the URL and the gate's user name and password for this VM:
# go-httpbin 2.26.0 - generated on first boot by go-httpbin-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret. GO_HTTPBIN_URL=https://<public-ip>/ GO_HTTPBIN_USERNAME=httpbin GO_HTTPBIN_PASSWORD=<GO_HTTPBIN_PASSWORD>
#
# Point any HTTP client at GO_HTTPBIN_URL with HTTP Basic Auth GO_HTTPBIN_USERNAME / GO_HTTPBIN_PASSWORD (self-signed
# certificate minted on this instance's first boot; port 80 redirects to https), e.g. GET /get, /status/418, /json. # The gate's Authorization header is removed before go-httpbin, so its echo endpoints never show this password, and a
# web page on another site cannot read it back through go-httpbin's CORS answers. # /redirect-to may only target this instance's name (add names to /etc/sac/go-httpbin-redirect-domains, then
#
sudo go-httpbin-set-hostname --refresh). Self-test:
sudo go-httpbin-selftest
# Change the served name:
sudo go-httpbin-set-hostname <your-domain>
# User guide: https://secureanycloud.com/stacks/go-httpbin/The URL uses the VM's public IP. The first-boot service cannot reach the metadata service itself. Instead, a small service named go-httpbin-address, which has no access to go-httpbin's files, reads the address from the Azure Instance Metadata Service on every boot and records it here. This also works for a Standard SKU public IP, which only the metadata service's load balancer view shows:
cat /run/sac-go-httpbin/address.envGO_HTTPBIN_PUBLIC_IP=<public-ip> GO_HTTPBIN_PRIVATE_IPS="<private-ip>"Requests to the VM's private address are redirected to the public one. To serve a DNS name instead, see Step 9.
From your own machine or a CI job, pass the user name and password as HTTP Basic Auth. For example, use a curl config file that holds user = "httpbin:<password>" and run curl -k -K <file> https://<public-ip>/get.
The certificate is self-signed. Do not add it to a trust store. Instead, compare its fingerprint with the one this VM reports (sudo openssl x509 -in /etc/ssl/go-httpbin/go-httpbin.crt -noout -fingerprint -sha256) and pin that in your client. Alternatively, replace it with a certificate from a certificate authority (Step 9).
Opening the URL in a browser shows go-httpbin's endpoint index after the password prompt.
Send your first requests
Run the commands below on the VM itself. They read the password from the credentials file with sudo and hand it to curl as a config file (-K), so it never appears on a command line. /get echoes your request back as JSON:
H=$(sudo sed -n 's#^GO_HTTPBIN_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/go-httpbin-credentials.txt) gate() {
sudo awk -F= '/^GO_HTTPBIN_USERNAME=/{u=$2} /^GO_HTTPBIN_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/go-httpbin-credentials.txt; } curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" "https://$H/get?team=qa"{ "args": { "team": [ "qa" ] }, "headers": { "Accept": [ "*/*" ], "Host": [ "<public-ip>" ], "User-Agent": [ "curl/8.5.0" ], "X-Forwarded-For": [ "127.0.0.1" ], "X-Forwarded-Proto": [ "https" ], "X-Real-Ip": [ "127.0.0.1" ] }, "method": "GET", "origin": "127.0.0.1", "url": "https://<public-ip>/get?team=qa" }origin and X-Forwarded-For show the address the request really came from. Here that is the VM itself. From your machine, they show your address. There is no Authorization header, because nginx checked the password and removed it.
Ask for a status code, post a JSON body, and list the headers go-httpbin receives:
H=$(sudo sed -n 's#^GO_HTTPBIN_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/go-httpbin-credentials.txt) gate() {
sudo awk -F= '/^GO_HTTPBIN_USERNAME=/{u=$2} /^GO_HTTPBIN_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/go-httpbin-credentials.txt; } curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code}\n' "https://$H/status/418" curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -H 'Content-Type: application/json' --data '{"order": 42}' "https://$H/post" | python3 -c 'import json,sys; o=json.load(sys.stdin); print(o["json"], o["headers"]["Content-Type"])' curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" "https://$H/headers" | python3 -c 'import json,sys; print(sorted(json.load(sys.stdin)["headers"]))'418 {'order': 42} ['application/json'] ['Accept', 'Host', 'User-Agent', 'X-Forwarded-For', 'X-Forwarded-Proto', 'X-Real-Ip']The endpoint index at the root URL lists everything go-httpbin answers. The go-httpbin README describes each endpoint.
Delays, streams and limits
Timed endpoints wait as long as you ask, up to 10 seconds, and sized endpoints return up to 1 MiB. Beyond those limits, go-httpbin answers 400 and says why:
H=$(sudo sed -n 's#^GO_HTTPBIN_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/go-httpbin-credentials.txt) gate() {
sudo awk -F= '/^GO_HTTPBIN_USERNAME=/{u=$2} /^GO_HTTPBIN_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/go-httpbin-credentials.txt; } curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code}\n' "https://$H/delay/2" curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" "https://$H/delay/11"; echo curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" "https://$H/bytes/2000000"; echo curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" "https://$H/drip?duration=2&numbytes=5&code=200"; echo200 { "status_code": 400, "error": "Bad Request", "detail": "invalid duration: duration 11s longer than 10s" } { "status_code": 400, "error": "Bad Request", "detail": "invalid byte count: 2000000 not in range [1, 1048576]" } *****nginx passes responses through as go-httpbin produces them, so /drip, /stream/<n> and /sse arrive gradually, as a real slow server's would. A request body larger than 1 MiB gets a 413 from nginx. A client that opens more than 64 connections, or sends more than 50 requests a second after a burst of 200, gets 429.
The limits are go-httpbin's -max-body-size and -max-duration options in /etc/systemd/system/go-httpbin.service. To raise them, run sudo systemctl edit --full go-httpbin and change the two values. Then change client_max_body_size in /etc/nginx/sites-available/sac-go-httpbin to match, restart go-httpbin and reload nginx.
Redirects
/redirect/<n>, /relative-redirect/<n> and /absolute-redirect/<n> redirect within the VM. /redirect-to?url= redirects wherever the url says. On an open server, that makes it an open redirector, so here it accepts only a path on this VM or this VM's own name:
H=$(sudo sed -n 's#^GO_HTTPBIN_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/go-httpbin-credentials.txt) gate() {
sudo awk -F= '/^GO_HTTPBIN_USERNAME=/{u=$2} /^GO_HTTPBIN_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/go-httpbin-credentials.txt; } curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code} %header{location}\n' "https://$H/redirect-to?url=/get" curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code} %header{location}\n' "https://$H/redirect/2" curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code}\n' "https://$H/redirect-to?url=https://example.com/"302 /get 302 /relative-redirect/1 400To let /redirect-to send clients to other names, such as the test system your client should end up at, list them one per line in /etc/sac/go-httpbin-redirect-domains and re-apply the configuration. Both nginx and go-httpbin then accept them:
echo '<your-domain>' | sudo tee -a /etc/sac/go-httpbin-redirect-domains
sudo go-httpbin-set-hostname --refreshOnly names are accepted, one per line, without a scheme, path or wildcard.
Auth challenge endpoints
/basic-auth/<user>/<password>, /hidden-basic-auth/..., /digest-auth/... and /bearer test how a client answers an authentication challenge. They use the same Authorization header as the gate, so through the gate they always answer 401, because nginx removes that header. Run them on the VM, or through an SSH tunnel to 127.0.0.1:8080, where go-httpbin answers directly:
curl -s -u alice:s3cret http://127.0.0.1:8080/basic-auth/alice/s3cret
curl -s -o /dev/null -w '%{http_code}\n' -u alice:wrong http://127.0.0.1:8080/basic-auth/alice/s3cret{ "authenticated": true, "user": "alice", "authorized": true } 401To reach go-httpbin directly from your machine, run ssh -L 8080:127.0.0.1:8080 azureuser@<public-ip>. While the tunnel is open, http://127.0.0.1:8080/ on your machine reaches go-httpbin.
Use your own domain
Point a DNS record for your domain at the VM's public IP, or give the public IP a DNS label in Azure. Then give go-httpbin the name. The helper mints a new certificate for the name and pins nginx, the cross-site guard and the redirect allow list to it. It also redirects the VM's IP addresses to the name:
sudo go-httpbin-set-hostname <your-domain>For a certificate your clients trust, replace /etc/ssl/go-httpbin/go-httpbin.crt and /etc/ssl/go-httpbin/go-httpbin.key with your own, for example from Let's Encrypt or your certificate authority. Then reload nginx.
Security notes
nginx, go-httpbin and the first-boot service are blocked from the Azure Instance Metadata Service and the WireServer (169.254.169.254, 168.63.129.16 and their IPv6 forms). Only go-httpbin-address, which runs no go-httpbin code, and the Azure agent keep that access:
for u in nginx go-httpbin go-httpbin-firstboot; do printf '%s ' "$u"; systemctl show -p IPAddressDeny --value "$u" | tr ' ' '\n' | sort | tr '\n' ' '; echo; donenginx 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 go-httpbin 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 go-httpbin-firstboot 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128The gate answers before go-httpbin does. nginx refuses a state-changing request that another web site's page sends, and it refuses a request body with a Content-Encoding. A request for a host name the VM does not serve gets no answer at all:
H=$(sudo sed -n 's#^GO_HTTPBIN_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/go-httpbin-credentials.txt) gate() {
sudo awk -F= '/^GO_HTTPBIN_USERNAME=/{u=$2} /^GO_HTTPBIN_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/go-httpbin-credentials.txt; } curl -sk -o /dev/null -w '%{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/get" curl -sk -K <(gate) -o /dev/null -w '%{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Origin: https://evil.example' --data 'x=1' "https://$H/post" printf 'x' | curl -sk -K <(gate) -o /dev/null -w '%{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'Content-Encoding: gzip' --data-binary @- "https://$H/post" curl -sk -o /dev/null -w '%{http_code}\n' --resolve evil.example:443:127.0.0.1 https://evil.example/ || true401 403 415 000go-httpbin answers CORS requests from any origin, because that is part of what it tests. A page on another site that your browser opens while it remembers the gate password can therefore read go-httpbin's GET responses. Those responses only reflect that page's own request, because the gate's Authorization header is gone before go-httpbin sees it. A browser-shaped cross-site request shows this:
H=$(sudo sed -n 's#^GO_HTTPBIN_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/go-httpbin-credentials.txt) gate() {
sudo awk -F= '/^GO_HTTPBIN_USERNAME=/{u=$2} /^GO_HTTPBIN_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/go-httpbin-credentials.txt; } curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -D - -o /dev/null -H 'Origin: https://evil.example' "https://$H/headers" | grep -i '^access-control-allow' | tr -d '\r' curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -H 'Origin: https://evil.example' "https://$H/headers" | python3 -c 'import json,sys; print(sorted(json.load(sys.stdin)["headers"]))'access-control-allow-credentials: true access-control-allow-origin: https://evil.example ['Accept', 'Host', 'Origin', 'User-Agent', 'X-Forwarded-For', 'X-Forwarded-Proto', 'X-Real-Ip']Show how go-httpbin itself is started:
systemctl show -p ExecStart --value go-httpbin | grep -o 'argv\[\]=[^;]*' | sed 's/argv\[\]=//'/usr/local/bin/go-httpbin -host 127.0.0.1 -port 8080 -max-body-size 1048576 -max-duration 10s -log-level OFF -allowed-redirect-domains ${GO_HTTPBIN_REDIRECT_DOMAINS}Other notes:
- Cross-site requests. Requests without a browser
Originheader (curl, SDKs, test runners) are unaffected by the cross-site guard. A page on another site cannot send POST, PUT, PATCH, DELETE or preflight (OPTIONS) requests, or open the WebSocket, with your login. - Compressed request bodies. nginx answers 415 to any request that carries a
Content-Encoding, before go-httpbin sees it. Send bodies uncompressed. go-httpbin's compressed responses (/gzip,/deflate,/brotli) are unaffected. - WebSocket echo.
/websocket/echoworks through nginx over HTTP/1.1 (wss://<your host>/websocket/echowith the Basic Auth header), with messages up to 1 MiB and a 10-second session. - Logs. go-httpbin's own request log is off, so the URLs and headers your tests send are not kept in the system journal. nginx records each request (client address, method, path, status, user agent) in
/var/log/nginx/access.log. To turn go-httpbin's log on, change-log-level OFFto-log-level INFOwithsudo systemctl edit --full go-httpbin. - What go-httpbin exposes.
/envis empty,/hostnamereturns a fixed generic value, and/versionreturns only the service name. Nothing about the VM is published, and go-httpbin makes no outbound requests of its own. - SSH. Root login, empty passwords and X11 forwarding are off. Password sign-in follows the choice you made when you created the VM.
- The build.
/usr/share/doc/sac-go-httpbin/BUILD-INFO.txtrecords the source archive, the Go toolchain and the checksums of the shipped files.
Keep it up to date
Ubuntu security updates install automatically. To apply all updates now:
sudo apt-get update && sudo apt-get upgrade -yNew go-httpbin releases arrive as new versions of this offer, built and checked the same way. go-httpbin keeps no data, so moving to a new version means deploying it and pointing your clients at it. The release notes list what changed.
Support and licensing
go-httpbin is open source under the MIT license (Will McCutchen and the go-httpbin contributors). The source code is at github.com/mccutchen/go-httpbin. It has no dependencies beyond the Go standard library, whose BSD-style license ships with it. The license texts are in /usr/share/doc/sac-go-httpbin/licences/.
There is no per-user license fee. The image price covers SecureAnyCloud's build, hardening, first-boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24 hour response SLA
Deploy on Azure
Launch go-httpbin 2.26.0 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
go-httpbin is a fast, dependency-free HTTP request and response testing service, a Go implementation of the well-known httpbin. It exposes endpoints for exercising and debugging anything that speaks HTTP:
- Echo endpoints such as
/get,/post,/putand/deletereflect the method, headers, query arguments and body of your request back to you as JSON. - Status code endpoints such as
/status/{code}return any HTTP status you ask for. - Utility endpoints cover headers, client IP, redirects, delays, streaming, gzip and deflate responses, basic auth challenges, cookies, UUIDs and random bytes.
Point your HTTP clients, SDKs, proxies, API gateways, load balancers and CI pipelines at go-httpbin when you need a predictable, controllable server on the other end. Want to verify that your client handles a 429 with a Retry-After header, a slow response, a chunked stream or a redirect chain? go-httpbin gives you an endpoint for each, with no external dependencies and no shared public rate limits to fight.
The image is delivered as a ready-to-use appliance: a working test endpoint is answering requests within minutes of launch. It is a headless service with no web console to log in to.
What you get:
- Secure by default: go-httpbin has no authentication of its own, and its redirect and fetch endpoints could be abused as an open reflector, so this image never exposes it directly
- go-httpbin bound to the loopback interface only (
127.0.0.1:8080), with nginx as the single network-facing surface - nginx terminating TLS on port 443 with a per-instance self-signed certificate, and fronting the entire service with HTTP basic authentication
- An unauthenticated health endpoint for load balancer probes, and plain HTTP on port 80 that redirects to HTTPS
- Nothing secret baked into the image: on the first boot of your instance, a one-shot service generates a unique HTTP basic authentication password for the user
httpbinand a unique self-signed TLS certificate - Those credentials written to the root-only file
/root/go-httpbin-credentials.txt
Prerequisites
Before you deploy this image, you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A VPC and subnet in the target Region, with a security group that allows inbound port 22 from your management network and ports 80 and 443 from your clients
- The AWS CLI (version 2) installed locally, if you plan to deploy from the command line
Launch the instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for go-httpbin. Select the SecureAnyCloud listing, choose Select, then choose Continue on the subscription summary.
Pick an instance type of m5.large or larger. Choose your EC2 key pair under Key pair (login).
Under Network settings, select your VPC and subnet. Create or select a security group that opens port 22 from your management network and ports 80 and 443 from your clients. Leave the root volume at the default size or larger.
Select Launch instance. First-boot initialization takes a few seconds after the instance state becomes Running and the status checks pass. By the time you can connect, the per-instance TLS certificate and basic auth password are generated and the service is serving over HTTPS.
Launch the instance from the AWS CLI
This command launches an instance from the SecureAnyCloud go-httpbin Marketplace AMI into an existing subnet and security group. Replace the values in angle brackets:
- <ami-id>: the AMI ID shown on the Marketplace listing
- <key-name>: your EC2 key pair name
- <subnet-id>: your subnet ID
- <security-group-id>: a security group that opens port 22 from your management network and ports 80 and 443 from your clients
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=go-httpbin}]'When the instance reaches the Running state and its status checks pass, note its public IP address or DNS name from the EC2 console or with aws ec2 describe-instances.
Connect to your instance
Connect over SSH using your key pair and the login user for your operating system variant. On Ubuntu 24.04, the SSH login user is ubuntu.
ssh -i /path/to/your-key.pem ubuntu@<public-ip>Retrieve the per-instance credentials
The HTTP basic auth password and the TLS certificate are unique to your instance and generated on first boot. Read the credentials file, which only root can read:
sudo cat /root/go-httpbin-credentials.txtThe file holds the username (httpbin), the generated password and the service URL. Keep this file secret.
To rotate the password, regenerate the htpasswd entry at /etc/nginx/go-httpbin.htpasswd and reload nginx.
Confirm the service is running
go-httpbin runs as a hardened systemd service bound to loopback only, with nginx as the sole network-facing surface. Confirm that both services are active and that go-httpbin listens only on 127.0.0.1:8080:
systemctl is-active go-httpbin nginxsudo ss -ltnp | grep -E '127.0.0.1:8080|:443'The first command prints active for both units. The second shows go-httpbin listening on 127.0.0.1:8080 (loopback only, never exposed to the network) and nginx listening on port 443.
Verify the security posture
The service is never an open reflector. An unauthenticated request is rejected with 401, while the health endpoint stays open for load balancer probes. Verify both directly on the instance:
curl -sk -o /dev/null -w 'unauthenticated /get -> %{http_code}\n' https://127.0.0.1/getcurl -sk https://127.0.0.1/healthzThe first command prints 401. The second prints ok (the unauthenticated /healthz probe). Plain HTTP on port 80 redirects to HTTPS.
Exercise the API
With the per-instance basic auth credentials, the echo endpoints reflect your request back as JSON. The commands below read the generated password from the credentials file into a shell variable at runtime, so you never type a secret on the command line. They then call a few representative endpoints locally on the instance:
PW=$(sudo grep '^GO_HTTPBIN_PASSWORD=' /root/go-httpbin-credentials.txt | cut -d= -f2-)
curl -sk -u "httpbin:$PW" "https://127.0.0.1/get?project=sac" | head -20
curl -sk -o /dev/null -w '/status/418 -> %{http_code}\n' -u "httpbin:$PW" https://127.0.0.1/status/418
curl -sk -u "httpbin:$PW" https://127.0.0.1/json | head -12/get echoes the method, headers and query arguments of your request as JSON. /status/418 returns the exact HTTP status code you asked for. /json returns a sample document.
Use it from your own clients
From your workstation, your test suite or a CI job, point any HTTP client at the HTTPS endpoint. Use the basic auth user and password from Step 4.
The TLS certificate is self-signed per instance. Pass -k (or install the certificate) until you front the service with your own domain and a trusted certificate.
# Echo endpoints reflect your request back as JSON curl -k -u httpbin:<password> https://<public-ip>/get curl -k -u httpbin:<password> -X POST -d 'hello=world' https://<public-ip>/post
# Ask for any HTTP status code to test error handling and retries curl -k -u httpbin:<password> https://<public-ip>/status/429
# Add a delay to test timeouts, or stream a response to test backpressure curl -k -u httpbin:<password> https://<public-ip>/delay/3 curl -k -u httpbin:<password> https://<public-ip>/stream/10
# Inspect headers, client IP, redirects, cookies and encodings curl -k -u httpbin:<password> https://<public-ip>/headers curl -k -u httpbin:<password> https://<public-ip>/ip curl -k -u httpbin:<password> https://<public-ip>/gzipFront it with your own domain and certificate (optional)
For production, put go-httpbin behind your own hostname and a trusted TLS certificate. Point a DNS record at the instance. Then either install a certificate from your certificate authority into /etc/nginx/tls/ and reload nginx, or terminate TLS at a load balancer in front of the instance.
The nginx site is at /etc/nginx/sites-available/sac-go-httpbin. SecureAnyCloud engineers can help you wire this up after purchase.
Security notes
- Loopback only. go-httpbin binds
127.0.0.1:8080only and is never exposed to the network directly. nginx on port 443 is the sole network-facing surface. - Everything behind authentication. The whole service is behind per-instance HTTP basic authentication, so its redirect and fetch endpoints cannot be abused as an SSRF or open redirect relay.
- No shared credentials. No shared or default credentials ship in the image. The per-instance password and TLS certificate are both generated on first boot.
- Protect the credentials file. Keep
/root/go-httpbin-credentials.txtsecret, and restrict the instance security group to the clients that need access.
Support
SecureAnyCloud provides 24/7 technical support for this go-httpbin product by email at sales@secureanycloud.com and via live chat. Critical issues receive a one-hour average response time.
Our engineers help with:
- Deployment and first-boot configuration
- Retrieving and rotating the per-instance credentials
- Fronting the service with your own domain and a trusted TLS certificate
- Wiring go-httpbin into your HTTP client, SDK and CI test suites
- Using the status code, delay, streaming and encoding endpoints to test retries, timeouts and error handling
- Tuning request and response limits
- go-httpbin version upgrades and patch guidance, and performance troubleshooting
Deploy on AWS
Launch go-httpbin on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com