Skip to main content

Feature Implementation Cookbook: 6 Gateway API Implementations

📅 Written: 2026-06-17 | Updated: 2026-06-17

info

This document is a deep-dive guide for the Gateway API Adoption Guide. It compares, with YAML examples, how the 8 major features used in NGINX Ingress are implemented in each Gateway API implementation. For solution selection, comparison matrices, and the decision tree, see Section 4 of the main guide.

Overview

This cookbook covers how to implement the following 8 features for AWS Native (LBC v3), Cilium, NGINX Gateway Fabric, Envoy Gateway, and kGateway. URL Rewrite and header manipulation are Gateway API v1 standard features that work identically across all implementations.

#FeatureStandard?
1Authentication (Basic Auth replacement)Per-implementation
2Rate LimitingPer-implementation
3IP Control (IP Allowlist)Per-implementation
4URL RewriteGateway API v1 standard
5Header ManipulationGateway API v1 standard
6Session Affinity (cookie-based)Per-implementation
7Request Body Size LimitPer-implementation
8Custom Error PagesPer-implementation

1. Authentication (Basic Auth replacement)

# Native JWT verification with AWS LBC v3
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: jwt-protected-route
namespace: production
spec:
parentRefs:
- name: production-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: ExtensionRef
extensionRef:
group: eks.amazonaws.com
kind: JWTAuthorizer
name: cognito-authorizer
backendRefs:
- name: api-service
port: 8080

---
# JWTAuthorizer CRD (LBC v3 extension)
apiVersion: eks.amazonaws.com/v1
kind: JWTAuthorizer
metadata:
name: cognito-authorizer
spec:
issuer: https://cognito-idp.us-west-2.amazonaws.com/us-west-2_ABC123
audiences:
- api-gateway-client
claimsToHeaders:
- claim: sub
header: x-user-id
- claim: email
header: x-user-email

2. Rate Limiting

Limitation

AWS Native (LBC v3) does not support gateway-level native rate limiting. IP-based request limiting is implemented using AWS WAF Rate-based Rules.

# Attach a WAF Rate-based Rule to the ALB
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
annotations:
# Rate limiting WAF ACL ARN
aws.load-balancer.waf-acl-arn: arn:aws:wafv2:us-west-2:123456789012:regional/webacl/rate-limit/a1b2c3d4
spec:
gatewayClassName: aws-alb
listeners:
- name: http
port: 80
protocol: HTTP

Create a WAF Rate-based Rule with ACK (AWS Controllers for Kubernetes):

Using the ACK WAFv2 controller, WAF resources can be managed declaratively as Kubernetes manifests.

Enable ACK with EKS Capabilities (recommended):

Using EKS Capabilities (GA in November 2025), the ACK controller can run as a fully AWS-managed component. Because the controller runs on AWS-managed infrastructure, no separate pod is deployed on the worker nodes.

# 1. Create the IAM Capability Role
aws iam create-role \
--role-name EKS-ACK-Capability-Role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "eks.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "aws:SourceAccount": "<ACCOUNT_ID>" }
}
}]
}'

# Attach the WAFv2 permission policy
aws iam put-role-policy \
--role-name EKS-ACK-Capability-Role \
--policy-name ACK-WAFv2-Policy \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["wafv2:*"],
"Resource": "*"
}]
}'

# 2. Create the ACK Capability on the EKS cluster
aws eks create-capability \
--cluster-name my-eks-cluster \
--capability-type ACK \
--capability-configuration '{
"capabilityRoleArn": "arn:aws:iam::<ACCOUNT_ID>:role/EKS-ACK-Capability-Role"
}'

# 3. Verify CRD registration
kubectl get crds | grep wafv2
Alternative: install directly with Helm (non-EKS environments)

For non-EKS environments or when the controller must be self-managed, it can be installed with Helm.

helm install ack-wafv2-controller \
oci://public.ecr.aws/aws-controllers-k8s/wafv2-chart \
--namespace ack-system \
--create-namespace \
--set aws.region=ap-northeast-2

With this approach the controller is deployed as a pod on the worker nodes, and permissions are managed via IRSA (IAM Roles for Service Accounts).

