{"id":46469511,"url":"https://github.com/restinthemiddle/restinthemiddle","last_synced_at":"2026-03-06T06:03:25.123Z","repository":{"id":40596445,"uuid":"232199534","full_name":"restinthemiddle/restinthemiddle","owner":"restinthemiddle","description":"HTTP logging proxy","archived":false,"fork":false,"pushed_at":"2026-03-04T09:42:19.000Z","size":4009,"stargazers_count":7,"open_issues_count":5,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-04T16:06:25.007Z","etag":null,"topics":["debugging","docker","go","golang","http","kubernetes","logging","reverse-proxy"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/restinthemiddle.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-01-06T22:51:10.000Z","updated_at":"2026-02-11T07:49:28.000Z","dependencies_parsed_at":"2023-12-18T22:53:36.883Z","dependency_job_id":"63d5327a-6fec-474e-a367-5c4aad0c1ffa","html_url":"https://github.com/restinthemiddle/restinthemiddle","commit_stats":null,"previous_names":["jensschulze/restinthemiddle"],"tags_count":67,"template":false,"template_full_name":null,"purl":"pkg:github/restinthemiddle/restinthemiddle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/restinthemiddle%2Frestinthemiddle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/restinthemiddle%2Frestinthemiddle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/restinthemiddle%2Frestinthemiddle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/restinthemiddle%2Frestinthemiddle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/restinthemiddle","download_url":"https://codeload.github.com/restinthemiddle/restinthemiddle/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/restinthemiddle%2Frestinthemiddle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30164532,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T04:43:31.446Z","status":"ssl_error","status_checked_at":"2026-03-06T04:40:30.133Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["debugging","docker","go","golang","http","kubernetes","logging","reverse-proxy"],"created_at":"2026-03-06T06:03:01.774Z","updated_at":"2026-03-06T06:03:25.089Z","avatar_url":"https://github.com/restinthemiddle.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Restinthemiddle\n\n![pulls](https://img.shields.io/docker/pulls/jdschulze/restinthemiddle)\n[![codecov](https://codecov.io/gh/restinthemiddle/restinthemiddle/branch/main/graph/badge.svg)](https://codecov.io/gh/restinthemiddle/restinthemiddle)\n![main](https://img.shields.io/github/v/tag/restinthemiddle/restinthemiddle)\n\nThis lightweight application acts as a HTTP logging proxy for developing and staging environments. If you put it between an HTTP client and the respective server you can easily monitor requests and responses.\n\n## Installation\n\n### Docker (recommended)\n\nPull the [Docker image](https://hub.docker.com/r/jdschulze/restinthemiddle/tags) from Docker Hub\n\n```shell\ndocker pull jdschulze/restinthemiddle:2\n```\n\nPinning the version to (at least) the major version is highly recommended. Use `latest` at your own risk. ATM the `latest` tag is always the `HEAD` of the `main` branch but this can change without notice anytime.\n\n### Build the Docker image yourself\n\nClone this repository and run `make docker`.\n\n```shell\ngit clone https://github.com/restinthemiddle/restinthemiddle.git\ncd restinthemiddle\ngit checkout main\nmake docker\n```\n\n### Build the binary yourself\n\nClone this repository and run `make build`.\n\n```shell\ngit clone https://github.com/restinthemiddle/restinthemiddle.git\ncd restinthemiddle\ngit checkout main\nmake build\n```\n\n## Usage\n\nTypically, you place the logging proxy between an application and an API. This is the use case Restinthemiddle was developed for.\n\n```text\n+-----------------+         +-----------------+         +-----------------+\n|                 +--------\u003e+                 +--------\u003e+                 |\n|   Application   |         | Restinthemiddle |         |       API       |\n|                 +\u003c--------+                 +\u003c--------+                 |\n+-----------------+         +-----------------+         +-----------------+\n```\n\nBut there are cases where it makes sense to place it between your browser and the application. For example, you could want to add custom headers to every request (kind of off-label use, because no logging is needed):\n\n```text\n+-----------------+         +-----------------+         +-----------------+\n|                 +--------\u003e+                 +--------\u003e+                 |\n|     Browser     |         | Restinthemiddle |         |   Application   |\n|                 +\u003c--------+                 +\u003c--------+                 |\n+-----------------+         +-----------------+         +-----------------+\n```\n\nYou may as well use Restinthemiddle as an alternative entrypoint for your application.\n\n### Configuration\n\nConfiguration is handled by [spf13/viper](https://pkg.go.dev/github.com/spf13/viper).\n\nRestinthemiddle is intended for use in a containerized environment. Therefore it is configurable entirely via environment variables - almost!\nHeaders have to be set via command line arguments or the configuration file.\n\nThe ascending order of precedence (last wins) is:\n\n* Restinthemiddle default values\n* Configuration via YAML file\n* Configuration via Environment variables\n* Command line arguments\n\nExample configuration file:\n\n```yaml\ntargetHostDsn: www.example.com\nlistenIp: 0.0.0.0\nlistenPort: \"8000\"\nheaders:\n    X-My-Header: myexamplevalue\nloggingEnabled: true\nsetRequestId: false\nexclude: \"\"\nlogPostBody: true\nlogResponseBody: true\nexcludePostBody: \"\"\nexcludeResponseBody: \"\"\nreadTimeout: 30\nreadHeaderTimeout: 10\nwriteTimeout: 30\nidleTimeout: 120\n```\n\nThere are several file locations where configuration is being searched for. The ascending order of precedence (last wins) is:\n\n* `/etc/restinthemiddle/config.yaml`\n* `$HOME/.restinthemiddle/config.yaml`\n* `./config.yaml`\n\n#### Keys\n\n| Configuration key                | Environment variable    | Command line flag       | Description                                                                                                                                                  | Default value                                  |\n|----------------------------------|-------------------------|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------|\n| `targetHostDsn` (required)       | `TARGET_HOST_DSN`       | --target-host-dsn       | The DSN of the target host in the form `schema://username:password@hostname:port/basepath?query`. Find a [detailed description](#the-target-host-dsn) below. | -                                              |\n| `listenIp` (optional)            | `LISTEN_IP`             | --listen-ip             | The IP Restinthemiddle is bound to.                                                                                                                          | `0.0.0.0`                                      |\n| `listenPort` (optional)          | `LISTEN_PORT`           | --listen-port           | The port Restinthemiddle is bound to.                                                                                                                        | `8000`                                         |\n| `metricsEnabled` (optional)      | `METRICS_ENABLED`       | --metrics-enabled       | Enable Prometheus metrics endpoint.                                                                                                                          | `true`                                         |\n| `metricsPort` (optional)         | `METRICS_PORT`          | --metrics-port          | The port on which the Prometheus metrics endpoint listens.                                                                                                   | `9090`                                         |\n| `headers` (optional)             | -                       | --header                | A dictionary of HTTP headers.                                                                                                                                | `User-Agent: Rest in the middle logging proxy` |\n| `loggingEnabled` (optional)      | `LOGGING_ENABLED`       | --logging-enabled       | Enable logging.                                                                                                                                              | `true`                                         |\n| `setRequestId` (optional)        | `SET_REQUEST_ID`        | --set-request-id        | If not already present in the request, add an `X-Request-Id` header with a version 4 UUID.                                                                   | `false`                                        |\n| `exclude` (optional)             | `EXCLUDE`               | --exclude               | If the given URL path matches this Regular Expression this request+response will not be logged.                                                              | `\"\"`                                           |\n| `logPostBody` (optional)         | `LOG_POST_BODY`         | --log-post-body         | Log the request's body.                                                                                                                                      | `true`                                         |\n| `logResponseBody` (optional)     | `LOG_RESPONSE_BODY`     | --log-response-body     | Log the response's body.                                                                                                                                     | `true`                                         |\n| `excludePostBody` (optional)     | `EXCLUDE_POST_BODY`     | --exclude-post-body     | If the given URL path matches this Regular Expression the request body (post) is set empty.                                                                  | `\"\"`                                           |\n| `excludeResponseBody` (optional) | `EXCLUDE_RESPONSE_BODY` | --exclude-response-body | If the given URL path matches this Regular Expression the response body is set emtpy.                                                                        | `\"\"`                                           |\n| `readTimeout` (optional)         | `READ_TIMEOUT`          | --read-timeout          | Read timeout in seconds. See [Timeout Configuration](#timeout-configuration) below.                                                                          | `0` (no timeout)                               |\n| `readHeaderTimeout` (optional)   | `READ_HEADER_TIMEOUT`   | --read-header-timeout   | Read header timeout in seconds. See [Timeout Configuration](#timeout-configuration) below.                                                                   | `0` (no timeout)                               |\n| `writeTimeout` (optional)        | `WRITE_TIMEOUT`         | --write-timeout         | Write timeout in seconds. See [Timeout Configuration](#timeout-configuration) below.                                                                         | `0` (no timeout)                               |\n| `idleTimeout` (optional)         | `IDLE_TIMEOUT`          | --idle-timeout          | Idle timeout in seconds. See [Timeout Configuration](#timeout-configuration) below.                                                                          | `0` (no timeout)                               |\n\n**Note:** See the [net/http.Server documentation](https://pkg.go.dev/net/http#Server) for detailed information about the behavior of `ReadTimeout`, `ReadHeaderTimeout`, `WriteTimeout`, and `IdleTimeout`.\n\n##### Timeout Configuration\n\n**Important:** The default timeout values are `0` (no timeout), which matches the behavior of Go's `net/http.Server`. While this provides maximum flexibility, it can expose your service to resource exhaustion and security vulnerabilities.\n\n**Recommended production values:**\n\n```yaml\nreadTimeout: 30\nreadHeaderTimeout: 10\nwriteTimeout: 30\nidleTimeout: 120\n```\n\n**Why you should configure timeouts:**\n\n* **ReadHeaderTimeout (recommended: 10s)**: Protects against [Slowloris attacks](https://en.wikipedia.org/wiki/Slowloris_(computer_security)) where clients send headers very slowly to exhaust server resources.\n* **ReadTimeout (recommended: 30s)**: Prevents slow clients from holding connections indefinitely while sending request bodies.\n* **WriteTimeout (recommended: 30s)**: Ensures responses are sent in a reasonable timeframe, preventing resource leaks from slow or stalled connections.\n* **IdleTimeout (recommended: 120s)**: Controls how long keep-alive connections remain open between requests, balancing connection reuse with resource management.\n\n**Without explicit timeouts:**\n\n* Vulnerable to slowloris and similar DoS attacks\n* Risk of resource exhaustion from hanging connections\n* Potential memory leaks from abandoned connections\n* No protection against malicious or buggy clients\n\n**With proper timeouts:**\n\n* Protection against common attack vectors\n* Predictable resource usage\n* Automatic cleanup of stale connections\n* Better overall system stability\n\n##### The target host DSN\n\n`schema://username:password@hostname:port/basepath?query`\n\n* `schema` (required) is `http` or `https`\n* `username:password@` is optional and will be evaluated only if both values are set.\n* `hostname` (required)\n* `port` is optional. Standard ports are `80` (http) and `443` (https).\n* `basepath` is optional. Will be prefixed to any request URL path pointed at Restinthemiddle. See examples section.\n* `query` is optional. If set, `query` will precede the actual request's query.\n\n##### Headers\n\nIf a header is defined multiple times, the last assignment wins.\n\nIf you need to make a HTTP Basic Authentication **and** need to send another Authorization header at the same time (e.g. a JWT) we have got you covered. Just put the HTTP Basic Auth credentials into the _target host DSN_ string:\n\n```shell\ndocker run -it --rm -e TARGET_HOST_DSN=http://user:password@www.example.com -p 8000:8000 jdschulze/restinthemiddle:2 --header=\"Authorization:Bearer ABCD1234\"\n```\n\n## Examples\n\n### Basic\n\nWe want to log HTTP calls against `www.example.com` over an insecure connection.\n\n```shell\n# Set up the proxy\ndocker run -it --rm -e TARGET_HOST_DSN=http://www.example.com -p 8000:8000 jdschulze/restinthemiddle:2\n\n# In another terminal window we make the API call against http://www.example.com/api/visitors\ncurl -i http://127.0.0.1:8000/api/visitors\n```\n\n### Advanced\n\nWe want to log HTTP calls against `www.example.com:4430` over a TLS connection (`https://…`). The API is protected by HTTP basic auth (username: `user`; password: `pass`). The base path always starts with `api/`.\n\nNote that the base path defined in `TARGET_HOST_DSN` prefixes any subsequent calls!\n\n```shell\n# Set up the proxy\ndocker run -it --rm -e TARGET_HOST_DSN=https://user:pass@www.example.com:4430/api?start=1577833200 -p 8000:8000 jdschulze/restinthemiddle:2\n\n# In another terminal window we make the API call against https://user:pass@www.example.com:4430/api/visitors?start=1577833200\ncurl -i http://127.0.0.1:8000/visitors\n```\n\n### Setting/changing headers\n\nWe want to log HTTP calls against `www.example.com` over an insecure connection. Every request has to be enhanced with a custom header `X-App-Version: 3.0.0`. No logging shall take place.\n\n#### With configuration file\n\n##### config.yaml\n\n```yaml\ntargetHostDsn: http://www.example.com\nheaders:\n    X-App-Version: '3.0.0'\nloggingEnabled: false\n```\n\n```shell\n# Set up the proxy\ndocker run -it --rm -v ./config.yaml:/restinthemiddle/config.yaml -p 8000:8000 jdschulze/restinthemiddle:2\n\n# In another terminal window we make the API call against http://www.example.com/home\ncurl -i http://127.0.0.1:8000/home\n```\n\n#### With command line arguments\n\n```shell\n# Set up the proxy\ndocker run -it --rm -p 8000:8000 jdschulze/restinthemiddle:2 restinthemiddle --target-host-dsn=http://www.example.com --header=x-app-version:3.0.0\n```\n\n### Helm Chart for Kubernetes\n\nThere is a Helm Chart for Restinthemiddle at [https://github.com/restinthemiddle/helm](https://github.com/restinthemiddle/helm).\nYou may want to add the Restinthemiddle Helm repository:\n\n```shell\nhelm repo add restinthemiddle https://restinthemiddle.github.io/helm\nhelm repo update\n```\n\n## Metrics\n\nRestinthemiddle exposes Prometheus metrics on a separate HTTP endpoint for monitoring proxy performance and health. See [METRICS.md](./METRICS.md) for detailed documentation on available metrics, configuration options, and example queries.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frestinthemiddle%2Frestinthemiddle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frestinthemiddle%2Frestinthemiddle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frestinthemiddle%2Frestinthemiddle/lists"}