{"id":13581922,"url":"https://github.com/caddyserver/replace-response","last_synced_at":"2025-05-16T13:05:54.486Z","repository":{"id":46183632,"uuid":"288233913","full_name":"caddyserver/replace-response","owner":"caddyserver","description":"Caddy module that performs replacements in response bodies","archived":false,"fork":false,"pushed_at":"2024-12-11T19:44:04.000Z","size":100,"stargazers_count":117,"open_issues_count":9,"forks_count":30,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-12T09:18:55.788Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/caddyserver.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-17T16:44:00.000Z","updated_at":"2025-04-10T05:56:36.000Z","dependencies_parsed_at":"2023-12-21T00:58:26.501Z","dependency_job_id":"bf41590d-6637-4ae6-aa2b-f87c9fcf72da","html_url":"https://github.com/caddyserver/replace-response","commit_stats":{"total_commits":11,"total_committers":4,"mean_commits":2.75,"dds":0.4545454545454546,"last_synced_commit":"f92bc7d0c29d0588f91f29ecb38a0c4ddf3f85f8"},"previous_names":["caddyserver/response-replace"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caddyserver%2Freplace-response","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caddyserver%2Freplace-response/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caddyserver%2Freplace-response/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caddyserver%2Freplace-response/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/caddyserver","download_url":"https://codeload.github.com/caddyserver/replace-response/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254535827,"owners_count":22087399,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-08-01T15:02:19.312Z","updated_at":"2025-05-16T13:05:54.433Z","avatar_url":"https://github.com/caddyserver.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"Caddy `replace_response` handler module\n=======================================\n\nThis Caddy module performs substring and regular expression replacements on response bodies, hence the name `replace_response`.\n\nBy default, this module operates in \"buffer\" mode. This is not very memory-efficient, but it guarantees we can always set the correct Content-Length header because we can buffer the output to know the resulting length before writing the response. If you need higher efficiency, you can enable \"streaming\" mode. When performing replacements on a stream, the Content-Length header may be removed because it is not always possible to know the correct value, since the results are streamed directly to the client and headers must be written before the body.\n\nNote: This handler cannot perform replacements on compressed content. If your response comes from a proxied backend that supports compression, you will either have to decompress it in a response handler chain before this handler runs, or disable from the backend. One easy way to ask the backend to _not_ compress the response is to set the `Accept-Encoding` header to `identity`, for example: `header_up Accept-Encoding identity` (in your Caddyfile, in the `reverse_proxy` block).\n\nThis module supports the use of placeholders in the `search` and `replace` arguments (but not regexes).\n\n**Module name:** `http.handlers.replace_response`\n\n\n## JSON examples\n\nSubstring substitution:\n\n```json\n{\n\t\"handler\": \"replace_response\",\n\t\"replacements\": [\n\t\t{\n\t\t\t\"search\": \"Foo\",\n\t\t\t\"replace\": \"Bar\"\n\t\t}\n\t]\n}\n```\n\nRegular expression replacement:\n\n```json\n{\n\t\"handler\": \"replace_response\",\n\t\"replacements\": [\n\t\t{\n\t\t\t\"search_regexp\": \"\\\\s+foo(bar|baz)\\\\s+\",\n\t\t\t\"replace\": \" foo $1 \"\n\t\t}\n\t]\n}\n```\n\nSame, but with streaming mode (we just set `\"stream\": true` in the handler):\n\n```json\n{\n\t\"handler\": \"replace_response\",\n\t\"replacements\": [\n\t\t{\n\t\t\t\"search_regexp\": \"\\\\s+foo(bar|baz)\\\\s+\",\n\t\t\t\"replace\": \" foo $1 \"\n\t\t}\n\t],\n\t\"stream\": true\n}\n```\n\nWith a response matcher:\n```json\n{\n\t\"handler\": \"replace_response\",\n\t\"replacements\": [\n\t\t{\n\t\t\t\"search\": \"Foo\",\n\t\t\t\"replace\": \"Bar\"\n\t\t}\n\t],\n\t\"match\": {\n\t\t\"headers\": {\n\t\t\t\"Content-Type\": [\"application/json*\"]\n\t\t}\n\t}\n}\n```\n\n## Caddyfile\n\nThis module has Caddyfile support. It registers the `replace` directive. Make sure to [order](https://caddyserver.com/docs/caddyfile/directives#directive-order) the handler directive in the correct place in the middleware chain; usually this works well:\n\n```\n{\n\torder replace after encode\n}\n```\n\nSyntax:\n\n```\nreplace [\u003cmatcher\u003e] [stream | [re] \u003csearch\u003e \u003creplace\u003e] {\n\tstream\n\tmatch {\n\t\theader Content-Type application/json*\n\t}\n\t[re] \u003csearch\u003e \u003creplace\u003e\n}\n```\n\n- `re` indicates a regular expression instead of substring.\n- `stream` enables streaming mode.\n- `match` defines a [response matcher](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy#response-matcher). If defined, replacements in this directive will only be performed on responses that match the matcher.\n- Note that you can use a matcher token to filter which requests have replacements performed.\n\nSimple substring substitution:\n\n```\nreplace Foo Bar\n```\n\nRegex replacement:\n\n```\nreplace re \"\\s+foo(bar|baz)\\s+\" \" foo $1 \"\n```\n\nStreaming mode:\n\n```\nreplace stream {\n\tFoo Bar\n}\n```\n\nMultiple replacements:\n\n```\nreplace {\n\tFoo Bar\n\tre \"\\s+foo(bar|baz)\\s+\" \" foo $1 \"\n\tA B\n}\n```\n\n## Limitations:\n\n- Regex matches longer than 2kb will not be replaced.\n\n- Compressed responses (e.g. from an upstream proxy which gzipped the response body) will not be decoded before attempting to replace. To work around this, you may send the `Accept-Encoding: identity` request header to the upstream to tell it not to compress the response. For example:\n\n      reverse_proxy localhost:8080 {\n          header_up Accept-Encoding identity\n      }\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaddyserver%2Freplace-response","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcaddyserver%2Freplace-response","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaddyserver%2Freplace-response/lists"}