# ACK WAFv2 WebACL - Rate-based Rule definition
apiVersion: wafv2.services.k8s.aws/v1alpha1
kind: WebACL
metadata:
name: rate-limit-acl
namespace: production
spec:
name: rate-limit-acl
scope: REGIONAL
defaultAction:
allow: {}
rules:
- name: ip-rate-limit
priority: 1
action:
block: {}
statement:
rateBasedStatement:
limit: 500 # max requests per 5 minutes (100~2,000,000,000)
aggregateKeyType: IP # aggregate by IP
visibilityConfig:
sampledRequestsEnabled: true
cloudWatchMetricsEnabled: true
metricName: ip-rate-limit
visibilityConfig:
sampledRequestsEnabled: true
cloudWatchMetricsEnabled: true
metricName: rate-limit-acl
# Attach the created WebACL ARN to the Gateway
# After WebACL creation, find the ARN in status.ackResourceMetadata.arn:
# kubectl get webacl rate-limit-acl -n production \
# -o jsonpath='{.status.ackResourceMetadata.arn}'
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
annotations:
aws.load-balancer.waf-acl-arn: <WebACL ARN>
spec:
gatewayClassName: aws-alb
listeners:
- name: http
port: 80
protocol: HTTP
ACK WAFv2 Controller Requirements
  • The ACK WAFv2 controller requires IAM permissions such as wafv2:CreateWebACL, wafv2:UpdateWebACL, wafv2:DeleteWebACL, and wafv2:GetWebACL
  • When using EKS Capabilities: attach the WAFv2 permissions to the IAM Capability Role. The controller runs on AWS-managed infrastructure
  • When using Helm install: grant least privilege via IRSA (IAM Roles for Service Accounts) or EKS Pod Identity
  • The WebACL and the ALB must be in the same region

3. IP Control (IP Allowlist)

# Attach WAF to ALB Ingress (LBC v3)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
annotations:
aws.load-balancer.waf-acl-arn: arn:aws:wafv2:us-west-2:123456789012:regional/webacl/ip-allowlist/a1b2c3d4
spec:
gatewayClassName: aws-alb
listeners:
- name: http
port: 80
protocol: HTTP

Create a WAF IP Allowlist with ACK (AWS Controllers for Kubernetes):

Using the ACK WAFv2 controller, IPSet and WebACL can be managed declaratively as Kubernetes manifests.

# 1. ACK WAFv2 IPSet - define the list of allowed IPs
apiVersion: wafv2.services.k8s.aws/v1alpha1
kind: IPSet
metadata:
name: allowed-ips
namespace: production
spec:
name: allowed-ips
scope: REGIONAL
ipAddressVersion: IPV4
addresses:
- "10.0.0.0/8" # internal VPC
- "192.168.1.0/24" # office network
- "203.0.113.100/32" # specific allowed IP
# 2. ACK WAFv2 WebACL - IPSet-based allowlist rule
# After IPSet creation, find the ARN in status.ackResourceMetadata.arn:
# kubectl get ipset allowed-ips -n production \
# -o jsonpath='{.status.ackResourceMetadata.arn}'
apiVersion: wafv2.services.k8s.aws/v1alpha1
kind: WebACL
metadata:
name: ip-allowlist-acl
namespace: production
spec:
name: ip-allowlist-acl
scope: REGIONAL
defaultAction:
block: {} # block by default, only the allowlist passes
rules:
- name: allow-trusted-ips
priority: 1
action:
allow: {}
statement:
ipSetReferenceStatement:
arn: <IPSet ARN> # ARN of the allowed-ips IPSet
visibilityConfig:
sampledRequestsEnabled: true
cloudWatchMetricsEnabled: true
metricName: allow-trusted-ips
visibilityConfig:
sampledRequestsEnabled: true
cloudWatchMetricsEnabled: true
metricName: ip-allowlist-acl
# 3. Attach the created WebACL ARN to the Gateway
# After WebACL creation, find the ARN in status.ackResourceMetadata.arn:
# kubectl get webacl ip-allowlist-acl -n production \
# -o jsonpath='{.status.ackResourceMetadata.arn}'
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
annotations:
aws.load-balancer.waf-acl-arn: <WebACL ARN>
spec:
gatewayClassName: aws-alb
listeners:
- name: http
port: 80
protocol: HTTP
ACK WAFv2 IPSet Management Tips
  • Updating the addresses field of the IPSet causes the ACK controller to automatically sync the AWS WAF IPSet
  • Combined with GitOps (ArgoCD/Flux), IP changes can be managed via PRs
  • The IPSet and WebACL must be in the same region, and wafv2:*IPSet* and wafv2:*WebACL* permissions are required (EKS Capabilities: IAM Capability Role / Helm: IRSA)

