# Configure external authentication Type of document: How-to guide Product: FABRIC --- This guide describes how to configure external authentication in NGINX Gateway Fabric using the `ExternalAuth` filter on an HTTPRoute. External authentication delegates the authorization decision for each request to an external service. NGINX issues a subrequest to that service before proxying the original request, and forwards the request only if the service responds with a 2xx status. Following these instructions to create two sample applications and compare the behavior of each: - `coffee` endpoint: Protected by an `ExternalAuth` filter. - `tea` endpoint: Exposed without any external authentication filter. ## Overview The `ExternalAuth` filter is declared in the `filters` list of an HTTPRoute rule. When NGINX processes a request that matches the rule, it first sends a subrequest to the backend referenced by the filter. Based on the status returned by that backend, NGINX either forwards the original request to the route's `backendRefs` or returns the error status to the client. Each route rule supports only one `ExternalAuth` filter. If your authentication flow requires multiple checks, consolidate them into a single authentication service that performs all the necessary validations. The filter translates to NGINX's [ngx_http_auth_request_module](https://nginx.org/en/docs/http/ngx_http_auth_request_module.html) directives: - [`auth_request`](https://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request) — Sends a subrequest to the specified URI and grants or denies access based on the response status. - [`auth_request_set`](https://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request_set) — Captures a value from the authentication response and stores it in a variable for use in the main request. ## Note on Gateway API Experimental Features **important:** ExternalAuth is a Gateway API resource from the experimental release channel. To use Gateway API experimental resources, the Gateway API resources from the experimental channel must be installed before deploying NGINX Gateway Fabric. Additionally, NGINX Gateway Fabric must have experimental features enabled. **Note:** As noted in the [Gateway API documentation](https://gateway-api.sigs.k8s.io/guides/#install-experimental-channel), future releases of the Gateway API can include breaking changes to experimental resources and fields. To install the Gateway API resources from the experimental channel, run the following: ```shell kubectl kustomize "https://github.com/nginx/nginx-gateway-fabric/config/crd/gateway-api/experimental?ref=v" | kubectl apply -f - ``` **Note:** If you plan to use the `edge` version of NGINX Gateway Fabric, you can replace the version in `ref` with `main`, for example `ref=main`. To enable experimental features on NGINX Gateway Fabric: Using Helm: Set `nginxGateway.gwAPIExperimentalFeatures.enable` to true. An example can be found in the [Installation with Helm](/ngf/install/helm.md#custom-installation-options) guide. Using Kubernetes manifests: Add the `--gateway-api-experimental-features` command-line flag to the deployment manifest args. An example can be found in the [Installation with Kubernetes manifests](/ngf/install/manifests.md#3-deploy-nginx-gateway-fabric) guide. ## Before you begin - [Install](/ngf/install/) NGINX Gateway Fabric with experimental features enabled. ## Deploy sample applications Run the following `kubectl apply` command to create the `coffee` and `tea` deployments and services: ```yaml kubectl apply -f - < ``` ## Deploy the external authentication server This sample authentication service is an NGINX deployment that checks the `X-Api-Key` request header. If the header value is `my-custom-secret`, the server responds with `200 OK`; otherwise it responds with `401 Unauthorized`. ```yaml kubectl apply -f - <<'EOF' apiVersion: v1 kind: ConfigMap metadata: name: ext-auth-config data: default.conf: | server { listen 8080; location / { if ($http_x_api_key != "my-custom-secret") { return 401 "unauthorized"; } return 200 "ok"; } } --- apiVersion: apps/v1 kind: Deployment metadata: name: ext-auth-server spec: replicas: 1 selector: matchLabels: app: ext-auth-server template: metadata: labels: app: ext-auth-server spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 8080 volumeMounts: - name: config mountPath: /etc/nginx/conf.d volumes: - name: config configMap: name: ext-auth-config --- apiVersion: v1 kind: Service metadata: name: ext-auth-server spec: ports: - port: 80 targetPort: 8080 protocol: TCP name: http selector: app: ext-auth-server EOF ``` ## Configure routing with the ExternalAuth filter Run the following `kubectl apply` command to create an HTTPRoute for `coffee` and `tea` applications. The `coffee` route uses an `ExternalAuth` filter to require authentication, while the `tea` route is exposed without one: ```yaml kubectl apply -f - < 401 Authorization Required

401 Authorization Required


nginx
``` Access `/coffee` with a valid API key: ```shell curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -H "X-Api-Key: my-custom-secret" ``` ```text Server address: 10.244.0.151:8080 Server name: coffee-654ddf664b-l9ml5 Date: 16/Apr/2026:20:14:28 +0000 URI: /coffee Request ID: 217931bc5fe27254d1821cec91e1f2d8 ``` The `X-Api-Key` header is listed in `allowedHeaders` so that it reaches the authentication server, which responds `200 OK`. NGINX then proxies the request to the `coffee` backend. Access `/tea`, which has no `ExternalAuth` filter and responds normally: ```shell curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/tea ``` ```text Server address: 10.244.0.149:8080 Server name: tea-75bc9f4b6d-q5wg5 Date: 16/Apr/2026:20:14:41 +0000 URI: /tea Request ID: d27f6ef4edc2f1e09bb455824ac67a07 ``` ### Exceed the body size limit Because `forwardBody.maxSize: 1024` is applied as `client_max_body_size` on the `/coffee` location, any client request with a body larger than 1024 bytes is rejected with `413 Request Entity Too Large` before the authorization subrequest runs. Send a 1100-byte body to demonstrate this: ```shell BODY=$(head -c 1100 /dev/zero | tr '\0' 'x') curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -X POST -H "X-Api-Key: my-custom-secret" -d "$BODY" ``` ```text 413 Request Entity Too Large

413 Request Entity Too Large


nginx
``` `client_max_body_size` can also be set on a route through a [ClientSettingsPolicy](/ngf/traffic-management/client-settings.md) via its `body.maxSize` field. If a ClientSettingsPolicy with `body.maxSize` is attached to the same HTTPRoute as an `ExternalAuth` filter that sets `forwardBody.maxSize`, the HTTPRoute is marked invalid with reason `InvalidFilter`. ## Troubleshooting - If the HTTPRoute is not accepted, run `kubectl describe httproute coffee` and check the `Status` conditions for validation errors. - If every request returns `401`, confirm that the authentication server is reachable from the NGINX pod and that the `backendRef` name, namespace, and port are correct. - If a required request header cannot reach the authentication server, confirm it is listed in `http.allowedHeaders`. - If a response header from the authentication server cannot reach the backend, confirm it is listed in `http.allowedResponseHeaders`. - If a request is rejected with `413 Request Entity Too Large`, raise `forwardBody.maxSize` to accommodate the client body. - If the HTTPRoute reports `ResolvedRefs: False` with an `InvalidFilter` reason mentioning `body.maxSize`, remove either the `ExternalAuth` filter's `forwardBody.maxSize` or the ClientSettingsPolicy's `body.maxSize` as they both cannot be set on the same route. ## Further reading - [NGINX HTTP auth request module](https://nginx.org/en/docs/http/ngx_http_auth_request_module.html) - [Gateway API HTTPExternalAuthFilter specification](https://gateway-api.sigs.k8s.io/reference/spec/#httpexternalauthfilter)