Header manipulation
Set up an external processing (ExtProc) server that manipulates request headers for a sample app.
External processing is an Enterprise-only feature.
-
Before you begin, install Gloo Gateway Enterprise in your cluster.
-
Set up the ExtProc server. This example uses a prebuilt ExtProc server that manipulates request and response headers based on instructions that are sent in an
instructionsheader.kubectl apply -f- <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: ext-proc-grpc spec: selector: matchLabels: app: ext-proc-grpc replicas: 1 template: metadata: labels: app: ext-proc-grpc spec: containers: - name: ext-proc-grpc image: gcr.io/solo-test-236622/ext-proc-example-basic-sink:0.0.2 imagePullPolicy: IfNotPresent ports: - containerPort: 18080 --- apiVersion: v1 kind: Service metadata: name: ext-proc-grpc labels: app: ext-proc-grpc annotations: gloo.solo.io/h2_service: "true" spec: ports: - port: 4444 targetPort: 18080 protocol: TCP selector: app: ext-proc-grpc EOFThe
instructionsheader must be provided as a JSON string in the following format:{ "addHeaders": { "header1": "value1", "header2": "value2" }, "removeHeaders": [ "header3", "header4" ], } } -
Verify that the ExtProc server is up and running.
kubectl get podsExample output:
NAME READY STATUS RESTARTS AGE ext-proc-grpc-59d44ddf76-42q2x 1/1 Running 0 24m -
Edit the default
Settingscustom resource to enable ExtProc in Gloo Gateway.kubectl edit settings default -n gloo-systemAdd the following ExtProc settings to the
specsection. This example configures the standardextProcfilter, which runs in the middle of the Envoy filter chain. Gloo Gateway also supportsextProcEarly(runs early in the filter chain) andextProcLate(runs as the final filter before a request leaves Envoy). For more information, see ExtProc filter variants.extProc: grpcService: extProcServerRef: name: default-ext-proc-grpc-4444 namespace: gloo-system filterStage: stage: AuthZStage predicate: After failureModeAllow: false allowModeOverride: false processingMode: requestHeaderMode: SEND responseHeaderMode: SKIPSetting Description grpcServiceThe configuration of the external processing server that you created earlier. grpcService.exProcServerRef.nameThe name of the upstream that was created for the ExtProc server. grpcService.exProcServerRef.namespaceThe namespace of the upstream that was created for the ExtProc server. filterStageWhere in the filter chain you want to apply the external processing. Applies to extProcEarlyandextProc. Has no effect onextProcLate, which always runs as the final filter.failureModeAllowAllow the ExtProc server to continue when an error is detected during external processing. If set to true, the ExtProc server continues. If set tofalse, external processing is stopped and an error is returned to the Envoy proxy.allowModeOverrideAllow the ExtProc server to override the processing mode settings that you set. Default value is false.processingModeDecide how you want the ExtProc server to process request and response information. processingMode.requestHeaderModeSend ( SEND) or skip sending (SKIP) request header information to the ExtProc server.processingMode.responseHeaderModeSend ( SEND) or skip sending (SKIP) response header information to the ExtProc server. -
Deploy the
httpbinsample app.kubectl apply -f- <<EOF apiVersion: v1 kind: ServiceAccount metadata: name: httpbin --- apiVersion: v1 kind: Service metadata: name: httpbin labels: app: httpbin spec: ports: - name: http port: 8000 targetPort: 80 selector: app: httpbin --- apiVersion: apps/v1 kind: Deployment metadata: name: httpbin spec: replicas: 1 selector: matchLabels: app: httpbin version: v1 template: metadata: labels: app: httpbin version: v1 spec: serviceAccountName: httpbin containers: - image: docker.io/kennethreitz/httpbin imagePullPolicy: IfNotPresent name: httpbin ports: - containerPort: 80 EOF -
Verify that the httpbin pod is up an running.
kubectl get pods | grep httpbin -
Create a virtual service to expose the httpbin app on the gateway.
kubectl apply -f- <<EOF apiVersion: gateway.solo.io/v1 kind: VirtualService metadata: name: vs namespace: gloo-system spec: virtualHost: domains: - '*' routes: - matchers: - prefix: / routeAction: single: upstream: name: default-httpbin-8000 namespace: gloo-system EOF -
Send a simple request to the httpbin app and make sure that you get back a 200 HTTP response code. The following request passes in two headers
header1andheader2that you see in your reponse.curl -vik $(glooctl proxy url)/get -H "header1: value1" -H "header2: value2"Example output:
HTTP/1.1 200 OK ... { "args": {}, "headers": { "Accept": "*/*", "Header1": "value1", "Header2": "value2", "Host": "example.com.com", "User-Agent": "curl/7.77.0", "X-Envoy-Expected-Rq-Timeout-Ms": "15000" }, "origin": "10.0.11.109", "url": "http://example.com/get" } -
Send another request to the httpbin app. This time, you pass along instructions for the ExtProc server in an
instructionheader. In this example, you use the ExtProc server to add theheader3header, and to removeheader2.curl -vik $(glooctl proxy url)/get -H "header1: value1" -H "header2: value2" -H 'instructions: {"addHeaders":{"header3":"value3","header4":"value4"},"removeHeaders":["instructions", "header2"]}'Example output:
{ "args": {}, "headers": { "Accept": "*/*", "Header1": "value1", "Header3": "value3", "Header4": "value4", "Host": "example.com", "User-Agent": "curl/7.77.0", "X-Envoy-Expected-Rq-Timeout-Ms": "15000" }, "origin": "10.0.11.109", "url": "http://example.com/get" } -
Edit the
Settingsresource again and setrequestHeaderMode: SKIP. This setting instructs the ExtProc filter to not send any request headers to the ExtProc server.kubectl edit settings default -n gloo-system -
Send the same request to the httpbin app. Note that this time,
header2is not removed andheader3andheader4are not added to the request, because the request headers are not sent to the ExtProc server.curl -vik $(glooctl proxy url)/get -H "header1: value1" -H "header2: value2" -H 'instructions: {"addHeaders":{"header3":"value3","header4":"value4"},"removeHeaders":["instructions", "header2"]}'Example output:
{ "args": {}, "headers": { "Accept": "*/*", "Header1": "value1", "Header2": "value2", "Host": "example.com", "Instructions": "{\"addHeaders\":{\"header3\":\"value3\",\"header4\":\"value4\"},\"removeHeaders\":[\"instructions\", \"header2\"]}", "User-Agent": "curl/7.77.0", "X-Envoy-Expected-Rq-Timeout-Ms": "15000" }, "origin": "10.0.11.109", "url": "http://example.com/get" }
Use multiple extProc filter variants
You can configure extProcEarly and extProcLate alongside extProc to run multiple external processors at different stages of the filter chain. For example, you might want to debug your extProc server by logging requests at both the earliest and latest stages so that you can compare what changed in between. You can also use this setup to integrate with different extProc servers.
-
Update the
defaultSettings resource to configure all three extProc variants. In this example, all extProc variants use the same extProc server. However, you can configure a specific extProc server for each phase. The extProc stages are processed as follows:- extProcEarly: Requests are modified before the
FaultEnvoy filter. TheFaultfilter is the first filter in the filter chain. For more information, see Filter flow description. - extProc: Requests are modified after the
AuthZEnvoy filter. - extProcLate: Requests are modified in the
upstream_http_filterthat is part of the Router phase. Note that although you must provide afilterStagesetting, this setting is ignored as theextProcLatevariant is always executed as part of theupstream_http_filterfilter.
kubectl edit settings default -n gloo-systemUpdate your settings as follows:
extProcEarly: grpcService: extProcServerRef: name: default-ext-proc-grpc-4444 namespace: gloo-system filterStage: stage: FaulStage predicate: Before processingMode: requestHeaderMode: SEND responseHeaderMode: SEND extProc: grpcService: extProcServerRef: name: default-ext-proc-grpc-4444 namespace: gloo-system filterStage: stage: AuthZStage predicate: After failureModeAllow: false allowModeOverride: false processingMode: requestHeaderMode: SEND responseHeaderMode: SKIP extProcLate: # The filter stage is ignored as it is always executed in the upstream_http_filter. filterStage: stage: AuthZStage predicate: After grpcService: extProcServerRef: name: default-ext-proc-grpc-4444 namespace: gloo-system processingMode: requestHeaderMode: SEND responseHeaderMode: SEND - extProcEarly: Requests are modified before the
-
Create a virtual service to expose the httpbin app on the gateway.
kubectl apply -f- <<EOF apiVersion: gateway.solo.io/v1 kind: VirtualService metadata: name: vs namespace: gloo-system spec: virtualHost: domains: - '*' routes: - matchers: - prefix: / routeAction: single: upstream: name: default-httpbin-8000 namespace: gloo-system EOF -
Send a request to the httpbin app.
curl -vik $(glooctl proxy url --name gateway-proxy)/get -H "header1: value1"Verify that you get back a 200 HTTP response code.
HTTP/1.1 200 OK -
Check the extProc server logs to verify that it received 3 processing requests (one for each stage). Because each variant sends request headers (
requestHeaderMode: SEND), the server receives three separate gRPC processing calls per request.kubectl logs -l app=ext-proc-grpc --tail=50Example output:
"Wed, 18 Mar 2026 19:57:41 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:207" Starting gRPC server on port ":18080" "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:55" Process "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:78" Got RequestHeaders "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:182" Sending ProcessingResponse "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:55" Process "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:78" Got RequestHeaders "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:182" Sending ProcessingResponse "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:55" Process "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:78" Got RequestHeaders "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:182" Sending ProcessingResponse "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:137" Got ResponseHeaders "Thu, 19 Mar 2026 13:14:48 UTC: /Users/ben/Documents/solo-repos/ext-proc-examples/basic-sink/main.go:182" Sending ProcessingResponse
Cleanup
You can optionally remove the resources that you created as part of this guide.
kubectl delete deployment ext-proc-grpc httpbin
kubectl delete service ext-proc-grpc httpbin
kubectl delete servicaccount httpbin
kubectl delete virtualservice vs -n gloo-system