4. URL Rewrite

Gateway API Standard

URL Rewrite is a Gateway API v1 standard feature that works identically across all implementations.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-rewrite
namespace: production
spec:
parentRefs:
- name: production-gateway
rules:
# /api/v1/users → /users
- matches:
- path:
type: PathPrefix
value: /api/v1
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: api-service
port: 8080

# /old-api/users → /v2/users
- matches:
- path:
type: PathPrefix
value: /old-api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /v2
backendRefs:
- name: api-service-v2
port: 8080

5. Header Manipulation

Gateway API Standard

Header manipulation is a Gateway API v1 standard feature that works identically across all implementations.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-manipulation
spec:
parentRefs:
- name: production-gateway
rules:
- matches:
- path:
value: /api
filters:
# Add request headers
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Custom-Header
value: "gateway-api"
- name: X-Forwarded-Proto
value: "https"
remove:
- Authorization # remove existing Authorization
# Add response headers
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: X-Server
value: "gateway-api"
- name: Strict-Transport-Security
value: "max-age=31536000; includeSubDomains"
backendRefs:
- name: api-service
port: 8080
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: sticky-session
annotations:
aws.load-balancer.target-group.stickiness.enabled: "true"
aws.load-balancer.target-group.stickiness.type: "lb_cookie"
aws.load-balancer.target-group.stickiness.duration: "3600"
spec:
parentRefs:
- name: production-gateway
rules:
- backendRefs:
- name: api-service
port: 8080

7. Request Body Size Limit

Limitation

Use an AWS WAF Rule to limit request body size (Console/CloudFormation configuration).

# Attach a WAF Body Size Limit Rule to the ALB
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
annotations:
aws.load-balancer.waf-acl-arn: arn:aws:wafv2:us-west-2:123456789012:regional/webacl/body-size-limit/a1b2c3d4
spec:
gatewayClassName: aws-alb
listeners:
- name: http
port: 80
protocol: HTTP

Create a WAF Body Size Rule with ACK (AWS Controllers for Kubernetes):

Using the ACK WAFv2 controller, body size limit rules can be managed declaratively as Kubernetes manifests.

# ACK WAFv2 WebACL - Body Size Limit Rule definition
apiVersion: wafv2.services.k8s.aws/v1alpha1
kind: WebACL
metadata:
name: body-size-limit-acl
namespace: production
spec:
name: body-size-limit-acl
scope: REGIONAL
defaultAction:
allow: {}
rules:
- name: block-large-body
priority: 1
action:
block: {}
statement:
sizeConstraintStatement:
fieldToMatch:
body:
oversizeHandling: MATCH # also match oversized bodies
comparisonOperator: GT
size: 10485760 # 10MB (in bytes)
textTransformations:
- priority: 0
type: NONE
visibilityConfig:
sampledRequestsEnabled: true
cloudWatchMetricsEnabled: true
metricName: block-large-body
visibilityConfig:
sampledRequestsEnabled: true
cloudWatchMetricsEnabled: true
metricName: body-size-limit-acl
# Attach the created WebACL ARN to the Gateway
# After WebACL creation, find the ARN in status.ackResourceMetadata.arn:
# kubectl get webacl body-size-limit-acl -n production \
# -o jsonpath='{.status.ackResourceMetadata.arn}'
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
annotations:
aws.load-balancer.waf-acl-arn: <WebACL ARN>
spec:
gatewayClassName: aws-alb
listeners:
- name: http
port: 80
protocol: HTTP
Consolidate Rules into a Single WebACL

If you use IP Allowlist, Rate Limiting, and Body Size limits all together, you do not need to create separate WebACLs for each — you can consolidate multiple rules into a single WebACL, distinguished by priority. Since only one WebACL can be attached per ALB, consolidated management is essential.

8. Custom Error Pages

# Use the ALB Fixed Response action
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: error-response
namespace: production
annotations:
# Configure a fixed response with an ALB action annotation
alb.ingress.kubernetes.io/actions.error-503: |
{
"type": "fixed-response",
"fixedResponseConfig": {
"contentType": "text/html",
"statusCode": "503",
"messageBody": "<html><body><h1>Service Under Maintenance</h1><p>Please try again later.</p></body></html>"
}
}
spec:
parentRefs:
- name: production-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /maintenance
backendRefs:
- name: error-503 # action name defined in the annotation
kind: Service
port: 503

References

Official Documentation