Application routes using HTTP matching conditions
Learn how to deploy multiple applications and HTTPRoutes with request conditions such as paths, methods, headers, and query parameters
In this guide we will configure advanced routing rules for multiple applications. These rules will showcase request matching by path (including prefix, exact, and regex patterns), headers, query parameters, and method. For an introduction to exposing your application, we recommend that you follow the basic guide first.
The following image shows the traffic flow that we will be creating with these rules.
graph LR users[Users] ngfSvc["Public Endpoint
for
cafe.example.com"] subgraph cluster [Kubernetes Cluster] subgraph appNs [Namespace
default] subgraph nsPadding [" "] nginxPod[Pod
NGINX] coffeeV1Pod[Pod
coffee v1] coffeeV2Pod[Pod
coffee v2] teaPod[Pod
tea] teaPostPod[Pod
tea-post] end end end ngfSvc --> nginxPod nginxPod --/coffee--> coffeeV1Pod nginxPod --/coffee
header: version=v2
OR
/coffee?TEST=v2--> coffeeV2Pod nginxPod --GET /tea--> teaPod nginxPod --POST /tea--> teaPostPod users --> ngfSvc class clusterPadding,nsPadding,clusterPadding2 noBorder class gwNS,appNs namespace class ngfSvc,nginxPod nginxNode class coffeeV1Pod,coffeeV2Pod coffeeNode class teaPod,teaPostPod teaNode classDef noBorder stroke:none,fill:none classDef default fill:#FFFFFF,stroke:#000000 classDef namespace fill:#FFFFFF,stroke:#036ffc,stroke-dasharray: 5 5,text-align:center classDef nginxNode fill:#b4e0ad,stroke:#2AA317 classDef coffeeNode fill:#edbd8c,stroke:#D9822B classDef teaNode fill:#ff8f6a,stroke:#e5805f
The goal is to create a set of rules that will result in client requests being sent to specific backends based on the request attributes. In this diagram, we have two versions of the coffee service. Traffic for v1 needs to be directed to the old application, while traffic for v2 needs to be directed towards the new application. We also have two tea services, one that handles GET operations and one that handles POST operations. Both the tea and coffee applications share the same Gateway.
- Install NGINX Gateway Fabric.
Begin by deploying the coffee-v1, coffee-v2 and coffee-v3 applications:
kubectl apply -f https://raw.githubusercontent.com/nginx/nginx-gateway-fabric/v2.2.2/examples/advanced-routing/coffee.yamlThe gateway resource is typically deployed by the cluster operator. To deploy the gateway:
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: cafe spec: gatewayClassName: nginx listeners: - name: http port: 80 protocol: HTTP EOFThis gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. After creating the Gateway resource, NGINX Gateway Fabric will provision an NGINX Pod and Service fronting it to route traffic.
Save the public IP address and port of the NGINX Service into shell variables:
GW_IP=XXX.YYY.ZZZ.III GW_PORT=<port number>In a production environment, you should have a DNS record for the external IP address that is exposed, and it should refer to the hostname that the gateway will forward for.
The HTTPRoute is typically deployed by the application developer. To deploy the coffee HTTPRoute:
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: coffee spec: parentRefs: - name: cafe sectionName: http hostnames: - cafe.example.com rules: - matches: - path: type: PathPrefix value: /coffee backendRefs: - name: coffee-v1-svc port: 80 - matches: - path: type: PathPrefix value: /coffee headers: - name: version value: v2 - path: type: PathPrefix value: /coffee queryParams: - name: TEST value: v2 backendRefs: - name: coffee-v2-svc port: 80 - matches: - path: type: PathPrefix value: /coffee headers: - name: headerRegex type: RegularExpression value: "header-[a-z]{1}" - path: type: PathPrefix value: /coffee queryParams: - name: queryRegex type: RegularExpression value: "query-[a-z]{1}" backendRefs: - name: coffee-v3-svc port: 80 EOFThis HTTPRoute has a few important properties:
-
The
parentRefsreferences the gateway resource that we created, and specifically defines thehttplistener to attach to, via thesectionNamefield. -
cafe.example.comis the hostname that is matched for all requests to the backends defined in this HTTPRoute. -
The first rule defines that all requests with the path prefix
/coffeeand no other matching conditions are sent to thecoffee-v1Service. -
The second rule defines two matching conditions. If either of these conditions match, requests are forwarded to the
coffee-v2Service:- Request with the path prefix
/coffeeand headerversion=v2. - Request with the path prefix
/coffeeand the query parameterTEST=v2.
The match type isExactfor both header and query param, by default. - Request with the path prefix
-
The third rule defines two matching conditions. If either of these conditions match, requests are forwarded to the
coffee-v3Service:- Request with the path prefix
/coffeeand headerHeaderRegex=Header-[a-z]{1}. - Request with the path prefix
/coffeeand the query parameterQueryRegex=Query-[a-z]{1}.
The match type used here isRegularExpression. A request will succeed if the header or query parameter value matches the specified regular expression.If you want both conditions to be required, you can define headers and queryParams in the same match object.
- Request with the path prefix
Using the external IP address and port for the NGINX Service, we can send traffic to our coffee applications.
If you have a DNS record allocated forcafe.example.com, you can send the request directly to that hostname, without needing to resolve.
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffeeThis request should receive a response from the coffee-v1 Pod.
Server address: 10.244.0.9:8080 Server name: coffee-v1-76c7c85bbd-cf8nzIf we want our request to be routed to coffee-v2, then we need to meet the defined conditions. We can include a header:
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -H "version:v2"or include a query parameter:
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee?TEST=v2Either request should result in a response from the coffee-v2 Pod.
Server address: 10.244.0.9:8080 Server name: coffee-v2-68bd55f798-s9z5qIf we want our request to be routed to coffee-v3, then we need to meet the defined conditions. We can include a header matching the regular expression:
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -H "headerRegex:header-a"or include a query parameter matching the regular expression:
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee?queryRegex=query-aEither request should result in a response from the coffee-v3 Pod.
Server address: 10.244.0.104:8080 Server name: coffee-v3-66d58645f4-6zsl2Let’s deploy a different set of applications now called tea and tea-post. These applications will have their own set of rules, but will still attach to the same gateway listener as the coffee apps.
kubectl apply -f https://raw.githubusercontent.com/nginx/nginx-gateway-fabric/v2.2.2/examples/advanced-routing/tea.yamlWe are reusing the previous gateway for these applications, so all we need to create is the HTTPRoute.
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: tea spec: parentRefs: - name: cafe hostnames: - cafe.example.com rules: - matches: - path: type: PathPrefix value: /tea method: POST backendRefs: - name: tea-post-svc port: 80 - matches: - path: type: PathPrefix value: /tea method: GET backendRefs: - name: tea-svc port: 80 EOFThe properties of this HTTPRoute include:
- The same gateway is referenced as before.
- The same hostname is used as with the
coffeeapps. - The first rule defines that a POST request to the
/teapath is routed to thetea-postService. - The second rule defines that a GET request to the
/teapath is routed to theteaService.
Using the external IP address and port for the NGINX Service, we can send traffic to our tea applications.
If you have a DNS record allocated forcafe.example.com, you can send the request directly to that hostname, without needing to resolve.
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/teaThis GET request should receive a response from the tea Pod.
Server address: 10.244.0.10:8080 Server name: tea-df5655878-5fmfgIf we want our request to be routed to tea-post, then we need to send a POST request:
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/tea -X POSTServer address: 10.244.0.7:8080 Server name: tea-post-b59b8596b-g586rThis request should receive a response from the tea-post pod. Any other type of method, such as PATCH, will result in a 404 Not Found response.
NGINX Gateway Fabric supports three types of path matching:
- PathPrefix: Matches based on a URL path prefix split by
/. For example,/coffeematches/coffee,/coffee/, and/coffee/latte. - Exact: Matches the exact path in the request. For example,
/coffeematches only/coffee. - RegularExpression: Matches based on RE2-compatible regular expressions. For example,
/coffee/[a-z]+matches/coffee/latteand/coffee/mochabut not/coffee/123.
Regular expression path matching uses the RE2 syntax. Patterns are automatically anchored to the beginning of the path.
To route requests based on regex patterns in the path, use type: RegularExpression:
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: coffee-regex spec: parentRefs: - name: cafe hostnames: - cafe.example.com rules: - matches: - path: type: RegularExpression value: /coffee/[a-z]+ backendRefs: - name: coffee-v1-svc port: 80 EOFThis configuration routes requests like /coffee/latte or /coffee/mocha to the coffee-v1-svc backend, while paths like /coffee/123 or /coffee will not match.
You can test the regex path matching with curl:
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee/latteThis request should receive a response from the coffee-v1 Pod since /coffee/latte matches the pattern /coffee/[a-z]+.
Server address: 10.244.0.9:8080 Server name: coffee-v1-76c7c85bbd-cf8nzHowever, a request with a numeric path segment will not match:
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee/123This will result in a 404 Not Found response since /coffee/123 does not match the pattern /coffee/[a-z]+.
If you have any issues while sending traffic, try the following to debug your configuration and setup:
-
Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX service. Refer to the Installation guides for more information.
-
Check the status of the Gateway:
kubectl describe gateway cafeThe Gateway status should look like this:
text Status: Addresses: Type: IPAddress Value: 10.244.0.85 Conditions: Last Transition Time: 2023-08-15T20:57:21Z Message: Gateway is accepted Observed Generation: 1 Reason: Accepted Status: True Type: Accepted Last Transition Time: 2023-08-15T20:57:21Z Message: Gateway is programmed Observed Generation: 1 Reason: Programmed Status: True Type: Programmed Listeners: Attached Routes: 2 Conditions: Last Transition Time: 2023-08-15T20:57:21Z Message: Listener is accepted Observed Generation: 1 Reason: Accepted Status: True Type: Accepted Last Transition Time: 2023-08-15T20:57:21Z Message: Listener is programmed Observed Generation: 1 Reason: Programmed Status: True Type: Programmed Last Transition Time: 2023-08-15T20:57:21Z Message: All references are resolved Observed Generation: 1 Reason: ResolvedRefs Status: True Type: ResolvedRefs Last Transition Time: 2023-08-15T20:57:21Z Message: No conflicts Observed Generation: 1 Reason: NoConflicts Status: False Type: Conflicted Name: httpCheck that the conditions match and that the attached routes for the
httplistener equals 2. If it is less than 2, there may be an issue with the routes. -
Check the status of the HTTPRoutes:
kubectl describe httproute coffeekubectl describe httproute teaEach HTTPRoute status should look like this:
text Status: Parents: Conditions: Last Transition Time: 2023-08-15T20:57:21Z Message: The route is accepted Observed Generation: 1 Reason: Accepted Status: True Type: Accepted Last Transition Time: 2023-08-15T20:57:21Z Message: All references are resolved Observed Generation: 1 Reason: ResolvedRefs Status: True Type: ResolvedRefs Controller Name: gateway.nginx.org/nginx-gateway-controller Parent Ref: Group: gateway.networking.k8s.io Kind: Gateway Name: cafe Namespace: defaultCheck for any error messages in the conditions.
To learn more about the Gateway API and the resources we created in this guide, check out the following Kubernetes documentation resources: