A microservices gateway coordinates ingress traffic for distributed services without requiring a full service mesh. It handles north-south traffic (public internet to your services) with JWT authentication and intelligent routing, providing a central place to route, secure, and monitor distributed microservices. With this setup, you can:
  • Handle public API traffic with JWT authentication and rate limiting
  • Route requests to different microservices based on URL paths
  • Apply consistent authentication and authorization policies
  • Deploy and test services independently without rewriting routing logic
  • Debug distributed request flows from a central gateway

1. Install the ngrok Kubernetes Operator

Install the ngrok Kubernetes Operator in your cluster. For detailed installation instructions, see the Kubernetes CRDs getting started guide.

2. Create AgentEndpoints for your microservices

Create AgentEndpoint CRDs to expose your microservices. These create internal endpoints that are not publicly accessible but can be routed through your gateway. User service:
apiVersion: ngrok.k8s.ngrok.com/v1alpha1
kind: AgentEndpoint
metadata:
  name: user-service
  namespace: default
spec:
  url: https://user-service.internal
  upstream:
    url: http://user-service.default.svc.cluster.local:8080
  description: "User service"
Order service:
apiVersion: ngrok.k8s.ngrok.com/v1alpha1
kind: AgentEndpoint
metadata:
  name: order-service
  namespace: default
spec:
  url: https://order-service.internal
  upstream:
    url: http://order-service.default.svc.cluster.local:8080
  description: "Order service"
Notification service:
apiVersion: ngrok.k8s.ngrok.com/v1alpha1
kind: AgentEndpoint
metadata:
  name: notification-service
  namespace: default
spec:
  url: https://notification-service.internal
  upstream:
    url: http://notification-service.default.svc.cluster.local:8080
  description: "Notification service"
Apply these configurations:
kubectl apply -f user-service.yaml
kubectl apply -f order-service.yaml
kubectl apply -f notification-service.yaml

3. Reserve a domain

Navigate to the Domains section of the ngrok dashboard and click New + to reserve a free static domain like https://your-service.ngrok.app or a custom domain you already own. We’ll refer to this domain as $NGROK_DOMAIN from here on out.

4. Create a Cloud Endpoint

Navigate to the Endpoints section of the ngrok dashboard, then click New + and Cloud Endpoint. In the URL field, enter the domain you just reserved to finish creating your Cloud Endpoint.

5. (Optional) Create secrets for authentication

Store your JWT validation keys using Traffic Policy Secrets.
# Create vault for microservices auth
ngrok api vaults create --name "microservices-auth" --description "Microservices authentication secrets"

# Add JWT validation key
ngrok api secrets create \
  --name "jwt-public-key" \
  --value "-----BEGIN PUBLIC KEY-----\nYour JWT validation public key here\n-----END PUBLIC KEY-----" \
  --vault-id "$VAULT_ID"

6. Apply Traffic Policy to your Cloud Endpoint

Configure your Cloud Endpoint with JWT authentication and routing to your microservices. Make sure you change each of the following values:
  • $JWT_ISSUER: Replace with your JWT issuer URL
  • $JWT_AUDIENCE: Replace with your JWT audience claim
  • $JWT_PUBLIC_KEY_URL: Replace with your JWKS endpoint URL
  • Service URLs: Replace with your actual internal service endpoints
on_http_request:
  # WAF-like protection using OWASP CRS
  - actions:
      - type: owasp-crs-request
        config:
          on_error: halt

  # JWT validation for public API access
  - actions:
      - type: jwt-validation
        config:
          issuer:
            allow_list:
              - value: "https://$JWT_ISSUER"
          audience:
            allow_list:
              - value: "$JWT_AUDIENCE"
          http:
            tokens:
              - type: "access_token"
                method: "header"
                name: "Authorization"
                prefix: "Bearer "
          jws:
            allowed_algorithms: ["RS256", "ES256"]
            keys:
              sources:
                additional_jkus: ["$JWT_PUBLIC_KEY_URL"]

  # Rate limiting per JWT subject
  - actions:
      - type: rate-limit
        config:
          name: "API rate limiting per user"
          algorithm: "sliding_window"
          capacity: 1000
          rate: "1h"
          bucket_key: ["actions.ngrok.jwt_validation.tokens[0].payload.sub"]

  # Route to user service
  - expressions:
      - "req.url.path.startsWith('/api/users')"
    actions:
      - type: forward-internal
        config:
          url: https://user-service.internal

  # Route to order service
  - expressions:
      - "req.url.path.startsWith('/api/orders')"
    actions:
      - type: forward-internal
        config:
          url: https://order-service.internal

  # Route to notification service
  - expressions:
      - "req.url.path.startsWith('/api/notifications')"
    actions:
      - type: forward-internal
        config:
          url: https://notification-service.internal

on_http_response:
  # OWASP protection for responses
  - actions:
      - type: owasp-crs-response
        config:
          on_error: halt

  # Add service tracing headers
  - actions:
      - type: add-headers
        config:
          headers:
            x-service-version: "v1.0"
            x-processed-at: "${time.now}"
            x-client-ip: "${conn.client_ip}"
What’s happening here? This policy creates a secure, centralized API gateway for your microservices architecture. On every HTTP request, the policy first applies OWASP Core Rule Set protection to block malicious requests like SQL injection and XSS attacks, then validates JWT tokens to ensure only authenticated users can access your APIs, and applies per-user rate limiting based on the JWT subject to prevent abuse. The gateway intelligently routes traffic to the appropriate microservice based on URL paths—user management requests go to the user service, order-related requests to the order service, and notification requests to the notification service. On responses, it applies OWASP protection to scan outgoing content for vulnerabilities and adds service tracing headers for observability and debugging across your distributed system.

7. Try out your microservices gateway

Visit the domain you reserved either in the browser or in the terminal using a tool like curl. You should see the app or service at the port connected to your internal Agent Endpoint. Test the public API gateway:
# Get a JWT token (replace with your auth provider)
JWT_TOKEN="your_jwt_token_here"

# Test user service
curl -H "Authorization: Bearer $JWT_TOKEN" \
     "https://$NGROK_DOMAIN/api/users/123"

# Test order service
curl -H "Authorization: Bearer $JWT_TOKEN" \
     "https://$NGROK_DOMAIN/api/orders/456"

# Test notification service
curl -H "Authorization: Bearer $JWT_TOKEN" \
     "https://$NGROK_DOMAIN/api/notifications/789"

What’s next?