# Table of Contents - [Apache APISIX Documentation | APISIX & API7 API Gateway Docs](#apache-apisix-documentation-apisix-api7-api-gateway-docs) - [Apache APISIX Documentation | APISIX & API7 API Gateway Docs](#apache-apisix-documentation-apisix-api7-api-gateway-docs) - [Apache APISIX Documentation | APISIX & API7 API Gateway Docs](#apache-apisix-documentation-apisix-api7-api-gateway-docs) - [Ingress Controller Documentation | APISIX & API7 API Gateway Docs](#ingress-controller-documentation-apisix-api7-api-gateway-docs) - [Get APISIX | APISIX & API7 API Gateway Docs](#get-apisix-apisix-api7-api-gateway-docs) - [Install APISIX on ROSA | APISIX & API7 API Gateway Docs](#install-apisix-on-rosa-apisix-api7-api-gateway-docs) - [Monitor APISIX Metrics with Prometheus | APISIX & API7 API Gateway Docs](#monitor-apisix-metrics-with-prometheus-apisix-api7-api-gateway-docs) - [Built-In Variables | APISIX & API7 API Gateway Docs](#built-in-variables-apisix-api7-api-gateway-docs) - [Routes | APISIX & API7 API Gateway Docs](#routes-apisix-api7-api-gateway-docs) - [Enterprise Features Overview | APISIX & API7 API Gateway Docs](#enterprise-features-overview-apisix-api7-api-gateway-docs) - [Overview | APISIX & API7 API Gateway Docs](#overview-apisix-api7-api-gateway-docs) - [Plugin Hub | APISIX & API7 API Gateway Docs](#plugin-hub-apisix-api7-api-gateway-docs) - [APISIX & API7 API Gateway Docs](#apisix-api7-api-gateway-docs) - [APISIX & API7 API Gateway Docs](#apisix-api7-api-gateway-docs) - [API Declarative CLI (ADC) | APISIX & API7 API Gateway Docs](#api-declarative-cli-adc-apisix-api7-api-gateway-docs) - [API7 Enterprise Admin APIs | APISIX & API7 API Gateway Docs](#api7-enterprise-admin-apis-apisix-api7-api-gateway-docs) - [Configure Routes | APISIX & API7 API Gateway Docs](#configure-routes-apisix-api7-api-gateway-docs) - [API7 Enterprise Admin APIs | APISIX & API7 API Gateway Docs](#api7-enterprise-admin-apis-apisix-api7-api-gateway-docs) - [Plugins | APISIX & API7 API Gateway Docs](#plugins-apisix-api7-api-gateway-docs) - [Load Balancing | APISIX & API7 API Gateway Docs](#load-balancing-apisix-api7-api-gateway-docs) - [API7 Enterprise Admin APIs | APISIX & API7 API Gateway Docs](#api7-enterprise-admin-apis-apisix-api7-api-gateway-docs) - [AI AWS Content Moderation | APISIX & API7 API Gateway Docs](#ai-aws-content-moderation-apisix-api7-api-gateway-docs) - [Install APISIX with Docker | APISIX & API7 API Gateway Docs](#install-apisix-with-docker-apisix-api7-api-gateway-docs) - [AI Aliyun Content Moderation | APISIX & API7 API Gateway Docs](#ai-aliyun-content-moderation-apisix-api7-api-gateway-docs) - [Integrate with HashiCorp Consul | APISIX & API7 API Gateway Docs](#integrate-with-hashicorp-consul-apisix-api7-api-gateway-docs) - [AI Prompt Decorator | APISIX & API7 API Gateway Docs](#ai-prompt-decorator-apisix-api7-api-gateway-docs) - [Gateway Groups | APISIX & API7 API Gateway Docs](#gateway-groups-apisix-api7-api-gateway-docs) - [Port Reference | APISIX & API7 API Gateway Docs](#port-reference-apisix-api7-api-gateway-docs) - [APISIX Expressions | APISIX & API7 API Gateway Docs](#apisix-expressions-apisix-api7-api-gateway-docs) - [Rate Limiting | APISIX & API7 API Gateway Docs](#rate-limiting-apisix-api7-api-gateway-docs) - [AI Prompt Template | APISIX & API7 API Gateway Docs](#ai-prompt-template-apisix-api7-api-gateway-docs) - [Monitor APISIX Metrics with Datadog | APISIX & API7 API Gateway Docs](#monitor-apisix-metrics-with-datadog-apisix-api7-api-gateway-docs) - [Log with ClickHouse | APISIX & API7 API Gateway Docs](#log-with-clickhouse-apisix-api7-api-gateway-docs) - [Service Release | APISIX & API7 API Gateway Docs](#service-release-apisix-api7-api-gateway-docs) - [Plugin Common Configurations | APISIX & API7 API Gateway Docs](#plugin-common-configurations-apisix-api7-api-gateway-docs) - [How APISIX Works | APISIX & API7 API Gateway Docs](#how-apisix-works-apisix-api7-api-gateway-docs) - [Serve Static Resources | APISIX & API7 API Gateway Docs](#serve-static-resources-apisix-api7-api-gateway-docs) - [AI RAG | APISIX & API7 API Gateway Docs](#ai-rag-apisix-api7-api-gateway-docs) - [Graphql Proxy Cache | APISIX & API7 API Gateway Docs](#graphql-proxy-cache-apisix-api7-api-gateway-docs) - [Proxy Mirror | APISIX & API7 API Gateway Docs](#proxy-mirror-apisix-api7-api-gateway-docs) - [SOAP | APISIX & API7 API Gateway Docs](#soap-apisix-api7-api-gateway-docs) - [Attach Consumer Label | APISIX & API7 API Gateway Docs](#attach-consumer-label-apisix-api7-api-gateway-docs) - [Key Authentication | APISIX & API7 API Gateway Docs](#key-authentication-apisix-api7-api-gateway-docs) - [Plugin Configs | APISIX & API7 API Gateway Docs](#plugin-configs-apisix-api7-api-gateway-docs) - [Services | APISIX & API7 API Gateway Docs](#services-apisix-api7-api-gateway-docs) - [Proxy Cache | APISIX & API7 API Gateway Docs](#proxy-cache-apisix-api7-api-gateway-docs) - [Fault Injection | APISIX & API7 API Gateway Docs](#fault-injection-apisix-api7-api-gateway-docs) - [Traffic Label | APISIX & API7 API Gateway Docs](#traffic-label-apisix-api7-api-gateway-docs) - [Exit transformer | APISIX & API7 API Gateway Docs](#exit-transformer-apisix-api7-api-gateway-docs) - [Multi Auth | APISIX & API7 API Gateway Docs](#multi-auth-apisix-api7-api-gateway-docs) - [Response Rewrite | APISIX & API7 API Gateway Docs](#response-rewrite-apisix-api7-api-gateway-docs) - [Request ID | APISIX & API7 API Gateway Docs](#request-id-apisix-api7-api-gateway-docs) - [degraphql | APISIX & API7 API Gateway Docs](#degraphql-apisix-api7-api-gateway-docs) - [Configuration Files | APISIX & API7 API Gateway Docs](#configuration-files-apisix-api7-api-gateway-docs) - [APISIX CLI | APISIX & API7 API Gateway Docs](#apisix-cli-apisix-api7-api-gateway-docs) - [Environment Variables | APISIX & API7 API Gateway Docs](#environment-variables-apisix-api7-api-gateway-docs) - [Canary Deployment | APISIX & API7 API Gateway Docs](#canary-deployment-apisix-api7-api-gateway-docs) - [Mocking | APISIX & API7 API Gateway Docs](#mocking-apisix-api7-api-gateway-docs) - [OAS Validator | APISIX & API7 API Gateway Docs](#oas-validator-apisix-api7-api-gateway-docs) - [UA Restriction | APISIX & API7 API Gateway Docs](#ua-restriction-apisix-api7-api-gateway-docs) - [gRPC Web | APISIX & API7 API Gateway Docs](#grpc-web-apisix-api7-api-gateway-docs) - [gRPC Transcode | APISIX & API7 API Gateway Docs](#grpc-transcode-apisix-api7-api-gateway-docs) - [Request Validation | APISIX & API7 API Gateway Docs](#request-validation-apisix-api7-api-gateway-docs) - [AI Request Rewrite | APISIX & API7 API Gateway Docs](#ai-request-rewrite-apisix-api7-api-gateway-docs) - [Datadog | APISIX & API7 API Gateway Docs](#datadog-apisix-api7-api-gateway-docs) - [Secret Providers | APISIX & API7 API Gateway Docs](#secret-providers-apisix-api7-api-gateway-docs) - [Serverless Functions | APISIX & API7 API Gateway Docs](#serverless-functions-apisix-api7-api-gateway-docs) - [Graphql Limit Count | APISIX & API7 API Gateway Docs](#graphql-limit-count-apisix-api7-api-gateway-docs) - [Proxy Rewrite | APISIX & API7 API Gateway Docs](#proxy-rewrite-apisix-api7-api-gateway-docs) - [Upstreams | APISIX & API7 API Gateway Docs](#upstreams-apisix-api7-api-gateway-docs) - [Authz Keycloak | APISIX & API7 API Gateway Docs](#authz-keycloak-apisix-api7-api-gateway-docs) - [OpenTelemetry | APISIX & API7 API Gateway Docs](#opentelemetry-apisix-api7-api-gateway-docs) - [SkyWalking | APISIX & API7 API Gateway Docs](#skywalking-apisix-api7-api-gateway-docs) - [SAML Auth | APISIX & API7 API Gateway Docs](#saml-auth-apisix-api7-api-gateway-docs) - [Zipkin | APISIX & API7 API Gateway Docs](#zipkin-apisix-api7-api-gateway-docs) - [OpenAPI to MCP | APISIX & API7 API Gateway Docs](#openapi-to-mcp-apisix-api7-api-gateway-docs) - [Forward Auth | APISIX & API7 API Gateway Docs](#forward-auth-apisix-api7-api-gateway-docs) - [IP Restriction | APISIX & API7 API Gateway Docs](#ip-restriction-apisix-api7-api-gateway-docs) - [Chaitin WAF | APISIX & API7 API Gateway Docs](#chaitin-waf-apisix-api7-api-gateway-docs) - [ClickHouse Logger | APISIX & API7 API Gateway Docs](#clickhouse-logger-apisix-api7-api-gateway-docs) - [CORS | APISIX & API7 API Gateway Docs](#cors-apisix-api7-api-gateway-docs) - [Back Up and Restore etcd | APISIX & API7 API Gateway Docs](#back-up-and-restore-etcd-apisix-api7-api-gateway-docs) - [Credentials | APISIX & API7 API Gateway Docs](#credentials-apisix-api7-api-gateway-docs) - [Trace Requests with Zipkin | APISIX & API7 API Gateway Docs](#trace-requests-with-zipkin-apisix-api7-api-gateway-docs) - [AWS Lambda | APISIX & API7 API Gateway Docs](#aws-lambda-apisix-api7-api-gateway-docs) - [Public API | APISIX & API7 API Gateway Docs](#public-api-apisix-api7-api-gateway-docs) - [Error Log Logger | APISIX & API7 API Gateway Docs](#error-log-logger-apisix-api7-api-gateway-docs) - [Traffic Split | APISIX & API7 API Gateway Docs](#traffic-split-apisix-api7-api-gateway-docs) - [Error Page | APISIX & API7 API Gateway Docs](#error-page-apisix-api7-api-gateway-docs) - [MQTT Proxy | APISIX & API7 API Gateway Docs](#mqtt-proxy-apisix-api7-api-gateway-docs) - [JWE Decrypt | APISIX & API7 API Gateway Docs](#jwe-decrypt-apisix-api7-api-gateway-docs) - [Prometheus | APISIX & API7 API Gateway Docs](#prometheus-apisix-api7-api-gateway-docs) - [APISIX Model Context Protocol (APISIX-MCP) | APISIX & API7 API Gateway Docs](#apisix-model-context-protocol-apisix-mcp-apisix-api7-api-gateway-docs) - [Limit Req | APISIX & API7 API Gateway Docs](#limit-req-apisix-api7-api-gateway-docs) - [Proxy Buffering | APISIX & API7 API Gateway Docs](#proxy-buffering-apisix-api7-api-gateway-docs) - [Real IP | APISIX & API7 API Gateway Docs](#real-ip-apisix-api7-api-gateway-docs) - [Loki Logger | APISIX & API7 API Gateway Docs](#loki-logger-apisix-api7-api-gateway-docs) - [syslog | APISIX & API7 API Gateway Docs](#syslog-apisix-api7-api-gateway-docs) - [Splunk HEC Logging | APISIX & API7 API Gateway Docs](#splunk-hec-logging-apisix-api7-api-gateway-docs) - [HTTP Logger | APISIX & API7 API Gateway Docs](#http-logger-apisix-api7-api-gateway-docs) - [SkyWalking Logger | APISIX & API7 API Gateway Docs](#skywalking-logger-apisix-api7-api-gateway-docs) - [Google Cloud Logging | APISIX & API7 API Gateway Docs](#google-cloud-logging-apisix-api7-api-gateway-docs) - [Build Your Own Docker Images | APISIX & API7 API Gateway Docs](#build-your-own-docker-images-apisix-api7-api-gateway-docs) - [Plugin Global Rules | APISIX & API7 API Gateway Docs](#plugin-global-rules-apisix-api7-api-gateway-docs) - [Plugin Metadata | APISIX & API7 API Gateway Docs](#plugin-metadata-apisix-api7-api-gateway-docs) - [Log with Elasticsearch | APISIX & API7 API Gateway Docs](#log-with-elasticsearch-apisix-api7-api-gateway-docs) - [OPA | APISIX & API7 API Gateway Docs](#opa-apisix-api7-api-gateway-docs) - [Kafka Logger | APISIX & API7 API Gateway Docs](#kafka-logger-apisix-api7-api-gateway-docs) - [ACL | APISIX & API7 API Gateway Docs](#acl-apisix-api7-api-gateway-docs) - [Consumer Restriction | APISIX & API7 API Gateway Docs](#consumer-restriction-apisix-api7-api-gateway-docs) - [Anonymous Consumers | APISIX & API7 API Gateway Docs](#anonymous-consumers-apisix-api7-api-gateway-docs) - [Router Options | APISIX & API7 API Gateway Docs](#router-options-apisix-api7-api-gateway-docs) - [Set Breakpoints | APISIX & API7 API Gateway Docs](#set-breakpoints-apisix-api7-api-gateway-docs) - [Log Consumer Label in Access Log | APISIX & API7 API Gateway Docs](#log-consumer-label-in-access-log-apisix-api7-api-gateway-docs) - [Elasticsearch Logger | APISIX & API7 API Gateway Docs](#elasticsearch-logger-apisix-api7-api-gateway-docs) - [Body Transformer | APISIX & API7 API Gateway Docs](#body-transformer-apisix-api7-api-gateway-docs) - [Integrate with Netflix Eureka | APISIX & API7 API Gateway Docs](#integrate-with-netflix-eureka-apisix-api7-api-gateway-docs) - [JWT Auth | APISIX & API7 API Gateway Docs](#jwt-auth-apisix-api7-api-gateway-docs) - [Data Mask | APISIX & API7 API Gateway Docs](#data-mask-apisix-api7-api-gateway-docs) - [Limit Count Advanced | APISIX & API7 API Gateway Docs](#limit-count-advanced-apisix-api7-api-gateway-docs) - [Use Debug Mode | APISIX & API7 API Gateway Docs](#use-debug-mode-apisix-api7-api-gateway-docs) - [Organization and RBAC | APISIX & API7 API Gateway Docs](#organization-and-rbac-apisix-api7-api-gateway-docs) - [Basic Auth | APISIX & API7 API Gateway Docs](#basic-auth-apisix-api7-api-gateway-docs) - [AI Rate Limiting | APISIX & API7 API Gateway Docs](#ai-rate-limiting-apisix-api7-api-gateway-docs) - [Audit and Rollback | APISIX & API7 API Gateway Docs](#audit-and-rollback-apisix-api7-api-gateway-docs) - [Consumer Groups | APISIX & API7 API Gateway Docs](#consumer-groups-apisix-api7-api-gateway-docs) - [Permission Policies and Boundaries | APISIX & API7 API Gateway Docs](#permission-policies-and-boundaries-apisix-api7-api-gateway-docs) - [Consumers | APISIX & API7 API Gateway Docs](#consumers-apisix-api7-api-gateway-docs) - [Secrets | APISIX & API7 API Gateway Docs](#secrets-apisix-api7-api-gateway-docs) - [Dashboard SSO Options | APISIX & API7 API Gateway Docs](#dashboard-sso-options-apisix-api7-api-gateway-docs) - [Stream Routes | APISIX & API7 API Gateway Docs](#stream-routes-apisix-api7-api-gateway-docs) - [Protos | APISIX & API7 API Gateway Docs](#protos-apisix-api7-api-gateway-docs) - [Autoscale APISIX Gateway (AWS EC2) | APISIX & API7 API Gateway Docs](#autoscale-apisix-gateway-aws-ec2-apisix-api7-api-gateway-docs) - [Workflow | APISIX & API7 API Gateway Docs](#workflow-apisix-api7-api-gateway-docs) - [AI Prompt Guard | APISIX & API7 API Gateway Docs](#ai-prompt-guard-apisix-api7-api-gateway-docs) - [Key Auth | APISIX & API7 API Gateway Docs](#key-auth-apisix-api7-api-gateway-docs) - [Credentials | APISIX & API7 API Gateway Docs](#credentials-apisix-api7-api-gateway-docs) - [Enterprise Plugins | APISIX & API7 API Gateway Docs](#enterprise-plugins-apisix-api7-api-gateway-docs) - [Custom Plugins and Sandbox | APISIX & API7 API Gateway Docs](#custom-plugins-and-sandbox-apisix-api7-api-gateway-docs) - [Alerts and Contact Points | APISIX & API7 API Gateway Docs](#alerts-and-contact-points-apisix-api7-api-gateway-docs) - [API Portal | APISIX & API7 API Gateway Docs](#api-portal-apisix-api7-api-gateway-docs) - [Security Hardening | APISIX & API7 API Gateway Docs](#security-hardening-apisix-api7-api-gateway-docs) - [HMAC Auth | APISIX & API7 API Gateway Docs](#hmac-auth-apisix-api7-api-gateway-docs) - [Set Up Ingress Controller and Gateway | APISIX & API7 API Gateway Docs](#set-up-ingress-controller-and-gateway-apisix-api7-api-gateway-docs) - [Admin API Key | APISIX & API7 API Gateway Docs](#admin-api-key-apisix-api7-api-gateway-docs) - [RocketMQ Logger | APISIX & API7 API Gateway Docs](#rocketmq-logger-apisix-api7-api-gateway-docs) --- # Apache APISIX Documentation | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page [Apache APISIX](https://api7.ai/apisix) is a dynamic, real-time, high-performance API Gateway. Originally created by [API7.ai](https://api7.ai/) , APISIX was open-sourced and donated to Apache Software Foundation in 2019, making the vision of serving half of the world's API requests possible. APISIX has hundreds of built-in features and nearly a hundred plugins, which can help developers quickly and safely process requests from APIs and microservices. If you are looking for an open-source, cloud-native API gateway for load balancing, dynamic upstream, canary release, circuit breaking, authentication, and observability, APISIX is worth a look. #### Getting started Quickstart. Provide a hands-on introduction to APISIX for developers. [Quickstart](https://docs.api7.ai/apisix/getting-started) #### How-to Guides Step-by-step guides. Cover common configurations and operations. [Guides](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus) #### Background information Concepts. Explain APISIX key concepts and how APISIX works. [Background](https://docs.api7.ai/apisix/key-concepts/routes) #### APISIX in production Production. Cover deployment, networking, security, upgrades, and more. [Production](https://docs.api7.ai/apisix/install/kubernetes/rosa) #### Plugins Plugin hub. Cover nearly 100 plugins. [Plugins](https://docs.api7.ai/hub) #### Reference Reference. Cover APISIX variables, expressions, API specifications, and CLI. [Reference](https://docs.api7.ai/apisix/reference/built-in-variables) Speak to an expert[​](https://docs.api7.ai/apisix/#speak-to-an-expert "Direct link to Speak to an expert") ----------------------------------------------------------------------------------------------------------- Do you need to know more about APISIX and its capabilities to decide whether APISIX API Gateway is suited to your requirements? Get some answers from our core developers and industry experts, with no obligations and no sales pitches - just the facts. [Book a Meeting](https://api7.ai/contact) * [Speak to an expert](https://docs.api7.ai/apisix/#speak-to-an-expert) --- # Apache APISIX Documentation | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page [Apache APISIX](https://api7.ai/apisix) is a dynamic, real-time, high-performance API Gateway. Originally created by [API7.ai](https://api7.ai/) , APISIX was open-sourced and donated to Apache Software Foundation in 2019, making the vision of serving half of the world's API requests possible. APISIX has hundreds of built-in features and nearly a hundred plugins, which can help developers quickly and safely process requests from APIs and microservices. If you are looking for an open-source, cloud-native API gateway for load balancing, dynamic upstream, canary release, circuit breaking, authentication, and observability, APISIX is worth a look. #### Getting started Quickstart. Provide a hands-on introduction to APISIX for developers. [Quickstart](https://docs.api7.ai/apisix/getting-started) #### How-to Guides Step-by-step guides. Cover common configurations and operations. [Guides](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus) #### Background information Concepts. Explain APISIX key concepts and how APISIX works. [Background](https://docs.api7.ai/apisix/key-concepts/routes) #### APISIX in production Production. Cover deployment, networking, security, upgrades, and more. [Production](https://docs.api7.ai/apisix/install/kubernetes/rosa) #### Plugins Plugin hub. Cover nearly 100 plugins. [Plugins](https://docs.api7.ai/hub) #### Reference Reference. Cover APISIX variables, expressions, API specifications, and CLI. [Reference](https://docs.api7.ai/apisix/reference/built-in-variables) Speak to an expert[​](https://docs.api7.ai/#speak-to-an-expert "Direct link to Speak to an expert") ---------------------------------------------------------------------------------------------------- Do you need to know more about APISIX and its capabilities to decide whether APISIX API Gateway is suited to your requirements? Get some answers from our core developers and industry experts, with no obligations and no sales pitches - just the facts. [Book a Meeting](https://api7.ai/contact) * [Speak to an expert](https://docs.api7.ai/#speak-to-an-expert) --- # Apache APISIX Documentation | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/documentation#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page [Apache APISIX](https://api7.ai/apisix) is a dynamic, real-time, high-performance API Gateway. Originally created by [API7.ai](https://api7.ai/) , APISIX was open-sourced and donated to Apache Software Foundation in 2019, making the vision of serving half of the world's API requests possible. APISIX has hundreds of built-in features and nearly a hundred plugins, which can help developers quickly and safely process requests from APIs and microservices. If you are looking for an open-source, cloud-native API gateway for load balancing, dynamic upstream, canary release, circuit breaking, authentication, and observability, APISIX is worth a look. #### Getting started Quickstart. Provide a hands-on introduction to APISIX for developers. [Quickstart](https://docs.api7.ai/apisix/getting-started) #### How-to Guides Step-by-step guides. Cover common configurations and operations. [Guides](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus) #### Background information Concepts. Explain APISIX key concepts and how APISIX works. [Background](https://docs.api7.ai/apisix/key-concepts/routes) #### APISIX in production Production. Cover deployment, networking, security, upgrades, and more. [Production](https://docs.api7.ai/apisix/install/kubernetes/rosa) #### Plugins Plugin hub. Cover nearly 100 plugins. [Plugins](https://docs.api7.ai/hub) #### Reference Reference. Cover APISIX variables, expressions, API specifications, and CLI. [Reference](https://docs.api7.ai/apisix/reference/built-in-variables) Speak to an expert[​](https://docs.api7.ai/apisix/documentation#speak-to-an-expert "Direct link to Speak to an expert") ------------------------------------------------------------------------------------------------------------------------ Do you need to know more about APISIX and its capabilities to decide whether APISIX API Gateway is suited to your requirements? Get some answers from our core developers and industry experts, with no obligations and no sales pitches - just the facts. [Book a Meeting](https://api7.ai/contact) * [Speak to an expert](https://docs.api7.ai/apisix/documentation#speak-to-an-expert) --- # Ingress Controller Documentation | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/ingress-controller/documentation#__docusaurus_skipToContent_fallback) * * * Version: latest On this page This documentation applies to two related tools: API7 Ingress Controller and APISIX Ingress Controller. API7 Ingress Controller is used with API7 Enterprise and APISIX Ingress Controller is used with the open-source APISIX gateway. Although these are separate tools, they share the same usage patterns and feature designs. Some features may be released at different times depending on the product, and any distinctions will be clearly identified and labeled throughout the documentation. Introduction[​](https://docs.api7.ai/ingress-controller/documentation#introduction "Direct link to Introduction") ------------------------------------------------------------------------------------------------------------------ API7 / APISIX Ingress Controller (AIC) is a Kubernetes Ingress Controller that converts Kubernetes resources—such as Ingress, Gateway API, and APISIX custom CRDs—into configurations for API7 / APISIX gateways. It enables declarative management of routing, SSL, plugins, upstreams, and other gateway resources directly from Kubernetes. You should use AIC if any of the following apply: * You are deploying APISIX or API7 Gateways in a Kubernetes environment. * You want a fully Kubernetes-native experience for managing your API Gateway. Supported Resources[​](https://docs.api7.ai/ingress-controller/documentation#supported-resources "Direct link to Supported Resources") --------------------------------------------------------------------------------------------------------------------------------------- AIC supports the following Kubernetes resources: * **Gateway API** – A modern, extensible Kubernetes-native API for traffic management. Supports multiple listeners, advanced routing, and richer traffic policies. * **Ingress** – The legacy Kubernetes-native API for basic HTTP/S routing to backend services. It does not support all advanced configurations available in Gateway API or APISIX CRDs. * **APISIX CRDs** – APISIX-specific custom resources that offer fine-grained control over routing, plugins, upstreams, and other gateway features, providing more flexibility and feature coverage. These resources are all supported by AIC and can coexist within the same Kubernetes cluster. Some features are only available through specific resource types, so care should be taken to avoid configuration conflicts when using multiple resources together. The diagram below illustrates how the Ingress Controller combines standard Gateway API resources with custom resources to define routing, traffic policies, and gateway behavior within a Kubernetes environment: ![ingress controller API resources](https://static.api7.ai/uploads/2025/05/09/iypiz8Ao_ic%201.png) How AIC Processes Kubernetes Resources[​](https://docs.api7.ai/ingress-controller/documentation#how-aic-processes-kubernetes-resources "Direct link to How AIC Processes Kubernetes Resources") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ This diagram illustrates how AIC processes Kubernetes resources. Resources—such as Ingress, Gateway API, or APISIX CRDs—are first translated into internal [ADC](https://docs.api7.ai/apisix/reference/adc) YAML, which is then synchronized to the gateway for dynamic traffic management. APISIX Ingress Controller vs API7 Ingress Controller[​](https://docs.api7.ai/ingress-controller/documentation#apisix-ingress-controller-vs-api7-ingress-controller "Direct link to APISIX Ingress Controller vs API7 Ingress Controller") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ### Resource Compatibility[​](https://docs.api7.ai/ingress-controller/documentation#resource-compatibility "Direct link to Resource Compatibility") All Kubernetes resources used with the APISIX Ingress Controller are fully compatible with the API7 Ingress Controller. No changes to the resource definitions are required. However, environment-specific configurations, such as namespaces, upstream service configuration, and the GatewayProxy connection configuration to the control plane, are expected to differ. ### Deployment Architectures[​](https://docs.api7.ai/ingress-controller/documentation#deployment-architectures "Direct link to Deployment Architectures") The architectural diagrams below use a single-controller topology to illustrate the different deployment modes of API7 and APISIX Ingress Controller. #### API7 Ingress Controller[​](https://docs.api7.ai/ingress-controller/documentation#api7-ingress-controller "Direct link to API7 Ingress Controller") API7 Ingress Controller supports a single deployment mode, in which the controller communicates directly with the API7 Control Plane (API7 Dashboard). ![API7 Ingress Controller deployment architecture with API7 Enterprise](https://static.api7.ai/uploads/2025/11/25/jXPqGXAO_API7-AIC-ARCH.png) #### APISIX Ingress Controller[​](https://docs.api7.ai/ingress-controller/documentation#apisix-ingress-controller "Direct link to APISIX Ingress Controller") APISIX Ingress Controller supports two deployment modes: standalone and traditional. ##### Standalone Mode[​](https://docs.api7.ai/ingress-controller/documentation#standalone-mode "Direct link to Standalone Mode") The standalone mode is recommended over the traditional mode (with etcd). It is designed to address the stability issues that can arise when running APISIX and etcd inside Kubernetes. ![APISIX Ingress Controller deployment architecture in standalone mode with APISIX](https://static.api7.ai/uploads/2025/11/25/TfWU6sbf_API6-AIC-ARCH-STDALN.png) ##### Traditional Mode[​](https://docs.api7.ai/ingress-controller/documentation#traditional-mode "Direct link to Traditional Mode") In the traditional deployment mode, APISIX uses etcd as its configuration center. ![APISIX Ingress Controller deployment architecture in traditional mode with APISIX](https://static.api7.ai/uploads/2025/11/25/Mdezu1R0_API6-AIC-ARCH-TRAD.png) * [Introduction](https://docs.api7.ai/ingress-controller/documentation#introduction) * [Supported Resources](https://docs.api7.ai/ingress-controller/documentation#supported-resources) * [How AIC Processes Kubernetes Resources](https://docs.api7.ai/ingress-controller/documentation#how-aic-processes-kubernetes-resources) * [APISIX Ingress Controller vs API7 Ingress Controller](https://docs.api7.ai/ingress-controller/documentation#apisix-ingress-controller-vs-api7-ingress-controller) * [Resource Compatibility](https://docs.api7.ai/ingress-controller/documentation#resource-compatibility) * [Deployment Architectures](https://docs.api7.ai/ingress-controller/documentation#deployment-architectures) --- # Get APISIX | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/getting-started#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Apache APISIX is a dynamic, real-time, and high-performance API Gateway. It is a [top-level project](https://projects.apache.org/project.html?apisix) of the Apache Software Foundation. You can use APISIX API Gateway as a traffic entrance to process all business data. It offers features including dynamic routing, dynamic upstream, dynamic certificates, A/B testing, canary release, blue-green deployment, limit rate, defense against malicious attacks, metrics, monitoring alarms, service observability, service governance, and more. This tutorial covers one installation method for you to quickly get started with APISIX: * Start APISIX in Docker with a quickstart script. Prerequisite(s)[​](https://docs.api7.ai/apisix/getting-started#prerequisites "Direct link to Prerequisite(s)") --------------------------------------------------------------------------------------------------------------- * Docker * Install [Docker](https://docs.docker.com/get-docker/) to be used in the quickstart script to create containerized **etcd** and **APISIX**. * Install [cURL](https://curl.se/) to be used in the quickstart script and to send requests to APISIX for verification. Get APISIX[​](https://docs.api7.ai/apisix/getting-started#get-apisix "Direct link to Get APISIX") -------------------------------------------------------------------------------------------------- * Docker caution To provide a better experience in this tutorial, the requirement of [Admin API key](https://docs.api7.ai/apisix/production/security/admin-api-key) is switched off by default. Please turn on the API key requirement of Admin API in the production environment. Start APISIX in Docker with the quickstart script: curl -sL "https://run.api7.ai/apisix/quickstart" | sh The script starts two Docker containers, `apisix-quickstart` and `etcd-quickstart` in the `apisix-quickstart-net` Docker network, where etcd is used to store APISIX configurations. You should see the following message once APISIX is ready: ✔ APISIX is ready! Verify Installation[​](https://docs.api7.ai/apisix/getting-started#verify-installation "Direct link to Verify Installation") ----------------------------------------------------------------------------------------------------------------------------- * Docker Send a request to see if APISIX is running: curl -sI "http://127.0.0.1:9080" | grep Server If everything is ok, you should see the APISIX version: Server: APISIX/3.15.0 APISIX is now installed and running. Next Steps[​](https://docs.api7.ai/apisix/getting-started#next-steps "Direct link to Next Steps") -------------------------------------------------------------------------------------------------- Follow the rest of the getting started tutorials to learn and compare different ways of configuring APISIX, including using: * Admin API * [API Declarative CLI (ADC)](https://docs.api7.ai/apisix/reference/adc) * [APISIX Model Context Protocol (APISIX-MCP)](https://docs.api7.ai/apisix/reference/apisix-mcp) If you would like to declaratively configure APISIX with ADC, or use natural language through LLM models to configure APISIX with APISIX-MCP, please visit their docs for installation and setup before visiting the other tutorials. Note that the APISIX instance started with the quickstart script is not optimized for production. For production installation, please see the [production installation options](https://docs.api7.ai/apisix/install/docker/) for more information. * [Prerequisite(s)](https://docs.api7.ai/apisix/getting-started#prerequisites) * [Get APISIX](https://docs.api7.ai/apisix/getting-started#get-apisix) * [Verify Installation](https://docs.api7.ai/apisix/getting-started#verify-installation) * [Next Steps](https://docs.api7.ai/apisix/getting-started#next-steps) --- # Install APISIX on ROSA | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/install/kubernetes/rosa#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page [Red Hat OpenShift Service on AWS (ROSA)](https://aws.amazon.com/rosa/) is a fully managed service that provides a simplified way to deploy and manage OpenShift clusters on AWS. Prerequisite(s)[​](https://docs.api7.ai/apisix/install/kubernetes/rosa#prerequisites "Direct link to Prerequisite(s)") ----------------------------------------------------------------------------------------------------------------------- * Have an AWS account * Have a Red Hat account * Follow the [Getting started with ROSA](https://cloud.redhat.com/learn/getting-started-red-hat-openshift-service-aws-rosa) learning path to * Set up AWS with OpenShift * Deploy a cluster * Grant the cluster admin rights to a user * Install [ROSA CLI](https://console.redhat.com/openshift/downloads) * Install [OpenShift CLI](https://docs.openshift.com/container-platform/4.11/cli_reference/openshift_cli/getting-started-cli.html) and [log in](https://docs.openshift.com/online/pro/cli_reference/get_started_cli.html#basic-setup-and-login) with the credentials of the user with cluster admin rights * Install [Helm CLI](https://helm.sh/docs/intro/install/) Create an OpenShift Project and Service Account[​](https://docs.api7.ai/apisix/install/kubernetes/rosa#create-an-openshift-project-and-service-account "Direct link to Create an OpenShift Project and Service Account") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Log in OpenShift CLI using the user account with cluster admin rights: oc login --username --password Create an OpenShift project for APISIX: oc new-project apisix Switch the default project to `apisix`: oc project apisix Create a service account `apisix-sa` for APISIX deployment: oc create sa apisix-sa Add the [security context constraint (SCC)](https://docs.openshift.com/container-platform/4.16/authentication/managing-security-context-constraints.html) `nonroot-v2` to the service account: oc adm policy add-scc-to-user nonroot-v2 -z apisix-sa Install With Helm[​](https://docs.api7.ai/apisix/install/kubernetes/rosa#install-with-helm "Direct link to Install With Helm") ------------------------------------------------------------------------------------------------------------------------------- Add APISIX Helm repository and update: helm repo add apisix https://apache.github.io/apisix-helm-charthelm repo update Install the APISIX Helm chart: helm install apisix apisix/apisix \ --set gateway.type=NodePort \ --set etcd.podSecurityContext.enabled=false \ --set etcd.containerSecurityContext.enabled=false \ --set serviceAccount.name=apisix-sa ❶ Disable `podSecurityContext` and `containerSecurityContext` for etcd to avoid running into [issues related to SCC](https://github.com/bitnami/charts/issues/12215) . ❷ Deploy with the service account `apisix-sa` to grant APISIX the required permissions. If successful, you should see a response similar to the following: NAME: apisixLAST DEPLOYED: Wed May 24 07:56:25 2023NAMESPACE: apisixSTATUS: deployed Verify Installation[​](https://docs.api7.ai/apisix/install/kubernetes/rosa#verify-installation "Direct link to Verify Installation") ------------------------------------------------------------------------------------------------------------------------------------- Check pod statuses to make sure all pods are up and running: oc get pod The response should be similar to the following with all pods `Running`: NAME READY STATUS RESTARTS AGEapisix-5899557df4-5wgcn 1/1 Running 0 6m33sapisix-etcd-0 1/1 Running 0 6m33sapisix-etcd-1 1/1 Running 0 6m33sapisix-etcd-2 1/1 Running 0 6m33s Send a request to APISIX to verify APISIX is running: oc exec -itq apisix-5899557df4-5wgcn -- curl -I http://127.0.0.1:9080 | grep Server You should see APISIX version in the response: Server: APISIX/3.13.0 You have now successfully installed APISIX on ROSA. * [Prerequisite(s)](https://docs.api7.ai/apisix/install/kubernetes/rosa#prerequisites) * [Create an OpenShift Project and Service Account](https://docs.api7.ai/apisix/install/kubernetes/rosa#create-an-openshift-project-and-service-account) * [Install With Helm](https://docs.api7.ai/apisix/install/kubernetes/rosa#install-with-helm) * [Verify Installation](https://docs.api7.ai/apisix/install/kubernetes/rosa#verify-installation) --- # Monitor APISIX Metrics with Prometheus | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Prometheus is a popular systems monitoring and alerting toolkit. It collects and stores multi-dimensional time series data like metrics with key-value paired labels. APISIX offers the capability to expose a significant number of metrics to Prometheus [with low latency](https://api7.ai/blog/1s-to-10ms-reducing-prometheus-delay-in-api-gateway) , allowing for continuous monitoring and diagnostics. This guide will show you how to enable the [`prometheus`](https://docs.api7.ai/hub/prometheus) plugin to integrate with Prometheus and Grafana services, where APISIX HTTP metrics are collected and visualized. ![APISIX Prometheus Grafana flow diagram](https://static.api7.ai/uploads/2023/05/16/5Z5bIUwF_grafana-prometheus.jpg) Prerequisite(s)[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#prerequisites "Direct link to Prerequisite(s)") --------------------------------------------------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker/) . * Install [cURL](https://curl.se/) to send requests to the services for validation. * Follow the [Getting Started tutorial](https://docs.api7.ai/apisix/getting-started/) to start a new APISIX instance in Docker or on Kubernetes. Enable Prometheus Plugin[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#enable-prometheus-plugin "Direct link to Enable Prometheus Plugin") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Enable the `prometheus` plugin globally. Alternatively, you can enable the plugin on a route. * Admin API * ADC curl -i "http://127.0.0.1:9180/apisix/admin/global_rules" -X PUT -d '{ "id": "rule-for-metrics", "plugins": { "prometheus":{} }}' APISIX gathers internal runtime metrics and exposes them through port `9091` and path `/apisix/prometheus/metrics` by default. The port and the path can be [customized in the configuration file](https://docs.api7.ai/hub/prometheus/configuration#static-configurations) . adc.yaml global_rules: prometheus: {} Synchronize the configuration to APISIX: adc sync -f adc.yaml APISIX gathers internal runtime metrics and exposes them through port `9091` and path `/apisix/prometheus/metrics` by default. The port and the path can be [customized in the configuration file](https://docs.api7.ai/hub/prometheus/configuration#static-configurations) . Send a request to the route `/apisix/prometheus/metrics` to fetch metrics from APISIX: curl "http://127.0.0.1:9091/apisix/prometheus/metrics" You should see a list of metrics similar to the following: # HELP apisix_etcd_modify_indexes Etcd modify index for APISIX keys# TYPE apisix_etcd_modify_indexes gaugeapisix_etcd_modify_indexes{key="consumers"} 0apisix_etcd_modify_indexes{key="global_rules"} 0apisix_etcd_modify_indexes{key="max_modify_index"} 16apisix_etcd_modify_indexes{key="prev_index"} 15apisix_etcd_modify_indexes{key="protos"} 0apisix_etcd_modify_indexes{key="routes"} 16apisix_etcd_modify_indexes{key="services"} 0apisix_etcd_modify_indexes{key="ssls"} 0apisix_etcd_modify_indexes{key="stream_routes"} 0apisix_etcd_modify_indexes{key="upstreams"} 0apisix_etcd_modify_indexes{key="x_etcd_index"} 16# HELP apisix_etcd_reachable Config server etcd reachable from APISIX, 0 is unreachable# TYPE apisix_etcd_reachable gaugeapisix_etcd_reachable 1...# HELP apisix_http_status HTTP status codes per service in APISIX# TYPE apisix_http_status counterapisix_http_status{code="200",route="ip",matched_uri="/ip",matched_host="",service="",consumer="",node="52.20.124.211"} 1... Configure Prometheus[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#configure-prometheus "Direct link to Configure Prometheus") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In Prometheus, targets are the endpoints that Prometheus scrapes for metrics. You can configure the APISIX metrics endpoint as a target in Prometheus to collect metrics from it. Create a configuration file `prometheus.yml`: echo 'scrape_configs: - job_name: "apisix" scrape_interval: 15s metrics_path: "/apisix/prometheus/metrics" static_configs: - targets: ["apisix-quickstart:9091"]' > prometheus.yml Start a Prometheus instance in Docker. The exposed port is mapped to `9092` on the host because `9090` is reserved for APISIX. The local configuration file `prometheus.yml` is mounted to the Prometheus container. docker run -d --name apisix-quickstart-prometheus \ -p 9092:9090 \ --network=apisix-quickstart-net \ -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \ prom/prometheus:latest You can now check if the APISIX metric endpoint state is `UP` on the Prometheus [targets page](http://localhost:9092/targets) . Prometheus will collect metrics from APISIX by scraping this endpoint. ![Prometheus](https://static.api7.ai/uploads/2023/03/02/mRbZ4Hxm_prometheus.png) Configure Grafana[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#configure-grafana "Direct link to Configure Grafana") ----------------------------------------------------------------------------------------------------------------------------------------------------------------- Grafana can visualize metrics stored in Prometheus. Start a Grafana instance on port `3000` in Docker: docker run -d --name=apisix-quickstart-grafana \ -p 3000:3000 \ --network=apisix-quickstart-net \ grafana/grafana-oss Visit [Grafana console](http://localhost:3000/) and add the Prometheus instance created above to Grafana as a data source. Configure `http://127.0.0.1:9092` in the URL. ![Grafana Data Source](https://static.api7.ai/uploads/2023/03/02/E9PNMkdv_grafana-data-source.png) The official APISIX metric dashboard is published to [Grafana dashboards](https://grafana.com/grafana/dashboards/) with ID [11719](https://grafana.com/grafana/dashboards/11719-apache-apisix/) . You can then import the dashboard into Grafana with the ID. ![Import Dashboard](https://static.api7.ai/uploads/2023/03/02/21YcUlui_grafana-import-dashboard.png) If everything is OK, the dashboard will automatically visualize metrics in real time. ![Grafana Dashboard](https://static.api7.ai/uploads/2023/03/02/8hcTkwWW_grafana-dashboard.png) Next Steps[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#next-steps "Direct link to Next Steps") -------------------------------------------------------------------------------------------------------------------------------------------- You have now learned how to monitor APISIX metrics with Prometheus and visualize them in Grafana. See the [`prometheus`](https://docs.api7.ai/hub/prometheus) plugin documentation for more configuration options. * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#prerequisites) * [Enable Prometheus Plugin](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#enable-prometheus-plugin) * [Configure Prometheus](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#configure-prometheus) * [Configure Grafana](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#configure-grafana) * [Next Steps](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus#next-steps) --- # Built-In Variables | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/built-in-variables#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page _Built-in variables_ in APISIX are pre-defined variables that can be directly referenced in configurations. They are often used in plugin configurations, route matching, and log customization. APISIX supports three types of built-in variables: * NGINX Variables * APISIX Variables * Custom Variables These variables are evaluated in [a given order](https://docs.api7.ai/apisix/reference/built-in-variables#evaluation-order) . NGINX Variables[​](https://docs.api7.ai/apisix/reference/built-in-variables#nginx-variables "Direct link to NGINX Variables") ------------------------------------------------------------------------------------------------------------------------------ NGINX provides a set of variables that can be used to access various request-specific information. Some of the commonly used variables include: * `upstream_addr` * `remote_addr` * `request_uri` * `server_name` * `uri` * `http_user_agent` See the [complete list of NGINX variables](https://nginx.org/en/docs/varindex.html) for more information. APISIX Variables[​](https://docs.api7.ai/apisix/reference/built-in-variables#apisix-variables "Direct link to APISIX Variables") --------------------------------------------------------------------------------------------------------------------------------- In addition to [NGINX variables](https://nginx.org/en/docs/varindex.html) , APISIX offers a variety of built-in variables: | Variable Name | Description | | --- | --- | | `post_arg_*` | HTTP POST form data when the content type is `application/x-www-form-urlencoded`. The asterisk is to be replaced with the actual name of the POST form data. | | `post_arg.*` | HTTP POST body parameter when the content type is `application/json`, `application/x-www-form-urlencoded`, or `multipart/form-data`. The asterisk is to be replaced with the actual name of the POST parameter. Supports JSON path-like selection, such as `post_arg.model.version` and `post_arg.messages[*].content[*].type`. | | `arg_*` | URL query string. The asterisk is to be replaced with the actual query parameter name. | | `uri_param_*` | URL parameter when APISIX uses the `radixtree_uri_with_parameter` [router](https://docs.api7.ai/apisix/reference/router-options#radixtree_uri_with_parameter)
. The asterisk is to be replaced with the actual query parameter name. | | `http_*` | HTTP request header. The asterisk is to be replaced with the actual name of the header. | | `cookie_*` | Request cookie. The asterisk is to be replaced with the actual name of the cookie. | | `balancer_ip` | Upstream server IP. | | `balancer_port` | Upstream server port. | | `consumer_name` | Consumer username. | | `consumer_group_id` | Consumer group ID. | | `graphql_name` | GraphQL [operation name](https://graphql.org/learn/queries/#operation-name)
. | | `graphql_operation` | GraphQL [operation type](https://graphql.org/learn/queries/#operation-name)
. | | `graphql_root_fields` | GraphQL [root fields](https://graphql.org/learn/execution/#root-fields-resolvers)
. | | `route_id` | Route ID. | | `route_name` | Route name. | | `service_id` | Service ID. | | `service_name` | Service name. | | `resp_body` | HTTP response body. | | `mqtt_client_id` | Client ID in MQTT protocol. | | `redis_cmd_line` | Redis command. | | `rpc_time` | RPC request round-trip time. | Custom Variables[​](https://docs.api7.ai/apisix/reference/built-in-variables#custom-variables "Direct link to Custom Variables") --------------------------------------------------------------------------------------------------------------------------------- You can also register your own variables and use them as built-in variables. For instance, you can use custom variables to customize log format in logging plugins, or use them as keys in rate limiting plugins. ### Example[​](https://docs.api7.ai/apisix/reference/built-in-variables#example "Direct link to Example") The following example demonstrates two approaches for custom variable registration and how you can leverage the variable to obtain information from a route, subsequently logging the information to a remote server. #### Create a Service[​](https://docs.api7.ai/apisix/reference/built-in-variables#create-a-service "Direct link to Create a Service") Create a service to configure `http-logger` plugin and upstream: curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "id":"srv_custom_var", "plugins": { "http-logger": { "uri": "'"${REMOTE_SERVER_ADDR}"'" } }, "upstream": { "nodes": { "httpbin.org:80": 1 } } }' #### Register a Custom Variable[​](https://docs.api7.ai/apisix/reference/built-in-variables#register-a-custom-variable "Direct link to Register a Custom Variable") You can choose to register the custom variable in the source code or use the `serverless` plugins. ##### Method 1: In the Source Code[​](https://docs.api7.ai/apisix/reference/built-in-variables#method-1-in-the-source-code "Direct link to Method 1: In the Source Code") Add the following snippet to your custom Lua file and source it to register a custom variable named `a6_route_labels`. The variable represents the value of `labels` in a request to a route, if available: local core = require "apisix.core"core.ctx.register_var("a6_route_labels", function(ctx) local route = ctx.matched_route and ctx.matched_route.value if route and route.labels then return route.labels end return nilend) [Start or reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli) accordingly. If APISIX is already running, send a PUT request to `/apisix/admin/plugins/reload` to [hot reload the plugins](https://docs.api7.ai/apisix/reference/admin-api#tag/Plugin/paths/~1apisix~1admin~1plugins~1reload/put) for changes to take effect. caution While the snippet can technically be added anywhere the code is sourced, exercise caution when modifying the APISIX core codebase to avoid any negative impact on the standard functionalities. It is recommended to keep your custom Lua code in a separate directory and source it by configuring `extra_lua_path` and `extra_lua_cpath` in the `config.yaml` [configuration file](https://docs.api7.ai/apisix/reference/configuration-files) . For more information, see [create Lua plugin guide](https://docs.api7.ai/apisix/how-to-guide/custom-plugins/create-plugin-in-lua) . ##### Method 2: In the `serverless` Plugins[​](https://docs.api7.ai/apisix/reference/built-in-variables#method-2-in-the-serverless-plugins "Direct link to method-2-in-the-serverless-plugins") You can also register a custom variable using the `serverless-pre-function` or `serverless-post-function` [serverless function plugins](https://docs.api7.ai/hub/serverless-functions) . These plugins run serverless functions before or after a specified [execution phase](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) , and you can register custom variables in these functions. Add the `serverless-pre-function` plugin to the previously created service, where the function registers the custom variable `a6_route_labels`: curl "http://127.0.0.1:9180/apisix/admin/services/srv_custom_var" -X PATCH \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "plugins": { "serverless-pre-function": { "phase": "rewrite", "functions": [ "return function() local core = require \"apisix.core\" core.ctx.register_var(\"a6_route_labels\", function(ctx) local route = ctx.matched_route and ctx.matched_route.value if route and route.labels then return route.labels end return nil end); end" ] } } }' ##### Method 3: In `_meta.prefunction` of Plugins[​](https://docs.api7.ai/apisix/reference/built-in-variables#method-3-in-_metaprefunction-of-plugins "Direct link to method-3-in-_metaprefunction-of-plugins") If you prefer to register the custom variable in the plugin without using the serverless plugin, you can use `_meta.pre_function` to configure the custom code execution prior to each [phase](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) of plugin execution. The following example will show you how to declare a `_meta.pre_function` in the [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite) plugin to extract the `user_id` in the request path, register it as a variable, and use it to compose the new request path. In order to use parameters in the route's URI, you should first update the router in the [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) to be `radixtree_uri_with_parameter` as it is not the default setting: config.yaml apisix: router: http: radixtree_uri_with_parameter Then [reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. Create a route to the httpbin service to examine the rewritten path: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \-H "X-API-KEY: ${ADMIN_API_KEY}" \-d '{ "id": "pre-func-route", "uri": "/anything/:user_id/hello", "plugins": { "proxy-rewrite": { "_meta": { "pre_function": " return function(conf, ctx) local core = require \"apisix.core\" core.ctx.register_var(\"user_id\", function(ctx) return ctx.curr_req_matched.user_id end) end" }, "uri": "/anything/$user_id/world" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' ❶ Match requests to `/anything/:user_id/hello` where `user_id` is a parameter. ❷ Customize the `_meta.pre_function` to extract the `user_id` value from the requested path and save it to a variable of the same name. ❸ Rewrite the request path to `/anything/$user_id/world`, where `$user_id` will be replaced by the variable value. Send a request to the route: curl -i "http://127.0.0.1:9080/anything/johndoe/hello" You should observe the following response, showing the request path has been rewritten partially with the `user_id`: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "text/html..." ... }, "json": null, "method": "GET", "origin": "127.0.0.1, 59.71.xxx.xxx", "url": "http://127.0.0.1/anything/johndoe/world"} #### Configure Log Format[​](https://docs.api7.ai/apisix/reference/built-in-variables#configure-log-format "Direct link to Configure Log Format") Create a [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) object to configure log format for all `http-logger` instances: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/http-logger" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "log_format": { "host": "$host", "client_ip": "$remote_addr", "labels": "$a6_route_labels" } }' ❶ `host` and `remote_addr`: [NGINX variables](https://docs.api7.ai/apisix/reference/built-in-variables#nginx-variables) . ❷ `a6_route_labels`: custom variable. #### Create a Route in Service[​](https://docs.api7.ai/apisix/reference/built-in-variables#create-a-route-in-service "Direct link to Create a Route in Service") Create a [route](https://docs.api7.ai/apisix/key-concepts/routes) in the service: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "id":"route_custom_var", "uri":"/get", "service_id": "srv_custom_var", "labels": { "key": "test_a6_route_labels" }}' ❶ `service_id`: corresponds to the previously created service. ❷ `labels`: route information to be logged with the custom variable. #### Verify Custom Variable Registration[​](https://docs.api7.ai/apisix/reference/built-in-variables#verify-custom-variable-registration "Direct link to Verify Custom Variable Registration") Send a request to verify that the custom variable logs the `labels` information in the route: curl "http://127.0.0.1:9080/get" You should see a log entry in your remote server created by a POST request with a body similar to the following: [ { "labels": { "key": "test_a6_route_labels" }, "service_id": "srv_custom_var", "client_ip": "172.21.0.1", "route_id": "route_custom_var", "host": "127.0.0.1" }] This verifies the custom variable was registered and it logs the `labels` information in a route successfully. Evaluation Order[​](https://docs.api7.ai/apisix/reference/built-in-variables#evaluation-order "Direct link to Evaluation Order") --------------------------------------------------------------------------------------------------------------------------------- APISIX evaluates variables in the given order: 1. Custom Variables 2. APISIX Variables 3. NGINX Variables If a variable is successfully sourced in custom variables, APISIX will not continue to look in APISIX variables or NGINX variables. In other words, custom variables will **overwrite variables of the same names** defined in APISIX variables or NGINX variables, to better meet requirements of your specific use cases. * [NGINX Variables](https://docs.api7.ai/apisix/reference/built-in-variables#nginx-variables) * [APISIX Variables](https://docs.api7.ai/apisix/reference/built-in-variables#apisix-variables) * [Custom Variables](https://docs.api7.ai/apisix/reference/built-in-variables#custom-variables) * [Example](https://docs.api7.ai/apisix/reference/built-in-variables#example) * [Evaluation Order](https://docs.api7.ai/apisix/reference/built-in-variables#evaluation-order) --- # Routes | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/routes#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of routes in APISIX, different routing options APISIX offers, as well as drawbacks and solutions to repetitive route configurations. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/routes#overview "Direct link to Overview") ------------------------------------------------------------------------------------------------ _Routes_ define paths to upstream services. In APISIX, a route object is used to create a route when APISIX operates on the [application layer](https://en.wikipedia.org/wiki/Application_layer) , as opposed to a [stream route](https://docs.api7.ai/apisix/key-concepts/stream-routes) object, which is used to create a route when APISIX functions as a stream proxy operating on the [transport layer](https://en.wikipedia.org/wiki/Transport_layer) . Routes are responsible for matching client requests based on configured rules, loading and executing the corresponding plugins, and forwarding requests to the specified upstream endpoints. A simple route can be set up with a path-matching URI and a corresponding upstream address. The diagram below shows an example of users sending two HTTP requests to the APISIX API gateway, which are forwarded accordingly per the configured rules in routes: ![Routes Diagram](https://static.api7.ai/uploads/2023/02/24/1yJwf7in_routes.svg) Routes are often configured with plugins as well. For example, [configuring the rate-limit plugin in a route](https://docs.api7.ai/apisix/getting-started/rate-limiting) will enable rate-limiting effects. Router Options[​](https://docs.api7.ai/apisix/key-concepts/routes#router-options "Direct link to Router Options") ------------------------------------------------------------------------------------------------------------------ APISIX offers three HTTP routing options: 1. `radixtree_host_uri` routes requests by hosts and URI paths, prioritizing hostnames over URI paths during matching. This is the default setting. It can be used to route north-south traffic between clients and servers. 2. `radixtree_uri` routes requests by hosts and URI paths, prioritizing URI paths over hostnames during matching. It can be used to route east-west traffic, such as between microservices. 3. `radixtree_uri_with_parameter` enhances `radixtree_uri` to support the use of parameter in path matching. These routing options can be configured in `conf/config.yaml` under `apisix.router.http`. For more information, see [Router Options](https://docs.api7.ai/apisix/reference/router-options) . Routes, Upstreams, and Services[​](https://docs.api7.ai/apisix/key-concepts/routes#routes-upstreams-and-services "Direct link to Routes, Upstreams, and Services") ------------------------------------------------------------------------------------------------------------------------------------------------------------------- While routes are essential in defining the paths of traffic flows, there are drawbacks to repetitive route configurations (i.e. hard coding **the same upstream addresses or plugin names** for a group of routes). During updates, the repetitive field(s) of these routes will need to be traversed and updated one by one. Configurations like this increase a lot of maintenance costs as a result, especially in large-scale systems with many routes. To address this issue, [Upstreams](https://docs.api7.ai/apisix/key-concepts/upstreams) and [Services](https://docs.api7.ai/apisix/key-concepts/services) were designed to abstract away repetitive information and reduce redundancies, following the [DRY principle](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) . Additional Resource(s)[​](https://docs.api7.ai/apisix/key-concepts/routes#additional-resources "Direct link to Additional Resource(s)") ---------------------------------------------------------------------------------------------------------------------------------------- * Getting Started - [Configure Routes](https://docs.api7.ai/apisix/getting-started/configure-routes) * Key Concepts - [Stream Routes](https://docs.api7.ai/apisix/key-concepts/stream-routes) * Admin API - [Route](https://docs.api7.ai/apisix/reference/admin-api#tag/Route) * [Overview](https://docs.api7.ai/apisix/key-concepts/routes#overview) * [Router Options](https://docs.api7.ai/apisix/key-concepts/routes#router-options) * [Routes, Upstreams, and Services](https://docs.api7.ai/apisix/key-concepts/routes#routes-upstreams-and-services) * [Additional Resource(s)](https://docs.api7.ai/apisix/key-concepts/routes#additional-resources) --- # Enterprise Features Overview | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/overview#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 [API7 Enterprise](https://api7.ai/enterprise) is designed to enhance the capabilities of Apache APISIX, offering advanced features to streamline API management, improve security, and ensure high availability. These features are tailored for organizations with demanding needs, providing robust solutions for API gateways, service releases, and credential management. With API7 Enterprise, users gain access to a comprehensive suite of tools for efficient API governance, compliance, and operational excellence. From monitoring and managing security policies to facilitating smooth integrations and automating deployment workflows, API7 Enterprise empowers organizations to manage APIs at scale with confidence. The enterprise-grade functionality ensures organizations can enhance their security posture while improving operational efficiency, ensuring the long-term success of their API strategies. This chapter introduces key features of API7 Enterprise, offering solutions for API lifecycle management, security hardening, compliance, and high availability. Whether you need to implement robust role-based access control (RBAC), manage credentials securely, or ensure continuous monitoring, API7 Enterprise equips your organization with the right tools to manage APIs at scale while maintaining control and flexibility. [#### Gateway Groups\ \ Gateway Groups allow you to manage multiple APIs as a cohesive unit, applying unified configurations and policies across services.](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups) [#### Service Release\ \ Service Release offers a controlled method for deploying new API versions, ensuring smooth transitions between environments without affecting reliability.](https://docs.api7.ai/apisix/enterprise-feature/service-release) [#### Secret Providers\ \ Secret Providers securely manage sensitive data like API keys and passwords by integrating with external systems, centralizing protection and accessibility.](https://docs.api7.ai/apisix/enterprise-feature/secret-providers) [#### Credentials\ \ Credentials decouple authentication from consumers for secure, flexible management, enabling key rotation and ensuring compliance.](https://docs.api7.ai/apisix/enterprise-feature/credentials) [#### Anonymous Consumers\ \ Anonymous Consumers allow you to manage multiple APIs as a cohesive unit, applying unified configurations and policies across services.](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers) [#### Alerts and Contact Points\ \ Alerts and Contact Points enable automated notifications for critical events, ensuring quick response and proactive monitoring of API issues.](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points) [#### Organization and RBAC\ \ Organization and RBAC manage user roles and permissions, enforcing access control over APIs and resources with defined roles and boundaries.](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac) [#### Permission Policies and Boundaries\ \ Permission Policies and Boundaries enable fine-grained access control, ensuring only authorized users can access sensitive APIs, boosting security.](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries) [#### Audit and Rollback\ \ Audit and Rollback enable comprehensive tracking of changes made to configurations and rollbacks, providing audit trails for compliance and troubleshooting.](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback) [#### Dashboard SSO\ \ Dashboard SSO streamlines user authentication through a centralized identity provider, simplifying login management across dashboards.](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso) [#### Enterprise Plugins\ \ Enterprise Plugins enhance Apache APISIX, addressing security and performance needs for extensive API management solutions.](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins) [#### Custom Plugins and Sandbox\ \ Custom Plugins and Sandbox provide the flexibility to create and test extensions for APISIX, tailoring API functionality while ensuring security and stability.](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox) [#### API Portal\ \ API Portal centralizes API documentation and management, enhancing discoverability and ensuring easy access for internal and external users.](https://docs.api7.ai/apisix/enterprise-feature/api-portal) [#### High Availability\ \ High Availability keeps your API services online with failover and redundancy, minimizing downtime and ensuring continuous access.](https://docs.api7.ai/apisix/enterprise-feature/high-availability) [#### Security Hardening\ \ Security Hardening secures your API with encryption, authentication, and access control, protecting data and ensuring compliance.](https://docs.api7.ai/apisix/enterprise-feature/security-hardening) [#### Compliance\ \ Compliance ensures adherence to regulatory requirements, such as GDPR, through auditing, logging, and access control for secure API management.](https://docs.api7.ai/apisix/enterprise-feature/compliance) --- # Overview | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/enterprise/introduction#__docusaurus_skipToContent_fallback) * * * Version: 3.9.x On this page **API7 Enterprise extends the core open-source functionality of [Apache APISIX](https://github.com/apache/apisix) to provide customized, full-lifecycle API management for enterprises.** It offers a comprehensive API management solution with **API7 Gateway** and the new **API7 Portal (Beta)**. API7 Enterprise also offers enterprise-level 24/7 support, advanced features, system integration, and SLA guarantees. What Is API7 Enterprise[​](https://docs.api7.ai/enterprise/introduction#what-is-api7-enterprise "Direct link to What Is API7 Enterprise") ------------------------------------------------------------------------------------------------------------------------------------------ API7 Enterprise is a full-lifecycle API management solution designed specifically for enterprises, offering a wide range of essential functionalities. These include multi-tenancy, Role-Based Access Control (RBAC), a developer portal, and more. With a variety of protocol transcoding plugins and professional and timely technical support, API7 Enterprise modernizes legacy applications, monetizes your APIs, and delivers products faster and more securely. Why API7 Enterprise[​](https://docs.api7.ai/enterprise/introduction#why-api7-enterprise "Direct link to Why API7 Enterprise") ------------------------------------------------------------------------------------------------------------------------------ Going beyond the core open-source capabilities, API7 Enterprise extends Apache APISIX to provide full-lifecycle API management tailored for enterprises. It delivers enterprise-grade 24/7 support, commercial features, plugin integrations, and SLA guarantees to realize the maximum potential of APISIX-driven API management. API7 Enterprise has the most requested enterprise-grade features tested and validated by the market. With robust support for security, traffic management, analytics, and developer engagement, API7 Enterprise adapts to diverse enterprise API requirements and use cases across sectors. By complementing Apache APISIX with enterprise-grade support and capabilities, API7 Enterprise enables enterprises to focus on delivering business value from APIs rather than complex management. With API7 Enterprise, enterprises can accelerate and optimize their end-to-end API lifecycle without having to build in-house expertise: * Govern API security, access, and traffic at scale * Gain visibility through analytics across the full-lifecycle * Integrate API gateways with existing systems and processes * Future-proof API infrastructure management as needs grow * Provide fine-grained traffic control with dynamic load balancing, circuit breaking, rate limiting, and so on API7 Enterprise enables enterprises to realize the full potential of APIs while addressing complexities at scale. Advantages and Highlights[​](https://docs.api7.ai/enterprise/introduction#advantages-and-highlights "Direct link to Advantages and Highlights") ------------------------------------------------------------------------------------------------------------------------------------------------ ![advantages-and-highlights](https://static.api7.ai/uploads/2023/08/29/XAO2dI0y_Advantages.PNG) * **Cloud-Native** API7 Enterprise is a cloud-native gateway that is platform-agnostic, eliminating the risk of vendor lock-in. It supports various environments including bare metal, virtual machines, Kubernetes, OpenShift, and ARM64. In addition, API7 Enterprise seamlessly integrates with other components like SkyWalking, Prometheus, Kafka, Zipkin, and others, providing comprehensive capabilities for enterprises. * **High Availability** API7 Enterprise can effortlessly handle millisecond-level configuration updates and support thousands of gateway nodes. The statelessness of gateway nodes allows for effortless scaling and resizing. * **Multiple Protocol Conversion** API7 Enterprise supports a wide range of protocols, including TCP/UDP, Dubbo, MQTT, gRPC, SOAP, and WebSocket. * **Enhanced Security and Protection** API7 Enterprise comes with a variety of built-in authentication and security capabilities, such as Basic Auth, JSON Web Token (JWT), IP blacklists/whitelists, and OAuth 2.0. * **Exceptional Performance** With its radix tree algorithm for high-performance and flexible routing, API7 Enterprise achieves impressive results on AWS 8-core servers. API7 Enterprise can handle approximately 140,000 queries per second (QPS) with a latency of around 0.2 milliseconds. * **Fully Dynamic** Modifications to gateway configurations, additions, or alterations of plugins can take effect in real-time without requiring gateway service restarts. API7 Enterprise also supports dynamic loading SSL certificates. * **Strong Extensibility** With the flexible plugin mechanism, API7 Enterprise allows for customizations catering to specific internal business requirements. It supports custom load balancing and routing algorithms and facilitates Serverless execution through runtime execution of user-defined functions. Therefore, API7 Enterprise enhances flexibility at the gateway's edge nodes. * **Comprehensive Governance** API7 Enterprise provides a rich set of governance capabilities, such as fault isolation, circuit-breaking, rate limiting, and throttling. By enabling active health checks, the gateway intelligently tracks the health status of upstream nodes and automatically filters out unhealthy nodes, enhancing overall service stability. Supported Functionalities[​](https://docs.api7.ai/enterprise/introduction#supported-functionalities "Direct link to Supported Functionalities") ------------------------------------------------------------------------------------------------------------------------------------------------ * **API Publishing** * Request routing * URI parameter matching * HTTP request header matching * HTTP request method matching * Conditional expressions * IPv6 * GeoIP location matching * Routing time-to-live (TTL) * Priority matching * Request rewrite * URI rewrite * Add, modify, and delete HTTP request headers * 301, 302 redirect * Force redirect to HTTPS * Response rewrite * Add, modify, and delete HTTP response headers * Modify HTTP response code * Modify response body * Protocol conversion * HTTP/1.1, HTTP/2 * HTTP/3 * TLS/HTTPS * MQTT * UDP * WebSocket * Dubbo * Custom Layer 4 protocol * Custom Layer 7 protocol * Canary release * Blue-green deployment * Response caching * Traffic mirroring * Circuit breaking * API circuit breaking * Service degradation * Fault injection * Traffic staining * Service Discovery * **API Consumption** * Consumer Management * **API Runtime** * Monitoring * Data throughput * Response time * Upstream response time * Status code * API call volume * Gateway instance version and status * Certificate expiration * Logging * Push to HTTP/TCP/UDP log servers * SkyWalking * Kafka * RocketMQ * ClickHouse * Syslog * Aliyun SLS * Google Cloud Logging Service * Splunk HTTP Event Collector (HEC) * Specific file on disk * Elasticsearch * Tencent Cloud CLS * Grafana Loki * Tracing * SkyWalking * Zipkin * OpenTracing * **API Security** * Request authentication * JWT * Key-auth * HMAC * Basic-auth * Keycloak * Casdoor * OpenID Connect * LDAP * Lua Casbin * Open Policy Agent * External auth servers (Auth0, Okta, etc.) * OAuth 2.0 * Rate limiting * Request limiting based on a fixed window * Request limiting based on the leaky bucket principle * Limiting concurrent requests * IP restriction * Blacklist * Whitelist * Preventing ReDoS attacks * Preventing replay attacks * URI restriction * Blacklist * Whitelist * CORS * **User Management** * Role-based Access Control (RBAC) * **Data Security** * mTLS * FIPS * SSL certificate rotation * **Tools** * CLI * Helm charts * Rollback * YAML for standalone mode * **Advanced** * Data sovereignty * Configuration hot update Related Topics[​](https://docs.api7.ai/enterprise/introduction#related-topics "Direct link to Related Topics") --------------------------------------------------------------------------------------------------------------- * If you want to have a basic knowledge of API7 Enterprise, see [concepts](https://docs.api7.ai/enterprise/key-concepts/services) section. * If you want to get started with API7 Enterprise, see [install API7 Enterprise](https://docs.api7.ai/enterprise/getting-started/install-api7-ee) and [launch your first API](https://docs.api7.ai/enterprise/getting-started/launch-your-first-api) . * [What Is API7 Enterprise](https://docs.api7.ai/enterprise/introduction#what-is-api7-enterprise) * [Why API7 Enterprise](https://docs.api7.ai/enterprise/introduction#why-api7-enterprise) * [Advantages and Highlights](https://docs.api7.ai/enterprise/introduction#advantages-and-highlights) * [Supported Functionalities](https://docs.api7.ai/enterprise/introduction#supported-functionalities) * [Related Topics](https://docs.api7.ai/enterprise/introduction#related-topics) --- # Plugin Hub | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub#__docusaurus_skipToContent_fallback) * * * ![](https://static.api7.ai/uploads/2025/03/10/CYPjJmOl_bg-title-bg.avif) Welcome to API Gateway Plugin Hub ================================= ![](https://static.api7.ai/uploads/2025/03/13/BrEQKYAn_plugin.avif) Discover powerful plugins to extend and enhance Apache APISIX’s capabilities for seamless API management [Plugin Overview](https://docs.api7.ai/apisix/key-concepts/plugins) | [Common Configurations](https://docs.api7.ai/apisix/reference/plugin-common-configurations) AI[#](https://docs.api7.ai/hub#ai) --------------------------------------- [![AI Aliyun Content Moderation](https://static.api7.ai/uploads/2025/03/13/tcMjtNjI_ai-aliyun-content-moderation.png)\ \ #### AI Aliyun Content Moderation\ \ The ai-aliyun-content-moderation plugin supports the integration with Aliyun to check prompt inputs for risk level when proxying to LLMs, rejecting requests that exceed the threshold.](https://docs.api7.ai/hub/ai-aliyun-content-moderation) [![AI AWS Content Moderation](https://static.api7.ai/uploads/2025/03/13/86ShT776_ai-aws-content-moderation.png)\ \ #### AI AWS Content Moderation\ \ The ai-aws-content-moderation plugin supports the integration with AWS Comprehend to check prompt inputs for toxicity when proxying to LLMs, rejecting requests that exceed the threshold.](https://docs.api7.ai/hub/ai-aws-content-moderation) [![AI Prompt Decorator](https://static.api7.ai/uploads/2024/10/10/D9oM81AC_ai-prompt-decorator.png)\ \ #### AI Prompt Decorator\ \ The ai-prompt-decorator plugin decorates user prompts to LLMs by prefixing and appending pre-engineered prompts, streamlining API operation and content generation.](https://docs.api7.ai/hub/ai-prompt-decorator) [![AI Prompt Template](https://static.api7.ai/uploads/2024/10/09/nmOsFCYK_ai-prompt-template.png)\ \ #### AI Prompt Template\ \ The ai-prompt-template plugin supports pre-configured templates for user inputs to LLMs in a "fill in the blank" fashion, streamlining API management.](https://docs.api7.ai/hub/ai-prompt-template) [![AI RAG](https://static.api7.ai/uploads/2024/10/24/qoiBcXyS_ai-rag.png)\ \ #### AI RAG\ \ The ai-rag plugin enhances LLM outputs with Retrieval-Augmented Generation (RAG), efficiently retrieving relevant documents to improve accuracy and contextual relevance in responses.](https://docs.api7.ai/hub/ai-rag) [![AI Proxy](https://static.api7.ai/uploads/2024/10/10/hj8UBb3W_ai-proxy.png)\ \ #### AI Proxy\ \ The ai-proxy plugin simplifies access to LLM and embedding models providers by converting plugin configurations into the required request format for OpenAI, DeepSeek, and other OpenAI-compatible APIs.](https://docs.api7.ai/hub/ai-proxy) [![AI Prompt Guard](https://static.api7.ai/uploads/2025/03/13/8v3RKxEA_ai-prompt-guard.png)\ \ #### AI Prompt Guard\ \ The ai-prompt-guard plugin safeguards prompts to LLM using allow/deny patterns, ensuring only approved inputs pass. It can check the latest message or full history.](https://docs.api7.ai/hub/ai-prompt-guard) [![AI Proxy Multi](https://static.api7.ai/uploads/2025/03/05/0Dzb0DI3_ai-proxy-multi.png)\ \ #### AI Proxy Multi\ \ The ai-proxy-multi plugin extends the capabilities of ai-proxy with load balancing, retries, fallbacks, and health checks, simplifying the integration with OpenAI, DeepSeek, and other OpenAI-compatible APIs.](https://docs.api7.ai/hub/ai-proxy-multi) [![AI Rate Limiting](https://static.api7.ai/uploads/2025/03/05/CJqBH5Rp_ai-rate-limiting.png)\ \ #### AI Rate Limiting\ \ The ai-rate-limiting plugin enforces token-based rate limiting for LLM service requests, preventing overuse, optimizing API consumption, and ensuring efficient resource allocation.](https://docs.api7.ai/hub/ai-rate-limiting) [![AI Request Rewrite](https://static.api7.ai/uploads/2025/04/07/bbcNos7x_ai-request-rewrite.png)\ \ #### AI Request Rewrite\ \ The ai-request-rewrite plugin forwards client requests to LLM services for processing before sending them upstream, enabling AI-driven redaction, enrichment, and reformatting.](https://docs.api7.ai/hub/ai-request-rewrite) [![OpenAPI to MCP](https://static.api7.ai/uploads/2025/09/22/rxyJgPnj_tmp-icon.png)\ \ Enterprise\ \ #### OpenAPI to MCP\ \ The openapi-to-mcp plugin lets API7 expose OpenAPI services through MCP, proxy requests with custom headers, and support real-time SSE streaming.](https://docs.api7.ai/hub/openapi-to-mcp) Traffic Management[#](https://docs.api7.ai/hub#traffic-management) ----------------------------------------------------------------------- [![Graphql Limit Count](https://static.api7.ai/uploads/2023/10/18/4qPNgRwS_test_2.8.png)\ \ Enterprise\ \ #### Graphql Limit Count\ \ The graphql-limit-count plugin employs a fixed window algorithm to restrict the rate of GraphQL requests based on the depth of queries or mutations, optimizing API performance and resource usage.](https://docs.api7.ai/hub/graphql-limit-count) [![Graphql Proxy Cache](https://static.api7.ai/uploads/2023/10/18/T9HdQS9U_test_2.6.png)\ \ Enterprise\ \ #### Graphql Proxy Cache\ \ The graphql-proxy-cache plugin enables caching of responses for GraphQL queries, improving API performance.](https://docs.api7.ai/hub/graphql-proxy-cache) [![Limit Count Advanced](https://static.api7.ai/uploads/2024/10/31/QdYZeJh7_limit-count-advanced.png)\ \ Enterprise\ \ #### Limit Count Advanced\ \ The limit-count-advanced plugin enforces API rate limiting with a fixed window or sliding window algorithm, restricting requests within a time window. Requests over the quota are rejected.](https://docs.api7.ai/hub/limit-count-advanced) [![Limit Req](https://static.api7.ai/uploads/2023/11/01/2CZt13jD_limit-req.png)\ \ #### Limit Req\ \ The limit-req plugin enforces API rate limiting with a leaky bucket algorithm to rate limit requests, enabling effective throttling to manage traffic flow.](https://docs.api7.ai/hub/limit-req) [![Limit Conn](https://static.api7.ai/uploads/2024/12/12/2OJrqIVM_limit-conn.png)\ \ #### Limit Conn\ \ The limit-conn plugin restricts the rate of requests by managing concurrent connections. Requests exceeding the threshold may be delayed or rejected, ensuring controlled API usage and preventing overload.](https://docs.api7.ai/hub/limit-conn) [![Limit Count](https://static.api7.ai/uploads/2023/10/27/wLA7vu2k_limit-count.png)\ \ #### Limit Count\ \ The limit-count plugin enforces API rate limiting with a fixed window algorithm, restricting requests within a time interval. Requests over the quota are rejected.](https://docs.api7.ai/hub/limit-count) [![OAS Validator](https://static.api7.ai/uploads/2023/11/08/Fu3KAzpq_oas-validator.png)\ \ Enterprise\ \ #### OAS Validator\ \ The oas-validator plugin ensures requests and responses comply with defined Open API schemas, enhancing API management and operational integrity.](https://docs.api7.ai/hub/oas-validator) [![Proxy Buffering](https://static.api7.ai/uploads/2023/10/25/03goqJor_proxy_buffering.png)\ \ Enterprise\ \ #### Proxy Buffering\ \ The proxy-buffering plugin dynamically disables NGINX proxy buffering, optimizing performance with SSE and other streaming upstream services in API gateway.](https://docs.api7.ai/hub/proxy-buffering) [![Proxy Cache](https://static.api7.ai/uploads/2024/03/11/ZWCmMDim_proxy-cache.png)\ \ #### Proxy Cache\ \ The proxy-cache plugin caches responses based on keys, supporting disk and memory caching for GET, POST, and HEAD requests, enhancing API performance.](https://docs.api7.ai/hub/proxy-cache) [![Proxy Mirror](https://static.api7.ai/uploads/2024/01/29/Hxr9HkkD_proxy-mirror.png)\ \ #### Proxy Mirror\ \ The proxy-mirror plugin duplicates ingress traffic to API gateway, forwarding it to a designated upstream while keeping regular services uninterrupted.](https://docs.api7.ai/hub/proxy-mirror) [![Request ID](https://static.api7.ai/uploads/2024/12/09/1U8hV11N_request-id.png)\ \ #### Request ID\ \ The request-id plugin adds a unique ID to each request proxied through the API gateway, facilitating effective tracking of API requests for better API management.](https://docs.api7.ai/hub/request-id) [![Request Validation](https://static.api7.ai/uploads/2023/12/07/bzStlCLK_request-validation.png)\ \ #### Request Validation\ \ The request-validation plugin checks requests for compliance before forwarding them to upstream services, enhancing security in API operations.](https://docs.api7.ai/hub/request-validation) [![Traffic Label](https://static.api7.ai/uploads/2023/10/18/IRDVY9sk_test_2.10.png)\ \ Enterprise\ \ #### Traffic Label\ \ The traffic-label plugin labels traffic based on user-defined rules, enabling actions based on labels and associated weights for improved API traffic management.](https://docs.api7.ai/hub/traffic-label) [![Workflow](https://static.api7.ai/uploads/2024/03/05/CTQ2O8NF_workflow.png)\ \ #### Workflow\ \ The workflow plugin enables conditional execution of user-defined actions on client traffic based on specific rules, allowing granular API traffic management.](https://docs.api7.ai/hub/workflow) [![Traffic Split](https://static.api7.ai/uploads/2023/11/01/tYpqD1OB_traffic-split.png)\ \ #### Traffic Split\ \ The traffic-split plugin directs traffic to multiple upstream services based on conditions or weights, providing a flexible approach for API release strategies and traffic management.](https://docs.api7.ai/hub/traffic-split) Transformation[#](https://docs.api7.ai/hub#transformation) --------------------------------------------------------------- [![Attach Consumer Label](https://static.api7.ai/uploads/2024/09/14/dNILru0T_update-icon-bcg.jpeg)\ \ #### Attach Consumer Label\ \ The attach-consumer-label plugin attaches custom consumer labels to authenticated requests, for upstream services to implement additional business logics.](https://docs.api7.ai/hub/attach-consumer-label) [![Body Transformer](https://static.api7.ai/uploads/2023/11/07/e7rKZFn9_body-transformer.png)\ \ #### Body Transformer\ \ The body-transformer plugin converts request and response bodies between formats, such as JSON to XML, facilitating seamless data exchange.](https://docs.api7.ai/hub/body-transformer) [![degraphql](https://static.api7.ai/uploads/2023/12/06/kms6zvcY_degraphql.png)\ \ #### degraphql\ \ The degraphql plugin enables communication with upstream GraphQL services through standard HTTP requests by mapping GraphQL queries to HTTP endpoints, simplifying API integration.](https://docs.api7.ai/hub/degraphql) [![Exit transformer](https://static.api7.ai/uploads/2024/10/25/R6XnlT7J_exit-transformer.png)\ \ Enterprise\ \ #### Exit transformer\ \ The exit-transformer plugin customizes API gateway responses based on status codes, headers, and bodies. When set as a global plugin, it also customizes responses for non-existent routes.](https://docs.api7.ai/hub/exit-transformer) [![Fault Injection](https://static.api7.ai/uploads/2025/01/22/m6b1DCku_fault-injection.png)\ \ #### Fault Injection\ \ The fault-injection plugin tests application resiliency by simulating controlled faults or delays, making it ideal for chaos engineering and failure condition analysis.](https://docs.api7.ai/hub/fault-injection) [![gRPC Transcode](https://static.api7.ai/uploads/2024/02/21/3uzsq5N4_grpc-transcode.png)\ \ #### gRPC Transcode\ \ The grpc-transcode plugin converts between HTTP and gRPC requests and responses, facilitating seamless communication between different API protocols.](https://docs.api7.ai/hub/grpc-transcode) [![gRPC Web](https://static.api7.ai/uploads/2026/01/06/al39Dafe_gRPC-web.png)\ \ #### gRPC Web\ \ The grpc-web plugin enables the gateway to handle gRPC-Web requests from browsers and JavaScript clients by translating them into standard gRPC calls and forwarding them to upstream gRPC services.](https://docs.api7.ai/hub/grpc-web) [![Mocking](https://static.api7.ai/uploads/2025/01/24/WttCR3KD_mocking.png)\ \ #### Mocking\ \ The mocking plugin simulates API responses without forwarding requests to upstream services, offering customization of status codes, response bodies, headers, and more for API testing and development.](https://docs.api7.ai/hub/mocking) [![Proxy Rewrite](https://static.api7.ai/uploads/2023/10/18/yCccWhP0_test_2.11.png)\ \ #### Proxy Rewrite\ \ The proxy-rewrite plugin offers flexible options to rewrite requests that API gateway forwards to upstream services, enhancing API management.](https://docs.api7.ai/hub/proxy-rewrite) [![Response Rewrite](https://static.api7.ai/uploads/2023/10/25/Nm2E852J_rr.png)\ \ #### Response Rewrite\ \ The response-rewrite plugin allows rewriting of responses from API gateway and upstream services, providing flexibility in API responses.](https://docs.api7.ai/hub/response-rewrite) [![SOAP](https://static.api7.ai/uploads/2023/10/18/V3vWj7A6_test_2.2.png)\ \ Enterprise\ \ #### SOAP\ \ The soap plugin simplifies transformation between RESTful HTTP requests and SOAP requests, including their corresponding responses, for better API interoperability.](https://docs.api7.ai/hub/soap) Authentication[#](https://docs.api7.ai/hub#authentication) --------------------------------------------------------------- [![Authz Keycloak](https://static.api7.ai/uploads/2023/12/08/pqqgJ2YO_keycloak-authz.png)\ \ #### Authz Keycloak\ \ The authz-keycloak plugin integrates with Keycloak for user authentication and authorization, enhancing API security and management.](https://docs.api7.ai/hub/authz-keycloak) [![Basic Auth](https://static.api7.ai/uploads/2024/08/23/YwSkGnhY_basic-auth.png)\ \ #### Basic Auth\ \ The basic-auth plugin provides basic access authentication, requiring clients to authenticate before accessing upstream resources, enhancing API security.](https://docs.api7.ai/hub/basic-auth) [![Forward Auth](https://static.api7.ai/uploads/2024/04/29/UNSKKKqr_forward-auth.png)\ \ #### Forward Auth\ \ The forward-auth plugin integrates with external authorization services, enhancing API security and access control.](https://docs.api7.ai/hub/forward-auth) [![JWT Auth](https://static.api7.ai/uploads/2024/03/20/3Dw978og_jwt-auth.png)\ \ #### JWT Auth\ \ The jwt-auth plugin supports the use of JSON Web Token (JWT) for client authentication before accessing upstream resources, enhancing API security measures.](https://docs.api7.ai/hub/jwt-auth) [![JWE Decrypt](https://static.api7.ai/uploads/2024/01/15/8AkaEKui_jwe-icon.png)\ \ #### JWE Decrypt\ \ The jwe-decrypt plugin decrypts JWE authorization headers in requests directed to routes or services, enhancing API security.](https://docs.api7.ai/hub/jwe-decrypt) [![Key Auth](https://static.api7.ai/uploads/2024/03/20/DRWFmK4D_key-auth.png)\ \ #### Key Auth\ \ The key-auth plugin allows clients to authenticate using an authentication key before accessing upstream resources, enhancing API security measures.](https://docs.api7.ai/hub/key-auth) [![HMAC Auth](https://static.api7.ai/uploads/2024/09/03/xG9Vqxl5_hmac-auth.png)\ \ #### HMAC Auth\ \ The hmac-auth plugin supports HMAC authentication to ensure request integrity, preventing modifications during transmission and enhancing API security.](https://docs.api7.ai/hub/hmac-auth) [![Multi Auth](https://static.api7.ai/uploads/2024/01/15/d4Lo3cix_multi-auth.png)\ \ #### Multi Auth\ \ The multi-auth plugin enables consumers using diverse authentication methods to share the same route or service, streamlining API lifecycle management.](https://docs.api7.ai/hub/multi-auth) [![OpenID Connect](https://static.api7.ai/uploads/2023/11/15/TdzRQl8n_oidc.png)\ \ #### OpenID Connect\ \ The openid-connect plugin integrates with OIDC providers like Keycloak and Auth0, simplifying user authentication in API management.](https://docs.api7.ai/hub/openid-connect) [![SAML Auth](https://static.api7.ai/uploads/2024/08/23/pYpD0ncp_saml-auth.png)\ \ Enterprise\ \ #### SAML Auth\ \ The saml-auth plugin enables user authentication via SAML 2.0 in the API gateway by interacting with identity providers (IdP), enhancing API security.](https://docs.api7.ai/hub/saml-auth) [![OPA](https://static.api7.ai/uploads/2024/08/23/BdRXak7x_opa.png)\ \ #### OPA\ \ The opa plugin integrates with Open Policy Agent, enabling unified policy definition and enforcement for authorization in API operations.](https://docs.api7.ai/hub/opa) Security[#](https://docs.api7.ai/hub#security) --------------------------------------------------- [![ACL](https://static.api7.ai/uploads/2024/02/04/dnlZbDhq_acl.png)\ \ Enterprise\ \ #### ACL\ \ The acl plugin controls access to upstream resources by verifying if the user is on the access control lists, enhancing API management.](https://docs.api7.ai/hub/acl) [![Chaitin WAF](https://static.api7.ai/uploads/2025/09/08/8mi7erpW_chaitin-waf.png)\ \ #### Chaitin WAF\ \ The chaitin-waf plugin integrates with Chaitin WAF (SafeLine) to detect and block web threats, strengthening application security and protecting user data.](https://docs.api7.ai/hub/chaitin-waf) [![CORS](https://static.api7.ai/uploads/2024/02/01/VGYw3KjS_20240201-095424.jpeg)\ \ #### CORS\ \ The cors plugin enables cross-origin resource sharing, allowing servers to specify permitted origins and instructing browsers to load resources from those origins, enhancing API accessibility.](https://docs.api7.ai/hub/cors) [![Data Mask](https://static.api7.ai/uploads/2024/04/01/2JlB10Xi_data-mask.png)\ \ Enterprise\ \ #### Data Mask\ \ The data-mask plugin removes or replaces sensitive information in request headers, bodies, and URL queries for logging purposes, enhancing data privacy and security.](https://docs.api7.ai/hub/data-mask) [![Consumer Restriction](https://static.api7.ai/uploads/2024/03/23/2Or0rpNg_consumer-restriction.png)\ \ #### Consumer Restriction\ \ The consumer-restriction plugin implements access controls based on consumer name, route ID, service ID, or consumer group ID, enhancing API security.](https://docs.api7.ai/hub/consumer-restriction) [![IP Restriction](https://static.api7.ai/uploads/2024/02/28/0WSO7GZL_ip-res.png)\ \ #### IP Restriction\ \ The ip-restriction plugin restricts access to upstream resources based on an IP address whitelist or blacklist, improving API security.](https://docs.api7.ai/hub/ip-restriction) [![UA Restriction](https://static.api7.ai/uploads/2024/02/29/Y0qfz6CT_ua-restriction.png)\ \ #### UA Restriction\ \ The ua-restriction plugin restricts access to upstream resources using an allowlist or denylist of user agents, preventing overload from web crawlers and enhancing API security.](https://docs.api7.ai/hub/ua-restriction) Observability[#](https://docs.api7.ai/hub#observability) ------------------------------------------------------------- [![ClickHouse Logger](https://static.api7.ai/uploads/2023/12/19/HYDN4Dmw_clickhouse.png)\ \ #### ClickHouse Logger\ \ The clickhouse-logger plugin pushes request and response logs to ClickHouse databases in batches, allowing for customizable log formats to enhance data management.](https://docs.api7.ai/hub/clickhouse-logger) [![Datadog](https://static.api7.ai/uploads/2024/01/18/s7gmob0S_datadog.png)\ \ #### Datadog\ \ The datadog plugin integrates with Datadog, sending metrics to DogStatsD in batches to improve API monitoring and API performance tracking.](https://docs.api7.ai/hub/datadog) [![Elasticsearch Logger](https://static.api7.ai/uploads/2025/01/13/9VsL3UBH_sukBLfk2_elasticsearch-logger.png)\ \ #### Elasticsearch Logger\ \ The elasticsearch-logger plugin pushes request and response logs in batches to Elasticsearch, allowing for customizable log formats to enhance data management.](https://docs.api7.ai/hub/elasticsearch-logger) [![Error Log Logger](https://static.apiseven.com/uploads/2025/01/26/9gRLjv8N_error-log-logger.png)\ \ #### Error Log Logger\ \ The error-log-logger plugin pushes APISIX's error logs to TCP, Apache SkyWalking, Apache Kafka, or ClickHouse servers, in batches. You can specify the severity level of which the plugin should send the corresponding logs.](https://docs.api7.ai/hub/error-log-logger) [![Google Cloud Logging](https://static.apiseven.com/uploads/2025/02/06/4qDGkFhw_google-cloud.png)\ \ #### Google Cloud Logging\ \ The google-cloud-logging plugin pushes request and response logs in batches to Google Cloud Logging Service and supports the customization of log formats.](https://docs.api7.ai/hub/google-cloud-logging) [![HTTP Logger](https://static.api7.ai/uploads/2025/01/20/XhSZolpi_http-logger.png)\ \ #### HTTP Logger\ \ The http-logger plugin pushes request and response logs as JSON objects to HTTP(S) servers in batches, allowing for customizable log formats to enhance data management.](https://docs.api7.ai/hub/http-logger) [![Kafka Logger](https://static.api7.ai/uploads/2025/01/20/ipzxNV6T_kafka-logger.png)\ \ #### Kafka Logger\ \ The kafka-logger plugin pushes request and response logs as JSON objects to Apache Kafka clusters in batches, allowing for customizable log formats to enhance data management.](https://docs.api7.ai/hub/kafka-logger) [![Loki Logger](https://static.api7.ai/uploads/2024/12/27/YXMsoZns_output-2.png)\ \ #### Loki Logger\ \ The loki-logger plugin sends request and response logs as JSON objects to Grafana Loki in batches via the Loki HTTP API, allowing for customizable log formats to enhance data management.](https://docs.api7.ai/hub/loki-logger) [![Prometheus](https://static.api7.ai/uploads/2023/10/18/Ep8zNIyd_test_2.5.png)\ \ #### Prometheus\ \ The prometheus plugin integrates with Prometheus for metric collection and continuous monitoring, enhancing API observability.](https://docs.api7.ai/hub/prometheus) [![OpenTelemetry](https://static.api7.ai/uploads/2024/02/17/TIoDf61O_otel-icon.png)\ \ #### OpenTelemetry\ \ The opentelemetry plugin instruments the API gateway, sending traces to the OpenTelemetry collector for monitoring API operations per OpenTelemetry specs.](https://docs.api7.ai/hub/opentelemetry) [![RocketMQ Logger](https://static.api7.ai/uploads/2023/12/18/JKMT7TOw_rocketmq.png)\ \ #### RocketMQ Logger\ \ The rocketmq-logger plugin pushes request and response logs as JSON objects to RocketMQ clusters in batches, allowing for customizable log formats to enhance data management.](https://docs.api7.ai/hub/rocketmq-logger) [![SkyWalking Logger](https://static.api7.ai/uploads/2025/01/20/bRD2lASg_SkyWalking-logger.png)\ \ #### SkyWalking Logger\ \ The skywalking-logger pushes request and response logs as JSON objects to SkyWalking OAP server in batches, allowing for customizable log formats to enhance data management.](https://docs.api7.ai/hub/skywalking-logger) [![syslog](https://static.api7.ai/uploads/2024/03/01/wq96rSoX_Syslog.png)\ \ #### syslog\ \ The syslog plugin pushes request and response logs as JSON objects to syslog servers in batches, allowing for customizable log formats to enhance data management.](https://docs.api7.ai/hub/syslog) [![SkyWalking](https://static.api7.ai/uploads/2025/01/15/JGYsNvMp_SkyWalking.png)\ \ #### SkyWalking\ \ The skywalking plugin integrates with Apache SkyWalking for effective request tracing, enhancing API observability.](https://docs.api7.ai/hub/skywalking) [![Splunk HEC Logging](https://static.api7.ai/uploads/2024/11/28/wFKN9wIR_output.png)\ \ #### Splunk HEC Logging\ \ The splunk-hec-logging plugin serializes request and response context information to Splunk Event Data format and push to your Splunk HTTP Event Collector (HEC) in batches, allowing for customizable log formats to enhance data management.](https://docs.api7.ai/hub/splunk-hec-logging) [![Zipkin](https://static.api7.ai/uploads/2024/01/23/K5z0SiCS_zipkin-white.png)\ \ #### Zipkin\ \ The zipkin plugin instruments the API gateway to send traces to Zipkin or compatible collectors like Jaeger and Apache SkyWalking, enhancing request tracing capabilities.](https://docs.api7.ai/hub/zipkin) General[#](https://docs.api7.ai/hub#general) ------------------------------------------------- [![Error Page](https://static.api7.ai/uploads/2024/03/15/yh6VviFP_error-page.png)\ \ Enterprise\ \ #### Error Page\ \ The error-page plugin customizes the error page displayed when gateways encounter exceptions, enhancing user experience and business branding.](https://docs.api7.ai/hub/error-page) [![Public API](https://static.api7.ai/uploads/2024/01/26/OJVqobOZ_public-api.png)\ \ #### Public API\ \ The public-api plugin exposes internal API endpoints, allowing external access while maintaining control over API management and security.](https://docs.api7.ai/hub/public-api) [![Real IP](https://static.api7.ai/uploads/2023/11/01/fi20fydE_real-ip.png)\ \ #### Real IP\ \ The real-ip plugin enables the API gateway to fetch the client's real IP using the IP address from the HTTP header or query string, improving data quality.](https://docs.api7.ai/hub/real-ip) Serverless[#](https://docs.api7.ai/hub#serverless) ------------------------------------------------------- [![AWS Lambda](https://static.api7.ai/uploads/2024/04/26/FjXGfhOO_aws-lambda.png)\ \ #### AWS Lambda\ \ The aws-lambda plugin simplifies APISIX integration with AWS Lambda and Amazon API gateway, supporting authentication via IAM user credentials and API keys.](https://docs.api7.ai/hub/aws-lambda) [![Serverless Functions](https://static.api7.ai/uploads/2024/05/09/2NzjIiNI_serverless-funcs.png)\ \ #### Serverless Functions\ \ The serverless function plugins (pre-function and post-function) allow execution of user-defined logic at the start or end of specified execution phases in API gateway.](https://docs.api7.ai/hub/serverless-functions) Other Protocols[#](https://docs.api7.ai/hub#other-protocols) ----------------------------------------------------------------- [![MQTT Proxy](https://static.api7.ai/uploads/2024/09/19/4S2PT5Bc_mqtt.png)\ \ #### MQTT Proxy\ \ The mqtt-proxy plugin supports proxying and load balancing MQTT requests to MQTT servers, enhancing API operation and management.](https://docs.api7.ai/hub/mqtt-proxy) --- # APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page [OpenAI](https://openai.com/) provides access to state-of-the-art AI models, such as GPT-3, for various applications including natural language processing, text generation, and more. Integrating OpenAI's APIs into your applications can unlock powerful capabilities for text analysis, content generation, and other AI-driven tasks. APISIX provides capabilities for secret management, response streaming, rate limiting, and more, making it an excellent choice for proxying requests from OpenAI's API endpoints. This guide will show you how to configure APISIX to integrate with OpenAI APIs to proxy user requests and model responses, using the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin. Prerequisite(s)[​](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#prerequisites "Direct link to Prerequisite(s)") --------------------------------------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker/) . * Install [cURL](https://curl.se/) to send requests to the services for validation. * Follow the [Getting Started Tutorial](https://docs.api7.ai/apisix/getting-started/) to start a new APISIX instance in Docker or on Kubernetes. Obtain an OpenAI API Key[​](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#obtain-an-openai-api-key "Direct link to Obtain an OpenAI API Key") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Create an [OpenAI account](https://openai.com/) and an [API key](https://openai.com/blog/openai-api) before proceeding. You can optionally save the key to an environment variable as such: export OPENAI_API_KEY=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 # replace with your API key Create a Route to OpenAI API[​](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#create-a-route-to-openai-api "Direct link to Create a Route to OpenAI API") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Create a route with the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin as such: * Admin API * ADC curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "openai-chat", "uri": "/anything", "plugins": { "ai-proxy": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options": { "model": "gpt-3.5-turbo" } } }}' ❶ Set the provider to `openai`, which will proxy requests to the OpenAI endpoint. ❷ Attach OpenAPI API key to `Authorization` request header. ❸ Set the model to `gpt-3.5-turbo`. adc.yaml services: - name: OpenAI Service routes: - uris: - /anything name: openai-chat plugins: ai-proxy: provider: openai auth: header: Authorization: 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 options: model: "gpt-3.5-turbo" ❶ Set the provider to `openai`, which will proxy requests to the OpenAI endpoint. ❷ Attach OpenAPI API key to `Authorization` request header. ❸ Set the model to `gpt-3.5-turbo`. Synchronize the configuration to APISIX: adc sync -f adc.yaml Verify[​](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#verify "Direct link to Verify") -------------------------------------------------------------------------------------------------------------------- Send a request with the following prompts to the route: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a computer scientist." }, { "role": "user", "content": "Explain in one sentence what a Turing machine is." } ] }' You should receive a response similar to the following: { ..., "choices": [ { "index": 0, "message": { "role": "assistant", "content": "A Turing machine is an abstract mathematical model that manipulates symbols on an infinite tape according to a set of rules, representing the concept of a general-purpose computer." }, "logprobs": null, "finish_reason": "stop" } ], ...} See [OpenAI's API specifications](https://platform.openai.com/docs/api-reference/chat/create) for more information about how to compose the request. Next Steps[​](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#next-steps "Direct link to Next Steps") -------------------------------------------------------------------------------------------------------------------------------- You have now learned how to integrate APISIX with OpenAI. See [OpenAI's API reference](https://platform.openai.com/docs/api-reference) to learn more about OpenAI's capabilities. If you would like to integrate with OpenAI's [streaming API](https://platform.openai.com/docs/api-reference/streaming) , you can use the [`proxy-buffering`](https://docs.api7.ai/hub/proxy-buffering) plugin to disable NGINX's `proxy_buffering` directive to avoid server-sent events (SSE) being buffered. In addition, you can integrate more capabilities that APISIX offers, such as [rate limiting](https://docs.api7.ai/apisix/how-to-guide/traffic-management/rate-limiting) and [caching](https://docs.api7.ai/hub/proxy-cache) , to improve system availability and user experience. * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#prerequisites) * [Obtain an OpenAI API Key](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#obtain-an-openai-api-key) * [Create a Route to OpenAI API](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#create-a-route-to-openai-api) * [Verify](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#verify) * [Next Steps](https://docs.api7.ai/apisix/how-to-guide/ai-gateway/proxy-openai-requests#next-steps) --- # APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/production/deployment-modes#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page You can deploy APISIX in three modes: * [Traditional mode](https://docs.api7.ai/apisix/production/deployment-modes#traditional-mode) * [Decoupled mode](https://docs.api7.ai/apisix/production/deployment-modes#decoupled-mode) * [Standalone mode](https://docs.api7.ai/apisix/production/deployment-modes#standalone-mode) Traditional Mode[​](https://docs.api7.ai/apisix/production/deployment-modes#traditional-mode "Direct link to Traditional Mode") -------------------------------------------------------------------------------------------------------------------------------- In traditional mode, every APISIX instance runs as both a control plane and data plane. APISIX configurations are stored in etcd. The APISIX instance dynamically routes requests and responses, as well as interacts with etcd to synchronize configurations. This example shows a sample configuration for deploying APISIX in traditional mode: config.yaml apisix: node_listen: - port: 9080deployment: role: traditional role_traditional: config_provider: etcd admin: admin_listen: port: 9180 admin_key_required: true allow_admin: - 127.0.0.0/24 admin_key: - name: admin key: ${{CUSTOM_API_WRITE_KEY}} role: admin - name: viewer key: ${{CUSTOM_API_VIEW_KEY}} role: viewer Running APISIX in traditional mode is the simplest way to get APISIX started and can reduce additional configuration management and overhead. However, there are some downsides too. As the responsibilities of data plane and control plane are not separate, deploying APISIX in traditional mode may not meet the production requirements in resiliency and availability. Decoupled Mode[​](https://docs.api7.ai/apisix/production/deployment-modes#decoupled-mode "Direct link to Decoupled Mode") -------------------------------------------------------------------------------------------------------------------------- In decoupled mode, APISIX instances are divided into two roles: data plane and control plane. The data plane is responsible for actual data like requests and responses flowing through the system. The control plane manages and orchestrates the whole system and sets rules and policies on how the data plane handles data. This example shows a sample configuration for deploying APISIX as a data plane in decoupled mode: config.yaml deployment: role: data_plane role_data_plane: config_provider: etcd This example shows a sample configuration for deploying APISIX as a control plane in decoupled mode: config.yaml deployment: role: control_plane role_control_plane: config_provider: etcd admin: admin_key_required: true allow_admin: - 127.0.0.0/24 admin_key: - name: admin key: ${{CUSTOM_API_WRITE_KEY}} role: admin - name: viewer key: ${{CUSTOM_API_VIEW_KEY}} role: viewer Running APISIX in decoupled mode brings some benefits: * **Improve scalability** Separation of the data plane and control plane allows both planes to operate independently without depending on one another. This independence enables the data plane to scale easily without impacting the control plane. This scalability ensures that a network architecture can seamlessly accommodate growth and increased data demands. * **Improve security** Since the control plane and the data plane are decoupled, a security breach in one plane cannot be exposed to the other. If a control plane is offline, data planes will run using their last configuration. Additionally, decoupling makes it easier to introduce optimized security mechanisms separately for each plane. * **Optimize performance** Both planes perform distinct functionalities independently. The separation of layers enables each plane to concentrate on optimizing its specific tasks. For instance, the control plane can prioritize efficient routing and decision-making. Meanwhile, the data plane can focus on swift and effective packet forwarding and enhancing network performance. While decoupling control planes and data planes provides many benefits, it also introduces some challenges and drawbacks. * **Increase latency** While the decoupled planes work independently from one another, they still need to communicate with each other to take instructions and perform their tasks. It can lead to increased latency, especially in distributed architectures, introducing delays for packet transmission through the networks. * **Increase complexity** Decoupling control planes and data planes can also increase additional configuration management and overhead. It requires significant skills and expertise to operate such two separate planes. Finding such expertise can be challenging and costly. For details about how to get started with APISIX in decoupled mode, see [Install APISIX in decoupled mode using Docker](https://docs.api7.ai/apisix/install/docker/#decoupled-mode) . Standalone Mode[​](https://docs.api7.ai/apisix/production/deployment-modes#standalone-mode "Direct link to Standalone Mode") ----------------------------------------------------------------------------------------------------------------------------- In standalone mode, APISIX operates solely as the data plane. It can either load configurations from [`apisix.yaml`](https://docs.api7.ai/apisix/reference/configuration-files#apisixyaml) in file-driven mode or receive configurations through the standalone Admin API, storing them entirely in memory. ### File-Driven[​](https://docs.api7.ai/apisix/production/deployment-modes#file-driven "Direct link to File-Driven") This example shows a sample configuration for deploying APISIX in file-driven standalone mode: config.yaml apisix: enable_admin: falsedeployment: role: data_plane role_data_plane: config_provider: yaml # yaml or json The combination of standalone mode and declarative configuration has a number of benefits: * **Reduce complexity** You do not need to install and manage the etcd. In this mode, all APISIX configurations are stored in-memory on the node. * **Consolidate your APISIX configurations** All APISIX configurations are kept in a single source of truths to reduce the possibility of errors and even simplify configuration management. For details about how to get started with APISIX in file-driven standalone mode, see [Install APISIX in standalone mode using Docker](https://docs.api7.ai/apisix/install/docker/#standalone-mode) . For details about how to configure APISIX, see [File-Driven Standalone configurations](https://docs.api7.ai/apisix/reference/file-standalone-configurations) . ### API-Driven[​](https://docs.api7.ai/apisix/production/deployment-modes#api-driven "Direct link to API-Driven") API-driven mode is a recent addition to APISIX’s standalone deployment model. In this mode, routing rules are stored entirely in memory rather than in a configuration file. All updates must be performed through the dedicated standalone Admin API. Each update replaces the full configuration and takes effect immediately through a hot update, without requiring a gateway restart. caution This feature is designed specifically for the APISIX Ingress Controller and is primarily intended for integration with ADC. APISIX provides an official, end-to-end, stateless Ingress Controller implementation. Do not use this feature directly unless you fully understand its internal workings and behavior. This example shows a sample configuration for deploying APISIX in API-driven standalone mode. It sets the APISIX role to `traditional` (to start both the gateway and the API-driven Admin API and use the YAML config provider: config.yaml deployment: role: traditional role_traditional: config_provider: yaml This disables the local file-based configuration source in favor of the API. When APISIX starts, it awaits configuration updates through the API. For details about how to work with the standalone Admin API, see [API-Driven Standalone configurations](https://docs.api7.ai/apisix/reference/api-standalone-usage) . * [Traditional Mode](https://docs.api7.ai/apisix/production/deployment-modes#traditional-mode) * [Decoupled Mode](https://docs.api7.ai/apisix/production/deployment-modes#decoupled-mode) * [Standalone Mode](https://docs.api7.ai/apisix/production/deployment-modes#standalone-mode) * [File-Driven](https://docs.api7.ai/apisix/production/deployment-modes#file-driven) * [API-Driven](https://docs.api7.ai/apisix/production/deployment-modes#api-driven) --- # API Declarative CLI (ADC) | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/adc/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page API Declarative CLI (ADC) is a command-line tool for managing both APISIX and API7 Enterprise declaratively. It offers simplicity and clarity in defining the desired state of the gateway, allowing developers and administrators to focus on the result rather than the steps. The declarative configurations serve as the single source of truth that developers can manage through their existing version control systems. ADC has the following general syntax: adc [command] [options] with global options: * `-v, --version` to check the version * `-h, --help` to print the help menu of the command Install ADC[​](https://docs.api7.ai/apisix/reference/adc/#install-adc "Direct link to Install ADC") ---------------------------------------------------------------------------------------------------- Install ADC with a quickstart script: curl -sL "https://run.api7.ai/adc/install" | bash To verify the installation, run: adc help You should see the following response: Usage: adc [options] [command]Options: -v, --version output the version number -h, --help display help for commandCommands: ping [options] Verify connectivity with backend dump [options] Dump configurations from the backend diff [options] Show the difference between local and backend configurations sync [options] Sync local configurations to backend convert [options] Convert other API spec to ADC configurations lint [options] Check provided configuration files, local execution only, ensuring inputs meet ADC requirements help [command] display help for command By default, the backend is set to `apisix` and the server is set to `http://localhost:9180`. To verify connectivity with APISIX, run: adc ping You should see the following response: Connected to the "apisix" backend successfully! Configure ADC[​](https://docs.api7.ai/apisix/reference/adc/#configure-adc "Direct link to Configure ADC") ---------------------------------------------------------------------------------------------------------- ADC needs to be configured before it can be used to manage the gateway. You can configure ADC using environment variables or command-line flags. ### Using Environment Variables[​](https://docs.api7.ai/apisix/reference/adc/#using-environment-variables "Direct link to Using Environment Variables") ADC exposes all configuration options as environment variables. For example, you can configure the backend type and address using the `ADC_BACKEND` and `ADC_SERVER` environment variables, respectively. export ADC_BACKEND=apisixexport ADC_SERVER=http://localhost:9180 A better way is to configure these options in a `.env` file: .env ADC_BACKEND=apisixADC_SERVER=http://localhost:9180 ### Using Command-Line Flags[​](https://docs.api7.ai/apisix/reference/adc/#using-command-line-flags "Direct link to Using Command-Line Flags") You can also configure ADC or overwrite the configuration in the environment variables using the command-line flags. For example, to configure/overwrite the backend type and address for the `ping` command: adc ping --backend apisix --server "http://localhost:9180" Run `adc help [command]` to see the available configuration options for a command. Commands[​](https://docs.api7.ai/apisix/reference/adc/#commands "Direct link to Commands") ------------------------------------------------------------------------------------------- ### `adc ping`[​](https://docs.api7.ai/apisix/reference/adc/#adc-ping "Direct link to adc-ping") Ping the configured backend to verify connectivity. | Option | Default Value | Description | Valid Values | Env Variable | | --- | --- | --- | --- | --- | | `--verbose ` | `1` | Overrides verbose logging levels.`0` represents no log, `1` represents basic logs, and `2` represents debug logs. | `0`, `1` or `2` | | | `--backend ` | `apisix` | Backend type to connect. | `apisix` or `api7ee` | `ADC_BACKEND` | | `--server ` | `http://localhost:9180` | Backend server address. | | `ADC_SERVER` | | `--token ` | | API token to access the backend. | | `ADC_TOKEN` | | `--gateway-group ` | `default` | Gateway group to operate on. | | `ADC_GATEWAY_GROUP` | | `--label-selector ` | | Resource labels to filter for. | | | | `--include-resource-type ` | | Filter for resource types, such that the resource search results should only contain the specified type. | | | | `--exclude-resource-type ` | | Filter for resource types, such that the resource search results should exclude the specified type. | | | | `--timeout ` | `10s` | Request timeout for the client to connect with the backend Admin API in duration, such as 30s, 10m, and 1h10m. | | | | `--ca-cert-file ` | | Path to CA certificate for verifying the backend Admin API. | | `ADC_CA_CERT_FILE` | | `--tls-client-cert-file ` | | Path to mutual TLS client certificate for verifying the backend Admin API. | | `ADC_TLS_CLIENT_CERT_FILE` | | `--tls-client-key-file ` | | Path to mutual TLS client key for verifying the backend Admin API. | `ADC_TLS_CLIENT_KEY_FILE` | | | `--tls-skip-verify` | `false` | Whether to verify the TLS certificate when connecting to the backend Admin API. | `ADC_TLS_SKIP_VERIFY` | | | `--request-concurrent` | `10` | Number of concurrent requests to the backend | | | #### Sample Usage[​](https://docs.api7.ai/apisix/reference/adc/#sample-usage "Direct link to Sample Usage") adc ping --backend apisix --server http://localhost:9180 ### `adc lint`[​](https://docs.api7.ai/apisix/reference/adc/#adc-lint "Direct link to adc-lint") Validate the provided configuration files locally. | Option | Default Value | Description | Valid Values | Env Variable | | --- | --- | --- | --- | --- | | `-f, --file ` | | Files to lint. | | | #### Sample Usage[​](https://docs.api7.ai/apisix/reference/adc/#sample-usage-1 "Direct link to Sample Usage") adc lint -f service-a.yaml -f service-b.yaml ### `adc sync`[​](https://docs.api7.ai/apisix/reference/adc/#adc-sync "Direct link to adc-sync") Synchronize the local configuration to the connected backend. | Option | Default Value | Description | Valid Values | Env Variable | | --- | --- | --- | --- | --- | | `--verbose ` | `1` | Overrides verbose logging levels.`0` represents no log, `1` represents basic logs, and `2` represents debug logs. | `0`, `1` or `2` | | | `--backend ` | `apisix` | Backend type to connect. | `apisix` or `api7ee` | `ADC_BACKEND` | | `--server ` | `http://localhost:9180` | Backend server address. | | `ADC_SERVER` | | `--token ` | | API token to access the backend. | | `ADC_TOKEN` | | `--gateway-group ` | `default` | Gateway group to operate on. | | `ADC_GATEWAY_GROUP` | | `--label-selector ` | | Resource labels to filter for. | | | | `-f, --file ` | | Configuration files to synchronize. | | | | `--include-resource-type ` | | Filter for resource types, such that the resource search results should only contain the specified type. | | | | `--exclude-resource-type ` | | Filter for resource types, such that the resource search results should exclude the specified type. | | | | `--timeout ` | `10s` | Request timeout for the client to connect with the backend Admin API in duration, such as 30s, 10m, and 1h10m. | | | | `--ca-cert-file ` | | Path to CA certificate for verifying the backend Admin API. | | `ADC_CA_CERT_FILE` | | `--tls-client-cert-file ` | | Path to mutual TLS client certificate for verifying the backend Admin API. | | `ADC_TLS_CLIENT_CERT_FILE` | | `--tls-client-key-file ` | | Path to mutual TLS client key for verifying the backend Admin API. | `ADC_TLS_CLIENT_KEY_FILE` | | | `--tls-skip-verify` | `false` | Whether to verify the TLS certificate when connecting to the backend Admin API. | `ADC_TLS_SKIP_VERIFY` | | #### Sample Usage[​](https://docs.api7.ai/apisix/reference/adc/#sample-usage-2 "Direct link to Sample Usage") adc sync -f service-a.yaml -f service-b.yaml --backend apisix --server http://localhost:9180 ### `adc dump`[​](https://docs.api7.ai/apisix/reference/adc/#adc-dump "Direct link to adc-dump") Save backend configuration to a local file. | Option | Default Value | Description | Valid Values | Env Variable | | --- | --- | --- | --- | --- | | `--verbose ` | `1` | Overrides verbose logging levels.`0` represents no log, `1` represents basic logs, and `2` represents debug logs. | `0`, `1` or `2` | | | `--backend ` | `apisix` | Backend type to connect. | `apisix` or `api7ee` | `ADC_BACKEND` | | `--server ` | `http://localhost:9180` | Backend server address. | | `ADC_SERVER` | | `--token ` | | API token to access the backend. | | `ADC_TOKEN` | | `--gateway-group ` | `default` | Gateway group to operate on. | | `ADC_GATEWAY_GROUP` | | `--label-selector ` | | Resource labels to filter for. | | | | `-o, --output ` | | Specify the file path where the backend data should be dumped. | | | | `--include-resource-type ` | | Filter for resource types, such that the resource search results should only contain the specified type. | | | | `--exclude-resource-type ` | | Filter for resource types, such that the resource search results should exclude the specified type. | | | | `--timeout ` | `10s` | Request timeout for the client to connect with the backend Admin API in duration, such as 30s, 10m, and 1h10m. | | | | `--ca-cert-file ` | | Path to CA certificate for verifying the backend Admin API. | | `ADC_CA_CERT_FILE` | | `--tls-client-cert-file ` | | Path to mutual TLS client certificate for verifying the backend Admin API. | | `ADC_TLS_CLIENT_CERT_FILE` | | `--tls-client-key-file ` | | Path to mutual TLS client key for verifying the backend Admin API. | `ADC_TLS_CLIENT_KEY_FILE` | | | `--tls-skip-verify` | `false` | Whether to verify the TLS certificate when connecting to the backend Admin API. | `ADC_TLS_SKIP_VERIFY` | | #### Sample Usage[​](https://docs.api7.ai/apisix/reference/adc/#sample-usage-3 "Direct link to Sample Usage") adc dump -o apisix-dump.yaml --backend apisix --server http://localhost:9180 ### `adc diff`[​](https://docs.api7.ai/apisix/reference/adc/#adc-diff "Direct link to adc-diff") Show differences in the configuration between the local file and the backend. | Option | Default Value | Description | Valid Values | Env Variable | | --- | --- | --- | --- | --- | | `--verbose ` | `1` | Overrides verbose logging levels.`0` represents no log, `1` represents basic logs, and `2` represents debug logs. | `0`, `1` or `2` | | | `--backend ` | `apisix` | Backend type to connect. | `apisix` or `api7ee` | `ADC_BACKEND` | | `--server ` | `http://localhost:9180` | Backend server address. | | `ADC_SERVER` | | `--token ` | | API token to access the backend. | | `ADC_TOKEN` | | `--gateway-group ` | `default` | Gateway group to operate on. | | `ADC_GATEWAY_GROUP` | | `--label-selector ` | | Resource labels to filter for. | | | | `-f, --file ` | | Configuration files to compare. | | | | `--include-resource-type ` | | Filter for resource types, such that the resource search results should only contain the specified type. | | | | `--exclude-resource-type ` | | Filter for resource types, such that the resource search results should exclude the specified type. | | | | `--timeout ` | `10s` | Request timeout for the client to connect with the backend Admin API in duration, such as 30s, 10m, and 1h10m. | | | | `--ca-cert-file ` | | Path to CA certificate for verifying the backend Admin API. | | `ADC_CA_CERT_FILE` | | `--tls-client-cert-file ` | | Path to mutual TLS client certificate for verifying the backend Admin API. | | `ADC_TLS_CLIENT_CERT_FILE` | | `--tls-client-key-file ` | | Path to mutual TLS client key for verifying the backend Admin API. | `ADC_TLS_CLIENT_KEY_FILE` | | | `--tls-skip-verify` | `false` | Whether to verify the TLS certificate when connecting to the backend Admin API. | `ADC_TLS_SKIP_VERIFY` | | #### Sample Usage[​](https://docs.api7.ai/apisix/reference/adc/#sample-usage-4 "Direct link to Sample Usage") adc diff -f service-a.yaml -f service-b.yaml --backend apisix --server http://localhost:9180 ### `adc convert`[​](https://docs.api7.ai/apisix/reference/adc/#adc-convert "Direct link to adc-convert") Convert API specification to ADC configuration. | Option | Default Value | Description | Valid Values | Env Variable | | --- | --- | --- | --- | --- | | `openapi` | | Convert an OpenAPI specification to ADC configuration. | | | #### Sample Usage[​](https://docs.api7.ai/apisix/reference/adc/#sample-usage-5 "Direct link to Sample Usage") adc convert openapi -f open-api-spec.yaml -o adc.yaml ### `adc help`[​](https://docs.api7.ai/apisix/reference/adc/#adc-help "Direct link to adc-help") Print the general help menu or the help menu for the specified command. #### Sample Usage[​](https://docs.api7.ai/apisix/reference/adc/#sample-usage-6 "Direct link to Sample Usage") adc help [command] * [Install ADC](https://docs.api7.ai/apisix/reference/adc/#install-adc) * [Configure ADC](https://docs.api7.ai/apisix/reference/adc/#configure-adc) * [Using Environment Variables](https://docs.api7.ai/apisix/reference/adc/#using-environment-variables) * [Using Command-Line Flags](https://docs.api7.ai/apisix/reference/adc/#using-command-line-flags) * [Commands](https://docs.api7.ai/apisix/reference/adc/#commands) * [`adc ping`](https://docs.api7.ai/apisix/reference/adc/#adc-ping) * [`adc lint`](https://docs.api7.ai/apisix/reference/adc/#adc-lint) * [`adc sync`](https://docs.api7.ai/apisix/reference/adc/#adc-sync) * [`adc dump`](https://docs.api7.ai/apisix/reference/adc/#adc-dump) * [`adc diff`](https://docs.api7.ai/apisix/reference/adc/#adc-diff) * [`adc convert`](https://docs.api7.ai/apisix/reference/adc/#adc-convert) * [`adc help`](https://docs.api7.ai/apisix/reference/adc/#adc-help) --- # API7 Enterprise Admin APIs | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/admin-api/#__docusaurus_skipToContent_fallback) * * * Loading ... --- # Configure Routes | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/getting-started/configure-routes/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Apache APISIX provides flexible gateway management capabilities based on routes, in which routing paths and target upstreams are defined. A _route_ is a routing path to upstream targets. In [Apache APISIX](https://api7.ai/apisix) , routes are responsible for matching client requests based on defined rules, loading and executing the corresponding plugins, as well as forwarding requests to the specified upstream services. A simple route can be set up with a path-matching URI and a corresponding upstream address. An _upstream_ is a set of target nodes with the same work. It defines a virtual host abstraction that performs load balancing on a given set of service nodes according to the configured rules. This tutorial guides you through how to create a route and verify it. You will: 1. Create a [route](https://docs.api7.ai/apisix/key-concepts/routes) with a sample [upstream](https://docs.api7.ai/apisix/key-concepts/upstreams) that points to an httpbin service. 2. Send a request to the route to see how APISIX proxies the request. Prerequisite(s)[​](https://docs.api7.ai/apisix/getting-started/configure-routes/#prerequisites "Direct link to Prerequisite(s)") --------------------------------------------------------------------------------------------------------------------------------- 1. Complete [Get APISIX](https://docs.api7.ai/apisix/getting-started/) to install APISIX in Docker. 2. Install [ADC](https://docs.api7.ai/apisix/reference/adc) or [APISIX-MCP](https://docs.api7.ai/apisix/reference/apisix-mcp) if you are using these tools. Create a Route[​](https://docs.api7.ai/apisix/getting-started/configure-routes/#create-a-route "Direct link to Create a Route") -------------------------------------------------------------------------------------------------------------------------------- In this section, you will create a route that forwards client requests to [httpbin](https://httpbin.org/) , an HTTP request and response service. * Admin API * ADC * APISIX-MCP curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "getting-started-ip", "uri": "/ip", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' You will receive an `HTTP/1.1 201 Created` response if the route was created successfully. adc.yaml services: - name: httpbin Service routes: - uris: - /ip name: getting-started-ip upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to APISIX: adc sync -f adc.yaml You will receive a similar response if the configuration was synchronized successfully: [11:25:49 AM] [ADC] › ✔ success Sync configuration[11:25:49 AM] [ADC] › ★ star All is well, see you next time! Enter the following prompt in your AI client: Create a route with the ID getting-started-ip that matches requests to the URI /ip, and forwards them to an upstream using round-robin load balancing with a single node at httpbin.org on port 80. You should see a response similar to the following: Successfully created route "getting-started-ip" with the following configuration:URI: /ipUpstream: http://httpbin.org:80 (roundrobin load balancing)Route ID: getting-started-ipStatus: Active (1)Created at: 1744183830 (2025-04-09 07:30:30 UTC)The route is now ready to accept requests at the /ip path, which will be forwarded to httpbin.org on port 80. Verify[​](https://docs.api7.ai/apisix/getting-started/configure-routes/#verify "Direct link to Verify") -------------------------------------------------------------------------------------------------------- * Admin API * ADC * APISIX-MCP Send a request to the route: curl "http://127.0.0.1:9080/ip" You should see a response similar to the following: { "origin": "183.94.122.205"} Send a request to the route: curl "http://127.0.0.1:9080/ip" You should see a response similar to the following: { "origin": "183.94.122.205"} Enter the following prompt in your AI client: Send a request to the route for verification. You should see a response similar to the following: Successfully verified the route "getting-started-ip":Received HTTP 200 response from httpbin.orgResponse shows origin IP address (192.168.107.1, 103.97.2.159)The route is properly configured and forwarding requests to httpbin.orgThe upstream service is responding correctly to requests What's Next[​](https://docs.api7.ai/apisix/getting-started/configure-routes/#whats-next "Direct link to What's Next") ---------------------------------------------------------------------------------------------------------------------- This tutorial creates a route pointing to one upstream node. In the next tutorial, you will learn how to configure load balancing with multiple upstream nodes. * [Prerequisite(s)](https://docs.api7.ai/apisix/getting-started/configure-routes/#prerequisites) * [Create a Route](https://docs.api7.ai/apisix/getting-started/configure-routes/#create-a-route) * [Verify](https://docs.api7.ai/apisix/getting-started/configure-routes/#verify) * [What's Next](https://docs.api7.ai/apisix/getting-started/configure-routes/#whats-next) --- # API7 Enterprise Admin APIs | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/control-api/#__docusaurus_skipToContent_fallback) * * * Loading ... --- # Plugins | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/plugins/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of plugins in APISIX and why you need plugins. You will be introduced to a few relevant concepts, including plugins enablement, plugins configuration files precedence, plugins execution filter and order, as well as plugins development. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/plugins/#overview "Direct link to Overview") -------------------------------------------------------------------------------------------------- APISIX _plugins_ extend APISIX's functionalities to meet organization or user-specific requirements in traffic management, observability, security, request/response transformation, serverless computing, and more. APISIX offers many existing plugins that can be customized and orchestrated to suit your needs. These plugins can be globally enabled to be triggered on every incoming request, or locally bound to other objects, such as [routes](https://docs.api7.ai/apisix/key-concepts/routes) , [services](https://docs.api7.ai/apisix/key-concepts/services) , [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) , [consumer groups](https://docs.api7.ai/apisix/key-concepts/consumer-groups) , or [plugin configs](https://docs.api7.ai/apisix/key-concepts/plugin-configs) . See [plugin hub](https://docs.api7.ai/hub) for an inventory of plugins and their usage. If existing APISIX plugins do not meet your needs, you can also write your own plugins in Lua or other languages such as Java, Python, Go, and Wasm. Plugins Installation[​](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-installation "Direct link to Plugins Installation") -------------------------------------------------------------------------------------------------------------------------------------- By default, most APISIX plugins are [installed](https://github.com/apache/apisix/blob/master/apisix/cli/config.lua) : apisix/cli/config.lua local _M = { ... plugins = { "real-ip", "ai", "client-control", "proxy-control", "request-id", "zipkin", "ext-plugin-pre-req", "fault-injection", "mocking", "serverless-pre-function", ... }, ...} If you would like to make adjustments to plugins installation, add the customized `plugins` configuration to `config.yaml`. For example: plugins: - real-ip - ai - client-control - proxy-control - request-id - zipkin - ext-plugin-pre-req - fault-injection # - mocking # do not install - serverless-pre-function ... # other plugins See `config.yaml.example`([https://github.com/apache/apisix/blob/master/conf/config.yaml.example](https://github.com/apache/apisix/blob/master/conf/config.yaml.example) ) for a complete configuration reference. [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. Plugins Execution Lifecycle[​](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-execution-lifecycle "Direct link to Plugins Execution Lifecycle") ----------------------------------------------------------------------------------------------------------------------------------------------------------- An installed plugin is first initialized. The configuration of the plugin is then checked against the defined [JSON Schema](https://json-schema.org/) to make sure the plugin configuration schema is correct. When a request goes through APISIX, the plugin's corresponding methods are executed in one or more of the following phases: `rewrite`, `access`, `before_proxy`, `header_filter`, `body_filter`, and `log`. These phases are largely influenced by the [OpenResty directives](https://openresty-reference.readthedocs.io/en/latest/Directives/) . ![Routes Diagram](https://static.api7.ai/uploads/2023/03/09/ZsH5C8Og_plugins-phases.png) To learn more about phases for your custom plugins development, see the [plugin development how-to guide](https://docs.api7.ai/apisix/how-to-guide/custom-plugins/create-plugin-in-lua) . Plugins Execution Order[​](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-execution-order "Direct link to Plugins Execution Order") ----------------------------------------------------------------------------------------------------------------------------------------------- In general, plugins are executed in the following order: 1. Plugins in [global rules](https://docs.api7.ai/apisix/key-concepts/plugin-global-rules) 1. plugins in rewrite phase 2. plugins in access phase 2. Plugins bound to other objects 1. plugins in rewrite phase 2. plugins in access phase Within each [phase](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-execution-lifecycle) , you can optionally define a new priority value in the `_meta.priority` attribute of the plugin, which takes precedence over the default plugin priority during execution. Plugins with higher priority values are executed first. See plugin [common configurations](https://docs.api7.ai/apisix/reference/plugin-common-configurations#_metapriority) for an example. Plugins Merging Precedence[​](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-merging-precedence "Direct link to Plugins Merging Precedence") -------------------------------------------------------------------------------------------------------------------------------------------------------- When the same plugin is configured both globally in a global rule and locally in an object (e.g. a route), both plugin instances are executed sequentially. However, if the same plugin is configured locally on multiple objects, such as on [routes](https://docs.api7.ai/apisix/key-concepts/routes) , [services](https://docs.api7.ai/apisix/key-concepts/services) , [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) , [consumer groups](https://docs.api7.ai/apisix/key-concepts/consumer-groups) , or [plugin configs](https://docs.api7.ai/apisix/key-concepts/plugin-configs) , only one copy of configuration is used as each non-global plugin is only executed once. This is because during execution, plugins configured in these objects are merged with respect to a specific order of precedence: `Consumer` > `Consumer Group` > `Route` > `Plugin Config` > `Service` such that if the same plugin has different configurations in different objects, the plugin configuration with the highest order of precedence during merging will be used. Plugins Execution Filter[​](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-execution-filter "Direct link to Plugins Execution Filter") -------------------------------------------------------------------------------------------------------------------------------------------------- By default, all plugins are triggered by incoming requests that match the configured rules in routes. However, in some cases, you may want more granular control over plugins execution; that is, conditionally determine which plugins are triggered for requests. APISIX allows for dynamic control over plugin execution by applying the `_meta.filter` configuration to the plugins. The configuration supports the evaluation of a wide range of [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) and [APISIX expressions](https://docs.api7.ai/apisix/reference/apisix-expressions) . See plugin [common configurations](https://docs.api7.ai/apisix/reference/plugin-common-configurations#_metafilter) for an example. Plugins Development[​](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-development "Direct link to Plugins Development") ----------------------------------------------------------------------------------------------------------------------------------- APISIX supports plugin extension in multiple languages, including Lua, Java, Python, Go, and Wasm. Plugins run in three major ways: * Lua plugins run natively in APISIX. * Java, Python, and Go plugins run in their corresponding APISIX plugin runners, communicating over [remote procedure calls (RPCs)](https://apisix.apache.org/docs/apisix/internal/plugin-runner/) . * Wasm plugins run in the APISIX Wasm plugin runtime. To learn more about developing plugins, see [create plugin in Lua](https://docs.api7.ai/apisix/how-to-guide/custom-plugins/create-plugin-in-lua) . Additional Resources[​](https://docs.api7.ai/apisix/key-concepts/plugins/#additional-resources "Direct link to Additional Resources") -------------------------------------------------------------------------------------------------------------------------------------- * Getting Started - [Configure Rate Limiting](https://docs.api7.ai/apisix/getting-started/rate-limiting) * Reference - [Plugin Common Configurations](https://docs.api7.ai/apisix/reference/plugin-common-configurations) * [Plugin Hub](https://docs.api7.ai/hub) * [Create Plugin in Lua](https://docs.api7.ai/apisix/how-to-guide/custom-plugins/create-plugin-in-lua) * [Overview](https://docs.api7.ai/apisix/key-concepts/plugins/#overview) * [Plugins Installation](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-installation) * [Plugins Execution Lifecycle](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-execution-lifecycle) * [Plugins Execution Order](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-execution-order) * [Plugins Merging Precedence](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-merging-precedence) * [Plugins Execution Filter](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-execution-filter) * [Plugins Development](https://docs.api7.ai/apisix/key-concepts/plugins/#plugins-development) * [Additional Resources](https://docs.api7.ai/apisix/key-concepts/plugins/#additional-resources) --- # Load Balancing | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/getting-started/load-balancing/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Load balancing is a technique used to distribute network request loads. It is a key consideration in designing systems that need to handle a large volume of traffic, allowing for improved system performance, scalability, and reliability. Apache APISIX supports a number of [load balancing algorithms](https://docs.api7.ai/apisix/key-concepts/upstreams#load-balancing) , one of which is the weighted round-robin algorithm. This algorithm distributes incoming requests over a set of servers in a cyclical pattern. In this tutorial, you will create a route with two upstream services and use the round-robin load balancing algorithm to load balance requests. Prerequisite(s)[​](https://docs.api7.ai/apisix/getting-started/load-balancing/#prerequisites "Direct link to Prerequisite(s)") ------------------------------------------------------------------------------------------------------------------------------- 1. Complete [Get APISIX](https://docs.api7.ai/apisix/getting-started/) to install APISIX in Docker. 2. Understand APISIX [routes](https://docs.api7.ai/apisix/key-concepts/routes) and [upstreams](https://docs.api7.ai/apisix/key-concepts/upstreams) . 3. Install [ADC](https://docs.api7.ai/apisix/reference/adc) or [APISIX-MCP](https://docs.api7.ai/apisix/reference/apisix-mcp) if you are using these tools. Enable Load Balancing[​](https://docs.api7.ai/apisix/getting-started/load-balancing/#enable-load-balancing "Direct link to Enable Load Balancing") --------------------------------------------------------------------------------------------------------------------------------------------------- * Admin API * ADC * APISIX-MCP Create a route with two upstream services, [httpbin.org](https://httpbin.org/headers) and [mock.api7.ai](https://mock.api7.ai/headers) , to distribute requests across. Both services respond with the request headers when receiving a request at `/headers`: curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "getting-started-headers", "uri": "/headers", "upstream" : { "type": "roundrobin", "nodes": { "httpbin.org:443": 1, "mock.api7.ai:443": 1 }, "pass_host": "node", "scheme": "https" }}' ❶ `type`: use `roundrobin` as the load balancing algorithm. ❷ `nodes`: upstream services. ❸ `pass_host`: use `node` to pass the host header to the upstream. ❹ `scheme`: use `https` to enable TLS with upstream. You should receive an `HTTP/1.1 201 Created` response if the route was created successfully. Create an ADC configuration file containing a route pointing to two upstream services, [httpbin.org](https://httpbin.org/headers) and [mock.api7.ai](https://mock.api7.ai/headers) . Both services will respond with the request headers when receiving a request at `/headers`. adc.yaml services: - name: httpbin Service routes: - uris: - /headers name: getting-started-headers upstream: type: roundrobin nodes: - host: mock.api7.ai port: 443 weight: 1 - host: httpbin.org port: 443 weight: 1 pass_host: node scheme: https ❶ `type`: use `roundrobin` as the load balancing algorithm. ❷ `nodes`: upstream services. ❸ `pass_host`: use `node` to pass the host header to the upstream. ❹ `scheme`: use `https` to enable TLS with upstream. Synchronize the configuration to APISIX: adc sync -f adc.yaml You will receive a similar response if the configuration was synchronized successfully: [11:25:49 AM] [ADC] › ✔ success Sync configuration[11:25:49 AM] [ADC] › ★ star All is well, see you next time! Enter the following prompt in your AI client: Create a route with the ID getting-started-headers that matches requests to the URI /headers, and forward them to two upstream nodes — httpbin.org and mock.api7.ai — both on 443. Use roundrobin algorithm for load balancing. The route should use HTTPS scheme and pass the original host of each upstream node in the request. You should see a response similar to the following: Successfully created route "getting-started-headers" with:URI: /headersTwo upstream nodes:https://httpbin.org:443https://mock.api7.ai:443Round-robin load balancingOriginal host headers preserved (pass_host: node)Route ID: getting-started-headersStatus: Active (1)Created at: 1744189246 (2025-04-09 09:00:46 UTC)The route is now ready to distribute requests between the two upstream endpoints while maintaining the original host headers. Verify[​](https://docs.api7.ai/apisix/getting-started/load-balancing/#verify "Direct link to Verify") ------------------------------------------------------------------------------------------------------ * Admin API * ADC * APISIX-MCP Generate 50 consecutive requests to APISIX `/headers` route to see the load-balancing effect: resp=$(seq 50 | xargs -I{} curl "http://127.0.0.1:9080/headers" -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 The command keeps count of the number of requests that were handled by the two services respectively. The output shows that requests were distributed over to the two services: httpbin.org: 23, mock.api7.ai: 27 Generate 50 consecutive requests to APISIX `/headers` route to see the load-balancing effect: resp=$(seq 50 | xargs -I{} curl "http://127.0.0.1:9080/headers" -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 The command keeps count of the number of requests that were handled by the two services respectively. The output shows that requests were distributed over to the two services: httpbin.org: 23, mock.api7.ai: 27 Enter the following prompt in your AI client: Generate 50 consecutive requests to the APISIX /headers route to observe the load-balancing behavior, then count how many responses came from httpbin.org and how many came from mock.api7.ai. You should see a response similar to the following: Load balancing test results for 50 requests to /headers route:httpbin.org responses: 23 (46%)mock.api7.ai responses: 27 (54%)The round-robin load balancing is working as expected, distributing requests between the two upstream nodes. The slight imbalance (46/54 split) could be due to connection timing or other network factors, but overall demonstrates effective load distribution. The distribution of requests across services should be close to 1:1 but might not always result in a perfect 1:1 ratio. The slight deviation is due to APISIX operating with multiple workers. What's Next[​](https://docs.api7.ai/apisix/getting-started/load-balancing/#whats-next "Direct link to What's Next") -------------------------------------------------------------------------------------------------------------------- You have learned how to configure load balancing. In the next tutorial, you will learn how to configure key authentication. * [Prerequisite(s)](https://docs.api7.ai/apisix/getting-started/load-balancing/#prerequisites) * [Enable Load Balancing](https://docs.api7.ai/apisix/getting-started/load-balancing/#enable-load-balancing) * [Verify](https://docs.api7.ai/apisix/getting-started/load-balancing/#verify) * [What's Next](https://docs.api7.ai/apisix/getting-started/load-balancing/#whats-next) --- # API7 Enterprise Admin APIs | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/enterprise/3.9.x/admin-api/#__docusaurus_skipToContent_fallback) * * * Loading ... --- # AI AWS Content Moderation | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ai-aws-content-moderation/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ai-aws-content-moderation` plugin supports the integration with [AWS Comprehend](https://aws.amazon.com/comprehend/) to check request bodies for toxicity when proxying to LLMs, such as profanity, hate speech, insult, harassment, violence, and more, rejecting requests if the evaluated outcome exceeds the configured threshold. Examples[​](https://docs.api7.ai/hub/ai-aws-content-moderation/#examples "Direct link to Examples") ---------------------------------------------------------------------------------------------------- The following examples will be using OpenAI as the upstream service provider. Before proceeding, create an [OpenAI account](https://openai.com/) and obtain an [API key](https://openai.com/blog/openai-api) . If you are working with other LLM providers, please refer to the provider's documentation to obtain an API key. Additionally, create [AWS IAM user access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) for APISIX to access [AWS Comprehend](https://aws.amazon.com/comprehend/) . You can optionally save these keys to environment variables: # replace with your keysexport OPENAI_API_KEY=sk-2LgTwrMuhOyvvRLTv0u4T3BlbkFJOM5sOqOvreE73rAhyg26export AWS_ACCESS_KEY=AKIARK7HKSJVSHWLD6OSexport AWS_SECRET_ACCESS_KEY=4ehUfCPoQmC+AKpG5/5ZaHlzFxFziZ88AylyPerj ### Moderate Profanity[​](https://docs.api7.ai/hub/ai-aws-content-moderation/#moderate-profanity "Direct link to Moderate Profanity") The following example demonstrates how you can use the plugin to moderate the level of profanity in prompts. * Admin API * ADC * Ingress Controller Create a route to the LLM chat completion endpoint using the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin and configure the allowed profanity level in `ai-aws-content-moderation`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-aws-content-moderation-route", "uri": "/post", "plugins": { "ai-aws-content-moderation": { "comprehend": { "access_key_id": "'"$AWS_ACCESS_KEY"'", "secret_access_key": "'"$AWS_SECRET_ACCESS_KEY"'", "region": "us-east-1" }, "moderation_categories": { "PROFANITY": 0.1 } }, "ai-proxy": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "model": "gpt-4" } } }' ❶ Update with your AWS Comprehend region. ❷ Configure the profanity threshold to a low value to allow a lower degree of profanity. Create a route with the `ai-aws-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: adc.yaml services: - name: aws-moderation-service routes: - name: aws-moderation-route uris: - /post methods: - POST plugins: ai-aws-content-moderation: comprehend: access_key_id: "${AWS_ACCESS_KEY}" secret_access_key: "${AWS_SECRET_ACCESS_KEY}" region: us-east-1 moderation_categories: PROFANITY: 0.1 ai-proxy: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Update with your AWS Comprehend region. ❷ Configure the profanity threshold to a low value to allow a lower degree of profanity. * Gateway API * APISIX CRD Create a route with the `ai-aws-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-aws-moderation-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-aws-moderation-plugin-configspec: plugins: - name: ai-aws-content-moderation config: comprehend: access_key_id: "AKIARK7HKSJVSHWLD6OS" secret_access_key: "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" region: us-east-1 moderation_categories: PROFANITY: 0.1 - name: ai-proxy config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: aws-moderation-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /post method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-aws-moderation-plugin-config Apply the configuration to your cluster: kubectl apply -f ai-aws-moderation-ic.yaml ❶ Update with your AWS Comprehend region. ❷ Configure the profanity threshold to a low value to allow a lower degree of profanity. Create a route with the `ai-aws-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-aws-moderation-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: aws-moderation-routespec: ingressClassName: apisix http: - name: aws-moderation-route match: paths: - /post methods: - POST plugins: - name: ai-aws-content-moderation enable: true config: comprehend: access_key_id: "AKIARK7HKSJVSHWLD6OS" secret_access_key: "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" region: us-east-1 moderation_categories: PROFANITY: 0.1 - name: ai-proxy enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 Apply the configuration to your cluster: kubectl apply -f ai-aws-moderation-ic.yaml ❶ Update with your AWS Comprehend region. ❷ Configure the profanity threshold to a low value to allow a lower degree of profanity. Send a POST request to the route with a system prompt and a user question with a mildly profane word in the request body: curl -i "http://127.0.0.1:9080/post" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Stupid, what is 1+1?" } ] }' You should receive an `HTTP/1.1 400 Bad Request` response and see the following message: request body exceeds PROFANITY threshold Send another request to the route with a typical question in the request body: curl -i "http://127.0.0.1:9080/post" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should receive an `HTTP/1.1 200 OK` response with the model output: { ..., "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "1+1 equals 2.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], ...} ### Moderate Overall Toxicity[​](https://docs.api7.ai/hub/ai-aws-content-moderation/#moderate-overall-toxicity "Direct link to Moderate Overall Toxicity") The following example demonstrates how you can use the plugin to moderate the overall toxicity level in prompts, in addition to moderating individual categories. * Admin API * ADC * Ingress Controller Create a route to the LLM chat completion endpoint using the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin and configure the allowed profanity and overall toxicity levels in `ai-aws-content-moderation`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-aws-content-moderation-route", "uri": "/post", "plugins": { "ai-aws-content-moderation": { "comprehend": { "access_key_id": "'"$AWS_ACCESS_KEY"'", "secret_access_key": "'"$AWS_SECRET_ACCESS_KEY"'", "region": "us-east-1" }, "moderation_categories": { "PROFANITY": 1 }, "moderation_threshold": 0.2 }, "ai-proxy": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "model": "gpt-4" } } }' ❶ Update with your AWS Comprehend region. ❷ Configure the profanity threshold to allow a high degree of profanity. ❸ Configure the overall toxicity threshold to allow a low degree of toxicity. Create a route with the `ai-aws-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: adc.yaml services: - name: aws-moderation-service routes: - name: aws-moderation-route uris: - /post methods: - POST plugins: ai-aws-content-moderation: comprehend: access_key_id: "${AWS_ACCESS_KEY}" secret_access_key: "${AWS_SECRET_ACCESS_KEY}" region: us-east-1 moderation_categories: PROFANITY: 1 moderation_threshold: 0.2 ai-proxy: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Update with your AWS Comprehend region. ❷ Configure the profanity threshold to allow a high degree of profanity. ❸ Configure the overall toxicity threshold to allow a low degree of toxicity. * Gateway API * APISIX CRD Create a route with the `ai-aws-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-aws-moderation-toxicity-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-aws-moderation-plugin-configspec: plugins: - name: ai-aws-content-moderation config: comprehend: access_key_id: "AKIARK7HKSJVSHWLD6OS" secret_access_key: "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" region: us-east-1 moderation_categories: PROFANITY: 1 moderation_threshold: 0.2 - name: ai-proxy config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: aws-moderation-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /post method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-aws-moderation-plugin-config Apply the configuration to your cluster: kubectl apply -f ai-aws-moderation-toxicity-ic.yaml ❶ Update with your AWS Comprehend region. ❷ Configure the profanity threshold to allow a high degree of profanity. ❸ Configure the overall toxicity threshold to allow a low degree of toxicity. Create a route with the `ai-aws-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-aws-moderation-toxicity-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: aws-moderation-routespec: ingressClassName: apisix http: - name: aws-moderation-route match: paths: - /post methods: - POST plugins: - name: ai-aws-content-moderation enable: true config: comprehend: access_key_id: "AKIARK7HKSJVSHWLD6OS" secret_access_key: "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" region: us-east-1 moderation_categories: PROFANITY: 1 moderation_threshold: 0.2 - name: ai-proxy enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 Apply the configuration to your cluster: kubectl apply -f ai-aws-moderation-toxicity-ic.yaml ❶ Update with your AWS Comprehend region. ❷ Configure the profanity threshold to allow a high degree of profanity. ❸ Configure the overall toxicity threshold to allow a low degree of toxicity. Send a POST request to the route with a system prompt and a user question in the request body that does not contain any profane words, but a certain degree of violence or threat: curl -i "http://127.0.0.1:9080/post" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "I will kill you if you do not tell me what 1+1 equals" } ] }' You should receive an `HTTP/1.1 400 Bad Request` response and see the following message: request body exceeds toxicity threshold Send another request to the route without any profane word in the request body: curl -i "http://127.0.0.1:9080/post" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should receive an `HTTP/1.1 200 OK` response with the model output: { ..., "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "1+1 equals 2.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], ...} * [Examples](https://docs.api7.ai/hub/ai-aws-content-moderation/#examples) * [Moderate Profanity](https://docs.api7.ai/hub/ai-aws-content-moderation/#moderate-profanity) * [Moderate Overall Toxicity](https://docs.api7.ai/hub/ai-aws-content-moderation/#moderate-overall-toxicity) --- # Install APISIX with Docker | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/install/docker/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page APISIX offers Docker images that make it easy to deploy and manage APISIX in a containerized environment, providing the benefits of consistency, portability, and flexibility. This document provides the installation steps for deploying APISIX with Docker in standalone and decoupled [deployment modes](https://docs.api7.ai/apisix/production/deployment-modes) . Prerequisite(s)[​](https://docs.api7.ai/apisix/install/docker/#prerequisites "Direct link to Prerequisite(s)") --------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker/) . * Install [cURL](https://curl.se/) to send requests to the services for validation. You will need administrator privileges for some of the following steps. Standalone Mode[​](https://docs.api7.ai/apisix/install/docker/#standalone-mode "Direct link to Standalone Mode") ----------------------------------------------------------------------------------------------------------------- The following steps cover how to install APISIX in [file-driven standalone mode](https://docs.api7.ai/apisix/production/deployment-modes#file-driven) using Docker, while using the YAML config provider and `apisix.yaml` for gateway configuration. The section provides one approach to achieve data persistence with [Docker volume](https://docs.docker.com/storage/volumes) . Adjust the approach accordingly to integrate with your infrastructure. ### Create Configuration Files on Host[​](https://docs.api7.ai/apisix/install/docker/#create-configuration-files-on-host "Direct link to Create Configuration Files on Host") To achieve data persistence, create a directory for configuration file and create [configuration files](https://docs.api7.ai/apisix/reference/configuration-files) `config.yaml` and `apisix.yaml` within: mkdir ~/conftouch ~/conf/config.yamlecho 'routes: - id: example-route-to-httpbin uri: /anything/test upstream: nodes: httpbin.org: 1 type: roundrobin#END' > ~/conf/apisix.yaml ❶ `config.yaml`: this file will be initialized in container at startup. It is created on host as an empty file to mount to the container and synchronize with the file content in the container. ❷ `apisix.yaml`: this file does not exist in container at startup. It is created on host with an example route to mount to the container and avoid any configuration error that may occur. ### Create `apisix` User on Host[​](https://docs.api7.ai/apisix/install/docker/#create-apisix-user-on-host "Direct link to create-apisix-user-on-host") If you use a Debian or Ubuntu-based APISIX Docker image, to volume mount files created in the previous step with the appropriate permissions, you should create an `apisix` user with the same gid and uid as the `apisix` user in the container and change the owner of the configuration files to `apisix`. Create a user `apisix` with uid and gid 636: groupadd --system --gid 636 apisixuseradd --system --gid apisix --no-create-home --shell /usr/sbin/nologin --uid 636 apisix Change the ownership of the directory with configuration files to `apisix`: chown -R apisix:apisix ~/conf ### Install APISIX[​](https://docs.api7.ai/apisix/install/docker/#install-apisix "Direct link to Install APISIX") Specify the [Docker image tag](https://hub.docker.com/r/apache/apisix/tags) in an environment variable: TAG=3.15.0-ubuntu Start APISIX in the standalone mode with configuration files mounted to the container: docker run -d \ --name apisix-standalone \ -p9080:9080 -p9443:9443 -p9090:9092 \ -e APISIX_STAND_ALONE=true \ --mount type=bind,source="$(pwd)"/conf/apisix.yaml,target=/usr/local/apisix/conf/apisix.yaml \ --mount type=bind,source="$(pwd)"/conf/config.yaml,target=/usr/local/apisix/conf/config.yaml \ apache/apisix:${TAG} ### Verify Installation[​](https://docs.api7.ai/apisix/install/docker/#verify-installation "Direct link to Verify Installation") Send a request to APISIX to see if it is running: curl -Is "http://127.0.0.1:9080" | grep Server If everything is ok, you should see the server version number, such as the following: Server: APISIX/3.15.0 ### Verify Data Persistence[​](https://docs.api7.ai/apisix/install/docker/#verify-data-persistence "Direct link to Verify Data Persistence") In the previous steps, you have mounted `apisix.yaml` and `config.yaml` on the host to the corresponding files in the container. Send a request to the pre-configured route in `apisix.yaml`: curl -i "http://127.0.0.1:9080/anything/test" You should see an `HTTP/1.1 200 OK` response similar to the following: { ... "headers": { ... }, "json": null, "method": "GET", "origin": "172.17.0.1, 34.xx.xx.xx", "url": "http://127.0.0.1/anything/test"} You can modify configurations in `apisix.yaml` and `config.yaml` on host, which update the configurations in the container. Changes to `apisix.yaml` will be loaded automatically to APISIX, whereas changes to `config.yaml` will require a reload of APISIX to take effect. See [configuration files](https://docs.api7.ai/apisix/reference/configuration-files) for more details. Decoupled Mode[​](https://docs.api7.ai/apisix/install/docker/#decoupled-mode "Direct link to Decoupled Mode") -------------------------------------------------------------------------------------------------------------- The following steps cover how to install APISIX in decoupled mode using Docker and provide one approach to achieve data persistence with [Docker volume](https://docs.docker.com/storage/volumes) . Adjust the approach accordingly to integrate with your infrastructure. In decoupled mode, two APISIX instances should be deployed: one being the data plane (DP) and the other one being the control plane (CP). ### Create Configuration Files on Host[​](https://docs.api7.ai/apisix/install/docker/#create-configuration-files-on-host-1 "Direct link to Create Configuration Files on Host") To achieve data persistence, create separate directories for DP and CP configuration files: for instance in {cp,dp}; do mkdir -p ~/conf/"$instance" touch ~/conf/"$instance"/config.yamldone Create the [configuration files](https://docs.api7.ai/apisix/reference/configuration-files) `config.yaml` for DP instance: echo 'deployment: role: data_plane role_data_plane: config_provider: etcd#END' > ~/conf/dp/config.yaml Create the [configuration files](https://docs.api7.ai/apisix/reference/configuration-files) `config.yaml` for CP instance: echo 'deployment: role: control_plane role_control_plane: config_provider: etcd admin: admin_key_required: true allow_admin: - 0.0.0.0/0 admin_key: - name: admin key: Sup3rs3cretWr1teK3y # replace with your write key role: admin - name: viewer key: Sup3rs3cretR3adK3y # replace with your read key role: viewer#END' > ~/conf/cp/config.yaml ### Create `apisix` User on Host[​](https://docs.api7.ai/apisix/install/docker/#create-apisix-user-on-host-1 "Direct link to create-apisix-user-on-host-1") If you use a Debian or Ubuntu-based APISIX Docker image, to volume mount files created in the previous step with the appropriate permissions, you should create an `apisix` user with the same gid and uid as the `apisix` user in the container and change the owner of the configuration files to `apisix`. Create a user `apisix` with uid and gid 636: groupadd --system --gid 636 apisixuseradd --system --gid apisix --no-create-home --shell /usr/sbin/nologin --uid 636 apisix Change the ownership of the directory with configuration files to `apisix`: chown -R apisix:apisix ~/conf ### Create a Docker Network[​](https://docs.api7.ai/apisix/install/docker/#create-a-docker-network "Direct link to Create a Docker Network") Create a Docker bridge network for APISIX and etcd containers: DOCKER_NETWORK_NAME="apisix-net"docker network create -d bridge ${DOCKER_NETWORK_NAME} ### Install etcd[​](https://docs.api7.ai/apisix/install/docker/#install-etcd "Direct link to Install etcd") Start the etcd container in the Docker network: ETCD_IMAGE_TAG="3.5.7" # >= 3.4.0ETCD_CONTAINER_NAME="etcd-$ETCD_IMAGE_TAG"ETCD_HOST=0.0.0.0ETCD_PORT=2379docker run -d \ --name ${ETCD_CONTAINER_NAME} \ --network=${DOCKER_NETWORK_NAME} \ -e ALLOW_NONE_AUTHENTICATION=yes \ -e ETCD_ADVERTISE_CLIENT_URLS=http://${ETCD_HOST}:${ETCD_PORT} \ bitnami/etcd:${ETCD_IMAGE_TAG} ### Install APISIX[​](https://docs.api7.ai/apisix/install/docker/#install-apisix-1 "Direct link to Install APISIX") Specify the APISIX [Docker image tag](https://hub.docker.com/r/apache/apisix/tags) in an environment variable: TAG=3.15.0-ubuntu Start an APISIX container as the **data plane** with configuration files mounted to the container. Map port `9080` for HTTP traffic and port `9443` for HTTPS traffic: docker run -d \ --name apisix-decoupled-dp \ -p9080:9080 -p9443:9443 \ --network=${DOCKER_NETWORK_NAME} \ --mount type=bind,source="$(pwd)"/conf/dp/config.yaml,target=/usr/local/apisix/conf/config.yaml \ -e APISIX_DEPLOYMENT_ETCD_HOST="[\"http://${ETCD_CONTAINER_NAME}:${ETCD_PORT}\"]" \ apache/apisix:${TAG} Start an APISIX container as the **control plane** with configuration files mounted to the container. Map port `9180` for Admin API and port `9090` for Control API: docker run -d \ --name apisix-decoupled-cp \ -p9180:9180 -p9090:9092 \ --network=${DOCKER_NETWORK_NAME} \ --mount type=bind,source="$(pwd)"/conf/cp/config.yaml,target=/usr/local/apisix/conf/config.yaml \ -e APISIX_DEPLOYMENT_ETCD_HOST="[\"http://${ETCD_CONTAINER_NAME}:${ETCD_PORT}\"]" \ apache/apisix:${TAG} ### Verify Installation[​](https://docs.api7.ai/apisix/install/docker/#verify-installation-1 "Direct link to Verify Installation") Send a request to APISIX to see if it is running: curl -Is "http://127.0.0.1:9080" | grep Server If everything is ok, you should see the server version number, such as the following: Server: APISIX/3.15.0 ### Verify Data Persistence[​](https://docs.api7.ai/apisix/install/docker/#verify-data-persistence-1 "Direct link to Verify Data Persistence") In the previous steps, you have mounted `config.yaml` files on the host to the corresponding files in the containers. Create a sample route by sending a request to the CP APISIX instance: curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: Sup3rs3cretWr1teK3y" \ -d '{ "id": "decoupled-test", "uri": "/anything/test", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to the route to verify: curl -i "http://127.0.0.1:9080/anything/test" You should see an `HTTP/1.1 200 OK` response. You can modify configurations in `config.yaml` on host, which update the configurations in the container. Changes to `config.yaml` will require a reload of APISIX to take effect. See [configuration files](https://docs.api7.ai/apisix/reference/configuration-files) for more details. * [Prerequisite(s)](https://docs.api7.ai/apisix/install/docker/#prerequisites) * [Standalone Mode](https://docs.api7.ai/apisix/install/docker/#standalone-mode) * [Create Configuration Files on Host](https://docs.api7.ai/apisix/install/docker/#create-configuration-files-on-host) * [Create `apisix` User on Host](https://docs.api7.ai/apisix/install/docker/#create-apisix-user-on-host) * [Install APISIX](https://docs.api7.ai/apisix/install/docker/#install-apisix) * [Verify Installation](https://docs.api7.ai/apisix/install/docker/#verify-installation) * [Verify Data Persistence](https://docs.api7.ai/apisix/install/docker/#verify-data-persistence) * [Decoupled Mode](https://docs.api7.ai/apisix/install/docker/#decoupled-mode) * [Create Configuration Files on Host](https://docs.api7.ai/apisix/install/docker/#create-configuration-files-on-host-1) * [Create `apisix` User on Host](https://docs.api7.ai/apisix/install/docker/#create-apisix-user-on-host-1) * [Create a Docker Network](https://docs.api7.ai/apisix/install/docker/#create-a-docker-network) * [Install etcd](https://docs.api7.ai/apisix/install/docker/#install-etcd) * [Install APISIX](https://docs.api7.ai/apisix/install/docker/#install-apisix-1) * [Verify Installation](https://docs.api7.ai/apisix/install/docker/#verify-installation-1) * [Verify Data Persistence](https://docs.api7.ai/apisix/install/docker/#verify-data-persistence-1) --- # AI Aliyun Content Moderation | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ai-aliyun-content-moderation` plugin supports the integration with [Aliyun Machine-Assisted Moderation Plus](https://help.aliyun.com/document_detail/2671445.html) to check request bodies for risk level when proxying to LLMs, such as profanity, hate speech, insult, harassment, violence, and more, rejecting requests if the evaluated outcome exceeds the configured threshold. Please ensure that the `access_key_secret` is correctly configured in the plugin. If misconfigured, all requests will bypass the plugin to be directly forwarded to the LLM upstream, and you will see a `Specified signature is not matched with our calculation` in the gateway's error log from the plugin. The `ai-aliyun-content-moderation` plugin should be used with either [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) or [`ai-proxy-multi`](https://docs.api7.ai/hub/ai-proxy-multi) plugin for proxying LLM requests. Demo[​](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#demo "Direct link to Demo") ------------------------------------------------------------------------------------------- The following demo demonstrates the [moderate request content toxicity example](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#moderate-request-content-toxicity) in API7 Enterprise using the Dashboard, where you can moderate request content for toxicity and customize the rejection code and message. Examples[​](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------------------- The following examples will be using OpenAI as the upstream service provider. Before proceeding, create an [OpenAI account](https://openai.com/) and obtain an [API key](https://openai.com/blog/openai-api) . If you are working with other LLM providers, please refer to the provider's documentation to obtain an API key. Additionally, create an [Aliyun account](https://www.aliyun.com/) , enable Machine-Assisted Moderation Plus, and obtain the endpoint, region ID, access key ID, and access key secret. You can optionally save these information to environment variables: # replace with your dataexport OPENAI_API_KEY=sk-2LgTwrMuhOyvvRLTv0u4T3BlbkFJOM5sOqOvreE73rAhyg26export ALIYUN_ENDPOINT=https://green-cip.cn-shanghai.aliyuncs.comexport ALIYUN_REGION_ID=cn-shanghaiexport ALIYUN_ACCESS_KEY_ID=LTAI5yXKZP77gR3BQQM9WJnAexport ALIYUN_ACCESS_KEY_SECRET=hT2YpkqLs9FIjh3dyznBw7RMux5OKv ### Moderate Request Content Toxicity[​](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#moderate-request-content-toxicity "Direct link to Moderate Request Content Toxicity") The following example demonstrates how you can use the plugin to moderate content toxicity in requests and customize rejection code and message. * Admin API * ADC * Ingress Controller Create a route to the LLM chat completion endpoint using the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin and configure the integration details as well as the deny code and message in the `ai-aliyun-content-moderation` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-aliyun-content-moderation-route", "uri": "/anything", "plugins": { "ai-aliyun-content-moderation": { "endpoint": "'"$ALIYUN_ENDPOINT"'", "region_id": "'"$ALIYUN_REGION_ID"'", "access_key_id": "'"$ALIYUN_ACCESS_KEY_ID"'", "access_key_secret": "'"$ALIYUN_ACCESS_KEY_SECRET"'", "deny_code": 400, "deny_message": "Request contains forbidden content, such as hate speech or violence." }, "ai-proxy": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } } } } }' ❶ Configure the rejection HTTP status code. ❷ Configure the rejection message. Create a route with the `ai-aliyun-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: adc.yaml services: - name: aliyun-moderation-service routes: - name: aliyun-moderation-route uris: - /anything methods: - POST plugins: ai-aliyun-content-moderation: endpoint: "${ALIYUN_ENDPOINT}" region_id: "${ALIYUN_REGION_ID}" access_key_id: "${ALIYUN_ACCESS_KEY_ID}" access_key_secret: "${ALIYUN_ACCESS_KEY_SECRET}" deny_code: 400 deny_message: "Request contains forbidden content, such as hate speech or violence." ai-proxy: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Configure the rejection HTTP status code. ❷ Configure the rejection message. * Gateway API * APISIX CRD Create a route with the `ai-aliyun-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-aliyun-moderation-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-aliyun-moderation-plugin-configspec: plugins: - name: ai-aliyun-content-moderation config: endpoint: "https://green-cip.cn-shanghai.aliyuncs.com" region_id: "cn-shanghai" access_key_id: "LTAI5yXKZP77gR3BQQM9WJnA" access_key_secret: "hT2YpkqLs9FIjh3dyznBw7RMux5OKv" deny_code: 400 deny_message: "Request contains forbidden content, such as hate speech or violence." - name: ai-proxy config: provider: openai auth: header: Authorization: "Bearer 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"---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: aliyun-moderation-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-aliyun-moderation-plugin-config Apply the configuration to your cluster: kubectl apply -f ai-aliyun-moderation-ic.yaml ❶ Configure the rejection HTTP status code. ❷ Configure the rejection message. Create a route with the `ai-aliyun-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-aliyun-moderation-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: aliyun-moderation-routespec: ingressClassName: apisix http: - name: aliyun-moderation-route match: paths: - /anything methods: - POST plugins: - name: ai-aliyun-content-moderation enable: true config: endpoint: "https://green-cip.cn-shanghai.aliyuncs.com" region_id: "cn-shanghai" access_key_id: "LTAI5yXKZP77gR3BQQM9WJnA" access_key_secret: "hT2YpkqLs9FIjh3dyznBw7RMux5OKv" deny_code: 400 deny_message: "Request contains forbidden content, such as hate speech or violence." - name: ai-proxy enable: true config: provider: openai auth: header: Authorization: "Bearer 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" Apply the configuration to your cluster: kubectl apply -f ai-aliyun-moderation-ic.yaml ❶ Configure the rejection HTTP status code. ❷ Configure the rejection message. Send a POST request to the route with a system prompt and a user question with a profane word in the request body: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4", "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Stupid, what is 1+1?" } ] }' You should receive an `HTTP/1.1 400 Bad Request` response and see the following message: { "object": "chat.completion", "usage": { "completion_tokens": 124, "prompt_tokens": 31, "total_tokens": 155 }, "choices": [ { "message": { "role": "assistant", "content": "Request contains forbidden content, such as hate speech or violence." }, "finish_reason": "stop", "index": 0 } ], "model": "gpt-4", "id": "c9466bbf-e010-469d-949a-a10f25525964"} Send another request to the route with a typical question in the request body: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should receive an `HTTP/1.1 200 OK` response with the model output: { ..., "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "1+1 equals 2.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], ...} ### Adjust Risk Level Threshold[​](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#adjust-risk-level-threshold "Direct link to Adjust Risk Level Threshold") The following example demonstrates how you can use adjust the threshold of risk level, which regulates whether a request/response should be allowed through. * Admin API * ADC * Ingress Controller Create a route to the LLM chat completion endpoint using the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin and configure the `risk_level_bar` in `ai-aliyun-content-moderation` to be `high`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-aliyun-content-moderation-route", "uri": "/anything", "plugins": { "ai-aliyun-content-moderation": { "endpoint": "'"$ALIYUN_ENDPOINT"'", "region_id": "'"$ALIYUN_REGION_ID"'", "access_key_id": "'"$ALIYUN_ACCESS_KEY_ID"'", "access_key_secret": "'"$ALIYUN_ACCESS_KEY_SECRET"'", "deny_code": 400, "deny_message": "Request contains forbidden content, such as hate speech or violence.", "risk_level_bar": "high" }, "ai-proxy": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "model": "gpt-4" } } }' Create a route with the `ai-aliyun-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: adc.yaml services: - name: aliyun-moderation-service routes: - name: aliyun-moderation-route uris: - /anything methods: - POST plugins: ai-aliyun-content-moderation: endpoint: "${ALIYUN_ENDPOINT}" region_id: "${ALIYUN_REGION_ID}" access_key_id: "${ALIYUN_ACCESS_KEY_ID}" access_key_secret: "${ALIYUN_ACCESS_KEY_SECRET}" deny_code: 400 deny_message: "Request contains forbidden content, such as hate speech or violence." risk_level_bar: high ai-proxy: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Create a route with the `ai-aliyun-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-aliyun-moderation-threshold-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-aliyun-moderation-plugin-configspec: plugins: - name: ai-aliyun-content-moderation config: endpoint: "https://green-cip.cn-shanghai.aliyuncs.com" region_id: "cn-shanghai" access_key_id: "LTAI5yXKZP77gR3BQQM9WJnA" access_key_secret: "hT2YpkqLs9FIjh3dyznBw7RMux5OKv" deny_code: 400 deny_message: "Request contains forbidden content, such as hate speech or violence." risk_level_bar: high - name: ai-proxy config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: aliyun-moderation-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-aliyun-moderation-plugin-config Apply the configuration to your cluster: kubectl apply -f ai-aliyun-moderation-threshold-ic.yaml Create a route with the `ai-aliyun-content-moderation` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-aliyun-moderation-threshold-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: aliyun-moderation-routespec: ingressClassName: apisix http: - name: aliyun-moderation-route match: paths: - /anything methods: - POST plugins: - name: ai-aliyun-content-moderation enable: true config: endpoint: "https://green-cip.cn-shanghai.aliyuncs.com" region_id: "cn-shanghai" access_key_id: "LTAI5yXKZP77gR3BQQM9WJnA" access_key_secret: "hT2YpkqLs9FIjh3dyznBw7RMux5OKv" deny_code: 400 deny_message: "Request contains forbidden content, such as hate speech or violence." risk_level_bar: high - name: ai-proxy enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 Apply the configuration to your cluster: kubectl apply -f ai-aliyun-moderation-threshold-ic.yaml Send a POST request to the route with a system prompt and a user question with a profane word in the request body: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4", "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Stupid, what is 1+1?" } ] }' You should receive an `HTTP/1.1 400 Bad Request` response and see the following message: { "object": "chat.completion", "usage": { "completion_tokens": 124, "prompt_tokens": 31, "total_tokens": 155 }, "choices": [ { "message": { "role": "assistant", "content": "Request contains forbidden content, such as hate speech or violence." }, "finish_reason": "stop", "index": 0 } ], "model": "gpt-4", "id": "c9466bbf-e010-469d-949a-a10f25525964"} Update the `risk_level_bar` in the plugin to `max`: curl "http://127.0.0.1:9180/apisix/admin/routes/ai-aliyun-content-moderation-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "ai-aliyun-content-moderation": { "risk_level_bar": "max" } } }' Send the same request to the route: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Stupid, what is 1+1?" } ] }' You should receive an `HTTP/1.1 200 OK` response with the model output: { ..., "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "1+1 equals 2.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], ...} This is because the word "stupid" has a risk level of `high`, which is lower than the configured threshold of `max`. To see the Aliyun moderation outcome, you can update the gateway's log level to `debug` as such: conf/config.yaml nginx_config: error_log_level: debug [Reload the gateway](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for configuration changes to take effect. For example, for the request above, you should see a debug log entry similar to the following: { "RequestId": "29F7AD19-074B-54AC-B240-B297AD96883F", "Message": "OK", "Data": { ..., "RiskLevel": "high", "Result": [ { "RiskWords": "are&you&stupid", ... } ] }, "Code": 200} * [Demo](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#demo) * [Examples](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#examples) * [Moderate Request Content Toxicity](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#moderate-request-content-toxicity) * [Adjust Risk Level Threshold](https://docs.api7.ai/hub/ai-aliyun-content-moderation/#adjust-risk-level-threshold) --- # Integrate with HashiCorp Consul | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page [HashiCorp Consul](https://www.consul.io/) is a multi-cloud service networking platform that connects and secures services from various clouds and runtime environments. It simplifies the process of [service discovery](https://docs.api7.ai/apisix/key-concepts/upstreams#service-discovery) by enabling services to quickly register and find other services. This guide will walk you through the process of setting up HashiCorp Consul for service discovery, and demonstrate how to integrate it with APISIX to dynamically route and load balance traffic across your microservices. note If you are running APISIX and the Ingress Controller in Kubernetes, this guide simulates a hybrid environment where APISIX routes traffic to services outside the cluster through an external Consul registry. In a setup where all services already run inside Kubernetes, you typically do not need Consul, as Kubernetes provides its own built-in service discovery through the Kubernetes Service registry and DNS. ![Integration with Consul](https://static.api7.ai/uploads/2023/12/12/5aWTZoya_apisix_consul.jpeg) Prerequisite(s)[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#prerequisites "Direct link to Prerequisite(s)") -------------------------------------------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker/) . * Install [cURL](https://curl.se/) to send requests to the services for validation. * Follow the [Getting Started Tutorial](https://docs.api7.ai/apisix/getting-started/) to start a new APISIX instance in Docker or on Kubernetes. Start Consul Instance[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#start-consul-instance "Direct link to Start Consul Instance") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- Start a Consul Docker instance named `consul` in the same network as the APISIX instance and expose port `8500` to the same port on the host machine: docker run \ --name consul \ -d -p 8500:8500 \ --network=apisix-quickstart-net \ consul:1.15.1 \ consul agent \ -server \ -bootstrap-expect=1 \ -node=agent-one \ -client 0.0.0.0 \ -log-level info \ -data-dir=/consul/data \ -enable-script-checks Start Sample Web Services[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#start-sample-web-services "Direct link to Start Sample Web Services") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In this section, you will start two sample web services based on NGINX. Create two configuration files, `web1.conf` and `web2.conf`, which instruct NGINX to serve static text responses at root: echo 'worker_processes 1;error_log stderr notice;events { worker_connections 1024;}http { variables_hash_max_size 1024; access_log off; real_ip_header X-Real-IP; charset utf-8; server { listen 80; location / { return 200 "Application 1 is running"; } location /static/ { alias static/; } }}' > web1.conf echo 'worker_processes 1;error_log stderr notice;events { worker_connections 1024;}http { variables_hash_max_size 1024; access_log off; real_ip_header X-Real-IP; charset utf-8; server { listen 80; location / { return 200 "Application 2 is running"; } location /static/ { alias static/; } }}' > web2.conf Start the web services `web1` and `web2`: docker run -d \ --name web1 \ --restart always \ -v $(pwd)/web1.conf:/etc/nginx/nginx.conf \ -p 9081:80 \ --network=apisix-quickstart-net \ nginx:1.25-alpine docker run -d \ --name web2 \ --restart always \ -v $(pwd)/web2.conf:/etc/nginx/nginx.conf \ -p 9082:80 \ --network=apisix-quickstart-net \ nginx:1.25-alpine ❶ Mount `web1.conf` and `web2.conf` from the host machine to `/etc/nginx/nginx.conf` inside the containers. ❷ Map port `80` of the containers to port `9081` and `9082` on the host. Register Services in Consul[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#register-services-in-consul "Direct link to Register Services in Consul") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Save the IP address of your host to an environment variable: HOST_IP=192.168.42.145 # replace with your host IP Register two services in Consul: curl "http://127.0.0.1:8500/v1/agent/service/register" -X PUT \ -H "Content-Type: application/json" \ -d '{ "ID": "svc-a1", "Name": "svc-a", "Tags": ["sample_web_svc", "v1"], "Address": "'"$HOST_IP"'", "Port": 9081, "Weights": { "Passing": 10, "Warning": 1 } }' curl "http://127.0.0.1:8500/v1/agent/service/register" -X PUT \ -H "Content-Type: application/json" \ -d '{ "ID": "svc-a2", "Name": "svc-a", "Tags": ["sample_web_svc", "v1"], "Address": "'"$HOST_IP"'", "Port": 9082, "Weights": { "Passing": 10, "Warning": 1 } }' ❶ `Tags`: An array of tags or metadata associated with the service. ❷ `Address`: The IP address or hostname where the service is located. ❸ `Port`: The port number on which the service is listening. Verify if the services are registered successfully: curl "http://127.0.0.1:8500/v1/catalog/service/svc-a" You should see a JSON response including information of your services. Connect Consul to APISIX[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#connect-consul-to-apisix "Direct link to Connect Consul to APISIX") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Add Consul discovery configurations to APISIX's `config.yaml` [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) : docker exec apisix-quickstart /bin/sh -c "echo 'discovery: consul: servers: - "http://consul:8500" dump: path: "logs/consul.dump" expire: 2592000' >> /usr/local/apisix/conf/config.yaml" ❶ `servers`: address of the Consul server. ❷ `logs/consul.dump`: file path where the data dump will be saved. Reload APISIX for configuration changes to take effect: docker exec apisix-quickstart apisix reload Create a Route in APISIX[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#create-a-route-in-apisix "Direct link to Create a Route in APISIX") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Create a route and configure the upstream to use Consul for service discovery of `svc-a`: * Admin API * ADC curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "consul-web-route", "uri": "/consul/web/*", "upstream": { "service_name": "svc-a", "discovery_type": "consul", "type": "roundrobin" }}' An `HTTP/1.1 200 OK` response verifies that the route is created successfully. adc.yaml services: - name: Consul Service routes: - uris: - /consul/web/* name: consul-web-route upstream: service_name: svc-a discovery_type: consul type: roundrobin Synchronize the configuration to APISIX: adc sync -f adc.yaml Validate[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#validate "Direct link to Validate") ------------------------------------------------------------------------------------------------------------------------------- Generate a few requests to the previously created route: curl "http://127.0.0.1:9080/consul/web/" You should see the responses alternating between the following: Application 1 is running% Application 2 is running% Next Steps[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#next-steps "Direct link to Next Steps") ------------------------------------------------------------------------------------------------------------------------------------- Service discovery in APISIX creates a few Control API endpoints for examination and troubleshooting. See [Control API reference](https://docs.api7.ai/apisix/reference/control-api#tag/Service-Discovery) for more information. In addition to Consul, APISIX also supports the integration with [Eureka](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration) , Nacos, and other service discovery platforms (coming soon). * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#prerequisites) * [Start Consul Instance](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#start-consul-instance) * [Start Sample Web Services](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#start-sample-web-services) * [Register Services in Consul](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#register-services-in-consul) * [Connect Consul to APISIX](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#connect-consul-to-apisix) * [Create a Route in APISIX](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#create-a-route-in-apisix) * [Validate](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#validate) * [Next Steps](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration/#next-steps) --- # AI Prompt Decorator | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ai-prompt-decorator/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ai-prompt-decorator` plugin modifies user input prompts by prefixing and appending pre-engineered prompts to set contexts in content generation. This practice helps the model operate within desired guidelines during interactions. Example[​](https://docs.api7.ai/hub/ai-prompt-decorator/#example "Direct link to Example") ------------------------------------------------------------------------------------------- The following example will be using OpenAI as the upstream service provider. Before proceeding, create an [OpenAI account](https://openai.com/) and an [API key](https://openai.com/blog/openai-api) . You can optionally save the key to an environment variable as such: export OPENAI_API_KEY=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 # replace with your API key If you are working with other LLM providers, please refer to the provider's documentation to obtain an API key. ### Prepend and Append Messages[​](https://docs.api7.ai/hub/ai-prompt-decorator/#prepend-and-append-messages "Direct link to Prepend and Append Messages") The following example demonstrates how to configure the `ai-prompt-decorator` plugin to prepend a system message and append a user message to the user input message. * Admin API * ADC * Ingress Controller Create a route to the chat completion endpoint with pre-configured prompt templates as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-prompt-decorator-route", "uri": "/v1/chat/completions", "methods": ["POST"], "plugins": { "proxy-rewrite": { "headers": { "set": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } } }, "ai-prompt-decorator": { "prepend":[ { "role": "system", "content": "Answer briefly and conceptually." } ], "append":[ { "role": "user", "content": "End the answer with a simple analogy." } ] } }, }, "upstream": { "type": "roundrobin", "nodes": { "api.openai.com:443": 1 }, "scheme": "https" } }' ❶ Configure the OpenAI API key in the [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite) plugin. Alternatively, you can choose to attach the API key in every client request if you do not wish to configure the key in APISIX. ❷ Prepend a system message to set the behavior of the assistant. ❸ Append additional user message to the user-defined prompt. Create a route with the `ai-prompt-decorator` plugin configured as such: adc.yaml services: - name: prompt-decorator-service routes: - name: prompt-decorator-route uris: - /v1/chat/completions methods: - POST plugins: proxy-rewrite: headers: set: Authorization: "Bearer ${OPENAI_API_KEY}" ai-prompt-decorator: prepend: - role: system content: "Answer briefly and conceptually." append: - role: user content: "End the answer with a simple analogy." upstream: type: roundrobin nodes: - host: api.openai.com port: 443 weight: 1 scheme: https Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Configure the OpenAI API key in the [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite) plugin. Alternatively, you can choose to attach the API key in every client request if you do not wish to configure the key in APISIX. ❷ Prepend a system message to set the behavior of the assistant. ❸ Append additional user message to the user-defined prompt. * Gateway API * APISIX CRD Create a route with the `ai-prompt-decorator` plugin configured as such: ai-prompt-decorator-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-prompt-decorator-plugin-configspec: plugins: - name: proxy-rewrite config: headers: set: Authorization: "Bearer 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" - name: ai-prompt-decorator config: prepend: - role: system content: "Answer briefly and conceptually." append: - role: user content: "End the answer with a simple analogy."---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: prompt-decorator-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /v1/chat/completions method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-prompt-decorator-plugin-config Apply the configuration to your cluster: kubectl apply -f ai-prompt-decorator-ic.yaml ❶ Configure the OpenAI API key in the [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite) plugin. Alternatively, you can choose to attach the API key in every client request if you do not wish to configure the key in APISIX. ❷ Prepend a system message to set the behavior of the assistant. ❸ Append additional user message to the user-defined prompt. Create a route with the `ai-prompt-decorator` plugin configured as such: ai-prompt-decorator-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: prompt-decorator-routespec: ingressClassName: apisix http: - name: prompt-decorator-route match: paths: - /v1/chat/completions methods: - POST plugins: - name: proxy-rewrite enable: true config: headers: set: Authorization: "Bearer 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" - name: ai-prompt-decorator enable: true config: prepend: - role: system content: "Answer briefly and conceptually." append: - role: user content: "End the answer with a simple analogy." Apply the configuration to your cluster: kubectl apply -f ai-prompt-decorator-ic.yaml ❶ Configure the OpenAI API key in the [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite) plugin. Alternatively, you can choose to attach the API key in every client request if you do not wish to configure the key in APISIX. ❷ Prepend a system message to set the behavior of the assistant. ❸ Append additional user message to the user-defined prompt. Send a POST request to the route specifying the model and a sample message in the request body: curl "http://127.0.0.1:9080/v1/chat/completions" -X POST \ -H "Content-Type: application/json" \ -H "Host: api.openai.com:443" \ -d '{ "model": "gpt-4", "messages": [{ "role": "user", "content": "What is mTLS authentication?" }] }' You should receive a response similar to the following: { "choices": [ { "finish_reason": "stop", "index": 0, "message": { "content": "Mutual TLS (mTLS) authentication is a security protocol that ensures both the client and server authenticate each other's identity before establishing a connection. This mutual authentication is achieved through the exchange and verification of digital certificates, which are cryptographically signed credentials proving each party's identity. In contrast to standard TLS, where only the server is authenticated, mTLS adds an additional layer of trust by verifying the client as well, providing enhanced security for sensitive communications.\n\nThink of mTLS as a secret handshake between two friends meeting at a club. Both must know the handshake to get in, ensuring they recognize and trust each other before entering.", "role": "assistant" } } ], "created": 1723193502, "id": "chatcmpl-9uFdWDlwKif6biCt9DpG0xgedEamg", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "system_fingerprint": "fp_abc28019ad", "usage": { "completion_tokens": 124, "prompt_tokens": 31, "total_tokens": 155 }} * [Example](https://docs.api7.ai/hub/ai-prompt-decorator/#example) * [Prepend and Append Messages](https://docs.api7.ai/hub/ai-prompt-decorator/#prepend-and-append-messages) --- # Gateway Groups | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page A gateway group is a logical unit that combines one or more API gateway instances. These instances share the same configurations and behaviors when processing API requests. This ensures consistent API handling and simplifies administration across the group. The default gateway group is sufficient for simple scenarios with only a single cluster or production environment. The advanced gateway groups are used for complex scenarios with separate API policies for different subsidiaries, lines of business, clusters, and environments like UAT and Staging. Although API7 Enterprise lacks concepts of multiple clusters and environments, you can achieve the same goal by naming and labeling gateway groups. A gateway group contains one or more gateway instances, but each gateway instance belongs to only one gateway group. The gateway instances can be deployed on the same or different virtual machines, bare metal servers, or Kubernetes nodes. Such combinations can meet users' diverse requirements across multiple lines of business, clusters, work zones, and environments. For example, in the diagram below, two teams are using API gateway in a company: Payment Team and Quote Team. The Payment Team has Production and UAT environments while the Quote Team has only one Production environment. In this case, three gateway groups can be created: `Payment Prod`, `Payment UAT`, and `Quote Prod`, and they can be labeled with `Env:Prod` and `Env:UAT`. ![Gateway Groups](https://static.api7.ai/uploads/2024/12/03/33FfJDFD_gateway-groups.jpeg) Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#key-features "Direct link to Key Features") --------------------------------------------------------------------------------------------------------------------------- * **API Gateway Group Management**: Managing a collection of API gateways as a logical unit sharing the same configurations. This simplifies administration and ensures consistent policy enforcement across the gateway instances. * **Business-Aligned Gateway Partitioning**: Segregating an enterprise's lines of business and solutions by assigning them to dedicated API gateway groups. This architectural approach enables better alignment between the API infrastructure and the organization's functional domains. * **Physical Isolation**: Gateway group isolates multiple API gateway instances in different physical environments, including datacenters, cloud platforms, and virtual machines. This effectively prevents interference between gateway groups and enhances system stability and security. * **Elastic Scaling**: Gateway group dynamically adds or removes API gateway instances based on traffic fluctuations to meet business demands. This improves resource utilization and reduces operational costs. * **Scalable and Flexible Infrastructure**: The logical architecture of the API gateway group is decoupled from the physical deployment of the individual gateway instances. This approach provides increased flexibility and scalability for the API infrastructure. * **Fine-Grained Permission Control**: Gateway group enables different permission configurations for different gateway instances and APIs to adhere to compliance requirements. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#use-cases "Direct link to Use Cases") ------------------------------------------------------------------------------------------------------------------ The versatility of gateway groups is reflected in their diverse range of use cases, which this section will explore in detail. ### Segregating Development, Test, UAT, and Production Environments[​](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#segregating-development-test-uat-and-production-environments "Direct link to Segregating Development, Test, UAT, and Production Environments") API deployment and release go through different stages and environments, which vary depending on companies' API management processes. Suppose your company has four environments: Dev, Test, UAT, and Prod. Without using gateway groups, you need to deploy 4 separate instances of API7 Enterprise, each with its independent control plane and data plane. Developers, QA, and Ops engineers need to develop, test, and release the same API by accessing API7 Enterprise's control plane with different domain names. This approach has significant drawbacks. When you have 5 business lines, each with 4 environments, you need to deploy a total of 20 sets of API7 Enterprise, resulting in resource wastage and increased management costs. By utilizing the gateway groups feature of API7 Enterprise, you can easily overcome this challenge. You can create 20 different gateway groups, following a naming convention that combines business line and environment names, and add labels for filtering and selection. Additionally, Role-Based Access Control (RBAC) can be applied to enable fine-grained permission control. In this manner, the developers can only modify the configuration in the development environment. ### Managing Multiple Clusters across Different Regions[​](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#managing-multiple-clusters-across-different-regions "Direct link to Managing Multiple Clusters across Different Regions") It is challenging to manage API services across multiple regions and clusters while ensuring data compliance for companies with a global customer base. Suppose your company operates in NA, EU, and APAC regions with 4 production clusters in each. Without gateway groups, you would need to maintain 12 sets of API gateway control planes and data planes. However, configuration discrepancies can easily occur with this approach. Even when most API gateway configurations are consistent, it takes additional effort to ensure that the encryption, privacy, and compliance-related settings of other components such as loggers, secret managers, and observability tools are consistent. API7 Enterprise's gateway groups provide a solution to centrally manage and configure API gateway clusters across multiple regions using a single control plane. You can use environment variables, service discovery, and registry centers to simplify and unify management and maintenance to reduce overall maintenance costs. Moreover, API7 Enterprise supports multi-layer networking, enabling seamless data processing and compliance across regions. If a US user named John logs in from Europe, the EU cluster can determine and redirect his API requests to the NA cluster based on his user ID. This capability ensures compliance and efficient API request handling. ### Meeting Service Level Objectives for Different Projects[​](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#meeting-service-level-objectives-for-different-projects "Direct link to Meeting Service Level Objectives for Different Projects") Services within various projects vary in their Service Level Objectives (SLOs). For instance, the SLO for a payment service can be 99.999%, while the SLO for a historical order service might be 99%. Specific strategies can be used for each service to align with their respective SLOs. The infrastructure and operations teams can deploy the payment service in multiple regions, and then allocate more gateway instances and higher-quality machine resources to this gateway group, and set strict alerting policies and detailed observability metrics for it. Conversely, the historical order service with lower SLO requirements can adopt a lightweight deployment strategy with relaxed alerts and monitoring to reduce resource allocation. * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#key-features) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#use-cases) * [Segregating Development, Test, UAT, and Production Environments](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#segregating-development-test-uat-and-production-environments) * [Managing Multiple Clusters across Different Regions](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#managing-multiple-clusters-across-different-regions) * [Meeting Service Level Objectives for Different Projects](https://docs.api7.ai/apisix/enterprise-feature/gateway-groups/#meeting-service-level-objectives-for-different-projects) --- # Port Reference | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/networking/port-reference/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 By default, APISIX exposes the following ports: | Port | Protocol | Description | | --- | --- | --- | | `9080` | HTTP | Listen for user requests. | | `9443` | HTTPS | Listen for user requests with SSL enabled. | | `9181` | PROXY | Listen for HTTP traffic with PROXY protocol. | | `9182` | PROXY | Listen for HTTPS traffic with PROXY protocol. | | `9090` | TCP | Listen for requests to Control API. | | `9100` | TCP | Listen for TCP traffic when [transport layer (L4) proxy](https://docs.api7.ai/apisix/how-to-guide/traffic-management/proxy-transport-layer-l4-traffic#enable-transport-layer-l4-proxy)
is enabled. | | `9200` | UDP | Listen for UDP traffic when [transport layer (L4) proxy](https://docs.api7.ai/apisix/how-to-guide/traffic-management/proxy-transport-layer-l4-traffic#enable-transport-layer-l4-proxy)
is enabled. | | `9180` | HTTP(S) | Listen for requests to Admin API. | | `9091` | HTTP | The port where APISIX exports metrics for Prometheus. | | `8443` | HTTPS | The port for HTTPS traffic when `redirect` plugin is used to redirect HTTP traffic to HTTPS. | To learn more about how to change the default ports, see [configuration files](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) . --- # APISIX Expressions | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/apisix-expressions/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page _APISIX Expressions_ are combinations of variables, operators, and values that can be evaluated to a result, such as a Boolean value, `true` or `false`. Expressions can be used in configurations for route matching, request filtering, selective plugin applications, log enrichment, and more. APISIX supports the evaluation of comparison operators and logical operators, as well as [regular expressions (RegEx)](https://www.pcre.org/) . Comparison Operators[​](https://docs.api7.ai/apisix/reference/apisix-expressions/#comparison-operators "Direct link to Comparison Operators") ---------------------------------------------------------------------------------------------------------------------------------------------- APISIX supports the following comparison operators to be used with [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) in expressions: | **Operator** | **Description** | **Example** | | --- | --- | --- | | `==` | equal | `["arg_version", "==", "v2"]` | | `~=` | not equal | `["arg_version", "~=", "v2"]` | | `>` | greater than | `["arg_ttl", ">", 3600]` | | `>=` | greater than or equal to | `["arg_ttl", ">=", 3600]` | | `<` | less than | `["arg_ttl", "<", 3600]` | | `<=` | less than or equal to | `["arg_ttl", "<=", 3600]` | | `~~` | match RegEx | `["arg_env", "~~", "[Dd]ev"]` | | `~*` | match RegEx (case-insensitive) | `["arg_env", "~*", "de(v\|mo)"]` | | `in` | exist in the right-hand side | `["arg_version", "in", ["v1","v2"]]` | | `has` | contain item in the right-hand side | `["graphql_root_fields", "has", "owner"]`
`["post_arg.messages[*].content[*].type","has","image_url"]` | | `!` | reverse the adjacent operator | `["arg_env", "!", "~~", "[Dd]ev"]` | | `ipmatch` | match IP address | `["remote_addr", "ipmatch", ["192.168.102.40", "192.168.3.0/24"]]` | Logical Operators[​](https://docs.api7.ai/apisix/reference/apisix-expressions/#logical-operators "Direct link to Logical Operators") ------------------------------------------------------------------------------------------------------------------------------------- APISIX supports the following logical operators: | **Operator** | **Explanation** | | --- | --- | | `AND` | `AND(A,B)` is true if both A and B are true. | | `OR` | `OR(A,B)` is true if either A or B is true. | | `!AND` | `!AND(A,B)` is true if either A or B is false. | | `!OR` | `!OR(A,B)` is true only if both A and B are false. | You can use logical operators to combine multiple expressions for evaluation, such as the following: [ "AND", ["arg_version", "==", "v2"], [ "OR", ["arg_action", "==", "signup"], ["arg_action", "==", "subscribe"] ]] * [Comparison Operators](https://docs.api7.ai/apisix/reference/apisix-expressions/#comparison-operators) * [Logical Operators](https://docs.api7.ai/apisix/reference/apisix-expressions/#logical-operators) --- # Rate Limiting | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/getting-started/rate-limiting/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page As an API gateway, APISIX serves as a unified entry point for a massive volume of requests that could include both legitimate and unwanted traffic. Rate limiting is one of the commonly used techniques to protect and manage APIs. For example, you can configure your API endpoints to allow for a set number of requests within a given period of time. This ensures fair usage of the upstream services and safeguards the APIs from potential cyber attacks like DDoS (Distributed Denial of Service) or excessive requests from web crawlers. ![Routes Diagram](https://static.api7.ai/uploads/2023/02/20/l9G9Kq41_rate-limiting.png) In this tutorial, you will enable the `limit-count` plugin to set a rate limiting constraint on the incoming traffic. Prerequisite(s)[​](https://docs.api7.ai/apisix/getting-started/rate-limiting/#prerequisites "Direct link to Prerequisite(s)") ------------------------------------------------------------------------------------------------------------------------------ 1. Complete [Get APISIX](https://docs.api7.ai/apisix/getting-started/) to install APISIX in Docker. 2. Complete [Configure Routes](https://docs.api7.ai/apisix/getting-started/configure-routes) . 3. Install [ADC](https://docs.api7.ai/apisix/reference/adc) or [APISIX-MCP](https://docs.api7.ai/apisix/reference/apisix-mcp) if you are using these tools. Enable Rate Limiting[​](https://docs.api7.ai/apisix/getting-started/rate-limiting/#enable-rate-limiting "Direct link to Enable Rate Limiting") ----------------------------------------------------------------------------------------------------------------------------------------------- * Admin API * ADC * APISIX-MCP Update the `getting-started-ip` route from [Configure Routes](https://docs.api7.ai/apisix/getting-started/configure-routes) with the `limit-count` plugin: curl -i "http://127.0.0.1:9180/apisix/admin/routes/getting-started-ip" -X PATCH -d '{ "plugins": { "limit-count": { "count": 2, "time_window": 10, "rejected_code": 429 } }}' You will receive an `HTTP/1.1 200 OK` response if the plugin was added successfully. adc.yaml services: - name: httpbin Service routes: - uris: - /ip name: getting-started-ip plugins: limit-count: rejected_code: 429 count: 2 time_window: 10 upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to APISIX: adc sync -f adc.yaml You will receive a similar response if the configuration was synchronized successfully: [11:25:49 AM] [ADC] › ✔ success Sync configuration[11:25:49 AM] [ADC] › ★ star All is well, see you next time! Enter the following prompt in your AI client: Apply rate limiting to the getting-started-ip route to restrict access to 2 requests every 10 seconds, returning a 429 response when the limit is exceeded. You should see a response similar to the following: Successfully configured rate limiting for route 'getting-started-ip' with:* Limit of 2 requests per 10 seconds* Rate limiting by client IP address (remote_addr)* HTTP 429 response when limit is exceeded The above configuration limits the incoming requests to a maximum of 2 requests within 10 seconds. ### Verify[​](https://docs.api7.ai/apisix/getting-started/rate-limiting/#verify "Direct link to Verify") * Admin API * ADC * APISIX-MCP Generate 50 simultaneous requests to see the rate limiting plugin in effect. resp=$(seq 50 | xargs -I{} curl "http://127.0.0.1:9080/ip" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 The results are as expected: out of the 50 requests, 2 requests were sent successfully (status code `200`) while the others were rejected (status code `429`). "200": 2, "429": 48 Generate 50 simultaneous requests to see the rate limiting plugin in effect. resp=$(seq 50 | xargs -I{} curl "http://127.0.0.1:9080/ip" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 The results are as expected: out of the 50 requests, 2 requests were sent successfully (status code `200`) while the others were rejected (status code `429`). "200": 2, "429": 48 Enter the following prompt in your AI client: Generate 50 consecutive requests to the APISIX /ip route to observe the load-balancing behavior, then count how many responses are 200 and how many are 429. You should see a response similar to the following: From 50 consecutive requests to the /ip route:* Successful responses (HTTP 200): 2* Rate limited responses (HTTP 429): 48The rate limiting is working as configured (2 requests per 10 seconds). All requests beyond the limit received 429 responses. The two successful responses show the load balancing is working with the upstream httpbin.org service. Disable Rate Limiting[​](https://docs.api7.ai/apisix/getting-started/rate-limiting/#disable-rate-limiting "Direct link to Disable Rate Limiting") -------------------------------------------------------------------------------------------------------------------------------------------------- * Admin API * ADC * APISIX-MCP Disable rate limiting by setting the `_meta.disable` parameter to `true`: curl -i "http://127.0.0.1:9180/apisix/admin/routes/getting-started-ip" -X PATCH -d '{ "plugins": { "limit-count": { "_meta": { "disable": true } } }}' Disable rate limiting by setting the `_meta.disable` parameter to `true`: adc.yaml services: - name: httpbin Service routes: - uris: - /ip name: getting-started-ip plugins: limit-count: rejected_code: 429 count: 2 time_window: 10 _meta: disable: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to APISIX: adc sync -f adc.yaml Enter the following prompt in your AI client: Disable the rate limiting plugin for route getting-started-ip while keeping its configuration intact. You should see a response similar to the following: Successfully disabled the limit-count plugin for route 'getting-started-ip' while maintaining all other configuration. The route now:* Still matches requests to URI '/ip'* Forwards to upstream httpbin.org:80* Has rate limiting disabled (but configuration preserved) ### Verify[​](https://docs.api7.ai/apisix/getting-started/rate-limiting/#verify-1 "Direct link to Verify") * Admin API * ADC * APISIX-MCP Generate 50 requests again to verify if the rate limiting is disabled: resp=$(seq 50 | xargs -I{} curl "http://127.0.0.1:9080/ip" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 The results below show that all requests were sent successfully: "200": 50, "429": 0 Generate 50 requests again to verify if the rate limiting is disabled: resp=$(seq 50 | xargs -I{} curl "http://127.0.0.1:9080/ip" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 The results below show that all requests were sent successfully: "200": 50, "429": 0 Enter the following prompt in your AI client: Generate 50 consecutive requests to the APISIX /ip route to observe the load-balancing behavior, then count how many responses are 200 and how many are 429. You should see a response similar to the following: From 50 consecutive requests to the /ip route:* Successful responses (HTTP 200): 50* Rate limited responses (HTTP 429): 0The rate limiting has been disabled and is working as expected with all requests successfully reaching the upstream httpbin.org service. More[​](https://docs.api7.ai/apisix/getting-started/rate-limiting/#more "Direct link to More") ----------------------------------------------------------------------------------------------- You can use [APISIX variables](https://docs.api7.ai/apisix/reference/built-in-variables#apisix-variables) to configure fine-grained matching rules of rate limiting, such as `$host` and `$uri`. In addition, APISIX also supports [rate limiting at the cluster level using Redis](https://docs.api7.ai/hub/limit-count#share-quota-among-apisix-nodes-with-a-redis-cluster) . What's Next[​](https://docs.api7.ai/apisix/getting-started/rate-limiting/#whats-next "Direct link to What's Next") ------------------------------------------------------------------------------------------------------------------- You have learned how to configure rate limiting and completed the Getting Started tutorials. You can continue to explore other documentation to customize APISIX and meet your production needs. * [Prerequisite(s)](https://docs.api7.ai/apisix/getting-started/rate-limiting/#prerequisites) * [Enable Rate Limiting](https://docs.api7.ai/apisix/getting-started/rate-limiting/#enable-rate-limiting) * [Verify](https://docs.api7.ai/apisix/getting-started/rate-limiting/#verify) * [Disable Rate Limiting](https://docs.api7.ai/apisix/getting-started/rate-limiting/#disable-rate-limiting) * [Verify](https://docs.api7.ai/apisix/getting-started/rate-limiting/#verify-1) * [More](https://docs.api7.ai/apisix/getting-started/rate-limiting/#more) * [What's Next](https://docs.api7.ai/apisix/getting-started/rate-limiting/#whats-next) --- # AI Prompt Template | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ai-prompt-template/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ai-prompt-template` plugin supports pre-configuring prompt templates that only accept user inputs in designated template variables, in a "fill in the blank" fashion. Examples[​](https://docs.api7.ai/hub/ai-prompt-template/#examples "Direct link to Examples") --------------------------------------------------------------------------------------------- The following examples will use OpenAI as the upstream service provider. Before proceeding, create an [OpenAI account](https://openai.com/) and an [API key](https://openai.com/blog/openai-api) . You can optionally save the key to an environment variable as such: export OPENAI_API_KEY=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 # replace with your API key If you are working with other LLM providers, please refer to the provider's documentation to obtain an API key. ### Configure a Template for Open Questions in Custom Complexity[​](https://docs.api7.ai/hub/ai-prompt-template/#configure-a-template-for-open-questions-in-custom-complexity "Direct link to Configure a Template for Open Questions in Custom Complexity") The following example demonstrates how to use the `ai-prompt-template` plugin to configure a template that can be used to answer open questions and accepts user-specified response complexity. * Admin API * ADC * Ingress Controller Create a route to the chat completion endpoint with pre-configured prompt templates as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-prompt-template-route", "uri": "/v1/chat/completions", "methods": ["POST"], "plugins": { "ai-proxy": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options": { "model": "gpt-4" } }, "ai-prompt-template": { "templates": [ { "name": "QnA with complexity", "template": { "model": "gpt-4", "messages": [ { "role": "system", "content": "Answer in {{complexity}}." }, { "role": "user", "content": "Explain {{prompt}}." } ] } } ] } } }' ❶ Configure the OpenAI API key in the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin. ❷ Name the template. When requesting the route, the request should include the template name. ❸ Specify the model identifier. ❹ Configure a prompt that obtains the user-defined answer complexity from the request body key `complexity`. ❺ Configure a prompt that obtains the user-defined question from the request body key `prompt`. Create a route with the `ai-prompt-template` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: adc.yaml services: - name: prompt-template-service routes: - name: prompt-template-route uris: - /v1/chat/completions methods: - POST plugins: ai-proxy: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 ai-prompt-template: templates: - name: "QnA with complexity" template: model: gpt-4 messages: - role: system content: "Answer in {{complexity}}." - role: user content: "Explain {{prompt}}." Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Configure the OpenAI API key in the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin. ❷ Name the template. When requesting the route, the request should include the template name. ❸ Specify the model identifier. ❹ Configure a prompt that obtains the user-defined answer complexity from the request body key `complexity`. ❺ Configure a prompt that obtains the user-defined question from the request body key `prompt`. * Gateway API * APISIX CRD Create a route with the `ai-prompt-template` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-prompt-template-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-prompt-template-plugin-configspec: plugins: - name: ai-prompt-template config: templates: - name: "QnA with complexity" template: model: gpt-4 messages: - role: system content: "Answer in {{complexity}}." - role: user content: "Explain {{prompt}}." - name: ai-proxy config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: prompt-template-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /v1/chat/completions method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-prompt-template-plugin-config Apply the configuration to your cluster: kubectl apply -f ai-prompt-template-ic.yaml ❶ Name the template. When requesting the route, the request should include the template name. ❷ Specify the model identifier. ❸ Configure a prompt that obtains the user-defined answer complexity from the request body key `complexity`. ❹ Configure a prompt that obtains the user-defined question from the request body key `prompt`. ❺ Configure the OpenAI API key in the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin. Create a route with the `ai-prompt-template` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-prompt-template-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: prompt-template-routespec: ingressClassName: apisix http: - name: prompt-template-route match: paths: - /v1/chat/completions methods: - POST plugins: - name: ai-prompt-template enable: true config: templates: - name: "QnA with complexity" template: model: gpt-4 messages: - role: system content: "Answer in {{complexity}}." - role: user content: "Explain {{prompt}}." - name: ai-proxy enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 Apply the configuration to your cluster: kubectl apply -f ai-prompt-template-ic.yaml ❶ Name the template. When requesting the route, the request should include the template name. ❷ Specify the model identifier. ❸ Configure a prompt that obtains the user-defined answer complexity from the request body key `complexity`. ❹ Configure a prompt that obtains the user-defined question from the request body key `prompt`. ❺ Configure the OpenAI API key in the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin. The route should now be available to be reused to respond to a variety of questions with different levels of user-specified desired complexity. Send a POST request to the route with a sample question and desired answer complexity in the request body: curl "http://127.0.0.1:9080/v1/chat/completions" -X POST \ -H "Content-Type: application/json" \ -d '{ "template_name": "QnA with complexity", "complexity": "brief", "prompt": "quick sort" }' You should receive a response similar to the following: { "choices": [ { "finish_reason": "stop", "index": 0, "message": { "content": "Quick sort is a highly efficient sorting algorithm that uses a divide-and-conquer approach to arrange elements in a list or array in order. Here’s a brief explanation:\n\n1. **Choose a Pivot**: Select an element from the list as a 'pivot'. Common methods include choosing the first element, the last element, the middle element, or a random element.\n\n2. **Partitioning**: Rearrange the elements in the list such that all elements less than the pivot are moved before it, and all elements greater than the pivot are moved after it. The pivot is now in its final position.\n\n3. **Recursively Apply**: Recursively apply the same process to the sub-lists of elements to the left and right of the pivot.\n\nThe base case of the recursion is lists of size zero or one, which are already sorted.\n\nQuick sort has an average-case time complexity of O(n log n), making it suitable for large datasets. However, its worst-case time complexity is O(n^2), which occurs when the smallest or largest element is always chosen as the pivot. This can be mitigated by using good pivot selection strategies or randomization.", "role": "assistant" } } ], "created": 1723194057, "id": "chatcmpl-9uFmTYN4tfwaXZjyOQwcp0t5law4x", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "system_fingerprint": "fp_abc28019ad", "usage": { "completion_tokens": 234, "prompt_tokens": 18, "total_tokens": 252 }} ### Configure Multiple Templates[​](https://docs.api7.ai/hub/ai-prompt-template/#configure-multiple-templates "Direct link to Configure Multiple Templates") The following example demonstrates how you can configure multiple templates on the same route. When requesting the route, users will be able to pass custom inputs to different templates by specifying the template name. The example continues with the [last example](https://docs.api7.ai/hub/ai-prompt-template/#configure-a-template-for-open-questions-in-custom-complexity) . Update the plugin with another template: * Admin API * ADC * Ingress Controller Update the route with an additional template: curl "http://127.0.0.1:9180/apisix/admin/routes/ai-prompt-template-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "ai-prompt-template": { "templates": [ { "name": "QnA with complexity", "template": { "model": "gpt-4", "messages": [ { "role": "system", "content": "Answer in {{complexity}}." }, { "role": "user", "content": "Explain {{prompt}}." } ] } }, { "name": "echo", "template": { "model": "gpt-4", "messages": [ { "role": "user", "content": "Echo {{prompt}}." } ] } } ] } } }' Update the route configuration with an additional template: adc.yaml services: - name: prompt-template-service routes: - name: prompt-template-route uris: - /v1/chat/completions methods: - POST plugins: ai-proxy: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 ai-prompt-template: templates: - name: "QnA with complexity" template: model: gpt-4 messages: - role: system content: "Answer in {{complexity}}." - role: user content: "Explain {{prompt}}." - name: "echo" template: model: gpt-4 messages: - role: user content: "Echo {{prompt}}." Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Update the PluginConfig with an additional template: ai-prompt-template-multi-ic.yaml # Other Configs# ---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-prompt-template-plugin-configspec: plugins: - name: ai-prompt-template config: templates: - name: "QnA with complexity" template: model: gpt-4 messages: - role: system content: "Answer in {{complexity}}." - role: user content: "Explain {{prompt}}." - name: "echo" template: model: gpt-4 messages: - role: user content: "Echo {{prompt}}." - name: ai-proxy config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 Apply the updated configuration to your cluster: kubectl apply -f ai-prompt-template-multi-ic.yaml Update the ApisixRoute with an additional template: ai-prompt-template-multi-ic.yaml # Other Configs# ---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: prompt-template-routespec: ingressClassName: apisix http: - name: prompt-template-route match: paths: - /v1/chat/completions methods: - POST plugins: - name: ai-prompt-template enable: true config: templates: - name: "QnA with complexity" template: model: gpt-4 messages: - role: system content: "Answer in {{complexity}}." - role: user content: "Explain {{prompt}}." - name: "echo" template: model: gpt-4 messages: - role: user content: "Echo {{prompt}}." - name: ai-proxy enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 Apply the updated configuration to your cluster: kubectl apply -f ai-prompt-template-multi-ic.yaml You should now be able to use both templates through the same route. Send a POST request to the route and use the first template: curl "http://127.0.0.1:9080/v1/chat/completions" -X POST \ -H "Content-Type: application/json" \ -d '{ "template_name": "QnA with complexity", "complexity": "brief", "prompt": "quick sort" }' You should receive a response similar to the following: { "choices": [ { "finish_reason": "stop", "index": 0, "message": { "content": "Quick sort is a highly efficient sorting algorithm that uses a divide-and-conquer approach to arrange elements in a list or array in order. Here’s a brief explanation:\n\n1. **Choose a Pivot**: Select an element from the list as a 'pivot'. Common methods include choosing the first element, the last element, the middle element, or a random element.\n\n2. **Partitioning**: Rearrange the elements in the list such that all elements less than the pivot are moved before it, and all elements greater than the pivot are moved after it. The pivot is now in its final position.\n\n3. **Recursively Apply**: Recursively apply the same process to the sub-lists of elements to the left and right of the pivot.\n\nThe base case of the recursion is lists of size zero or one, which are already sorted.\n\nQuick sort has an average-case time complexity of O(n log n), making it suitable for large datasets. However, its worst-case time complexity is O(n^2), which occurs when the smallest or largest element is always chosen as the pivot. This can be mitigated by using good pivot selection strategies or randomization.", "role": "assistant" } } ], ...} Send a POST request to the route and use the second template: curl "http://127.0.0.1:9080/v1/chat/completions" -X POST \ -H "Content-Type: application/json" \ -d '{ "template_name": "echo", "prompt": "hello APISIX" }' You should receive a response similar to the following: { "choices": [ { "finish_reason": "stop", "index": 0, "message": { "content": "hello APISIX", "role": "assistant" } } ], ...} * [Examples](https://docs.api7.ai/hub/ai-prompt-template/#examples) * [Configure a Template for Open Questions in Custom Complexity](https://docs.api7.ai/hub/ai-prompt-template/#configure-a-template-for-open-questions-in-custom-complexity) * [Configure Multiple Templates](https://docs.api7.ai/hub/ai-prompt-template/#configure-multiple-templates) --- # Monitor APISIX Metrics with Datadog | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page As the complexity of IT infrastructures and systems increases, continuous monitoring has become a vital part of any IT operation to improve system reliability and avoid costly downtimes. [Datadog](https://www.datadoghq.com/) is a cloud monitoring platform that offers a unified solution for metrics, logs, and tracing. The platform provides many pre-defined dashboard templates and allows for flexible customization to meet complex data analytics and visualization needs. This guide will walk you through the process of integrating Datadog with APISIX using a containerized Datadog agent. You will enable the [`datadog`](https://docs.api7.ai/hub/datadog) plugin in APISIX to export metrics to the agent, allowing you to monitor APISIX metrics in Datadog and use them to create additional monitoring, alerting, and analytics. ![APISIX and Datadog Architectural Diagram](https://static.api7.ai/uploads/2024/01/19/LUWRybZv_dd-sad.png) Prerequisite(s)[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#prerequisites "Direct link to Prerequisite(s)") ------------------------------------------------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker/) . * Install [cURL](https://curl.se/)  to send requests to the services for validation. * Follow the [Getting Started tutorial](https://docs.api7.ai/apisix/getting-started/)  to start a new APISIX instance in Docker or on Kubernetes. * Create a [Datadog account](https://www.datadoghq.com/) and note down the site and API key. Start Datadog Agent[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#start-datadog-agent "Direct link to Start Datadog Agent") --------------------------------------------------------------------------------------------------------------------------------------------------------------------- The [Datadog agent](https://docs.datadoghq.com/agent) collects events and metrics from monitored objects and sends them to Datadog, where you can further analyze your monitoring and performance data. Start the Datadog agent: docker run -d \ --name dogstatsd-agent \ -e DD_API_KEY=35ebe12345678dec56218930b79fdb4cf \ -e DD_SITE="us5.datadoghq.com" \ -e DD_HOSTNAME=apisix.quickstart \ -e DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true \ -p 8125:8125/udp \ datadog/dogstatsd:latest ❶ `DD_API_KEY`: replace with your API key. ❷ `DD_SITE`: replace with your Datadog site. ❸ `DD_HOSTNAME`: replace with your hostname. ❹ `DD_DOGSTATSD_NON_LOCAL_TRAFFIC`: set to true to listen to DogStatsD packets from other containers. You can configure most options in the agent’s main configuration file `datadog.yaml` through environment variables, prefixed with `DD_`. For more information, see [agent environment variables](https://docs.datadoghq.com/agent/guide/environment-variables) . Connect Datadog Agent to APISIX[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#connect-datadog-agent-to-apisix "Direct link to Connect Datadog Agent to APISIX") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- By default, the `datadog` plugin expects the Datadog agent to be available at `127.0.0.1:8125`. To update the IP address and other metadata, configure the [plugin metadata](https://docs.api7.ai/hub/datadog/configuration#metadata) of `datadog` plugin as such: * Admin API * ADC curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/datadog" -X PUT -d '{ "host": "192.168.0.90", "port": 8125, "namespace": "apisix"}' ❶ `host`: replace with your IP address. ❷ `port`: replace with your port, if not using the default `8125`. ❸ `namespace`: customize the namespace that prefixes all metrics. adc-plugin-metadata.yaml plugin_metadata: datadog: host: 192.168.0.90 port: 8125 namespace: ingress-apisix ❶ `host`: replace with your IP address. ❷ `port`: replace with your port, if not using the default `8125`. ❸ `namespace`: customize the namespace that prefixes all metrics. Synchronize the configuration to APISIX: adc sync -f adc-plugin-metadata.yaml Monitor Route Metrics[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#monitor-route-metrics "Direct link to Monitor Route Metrics") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Enable `datadog` globally and create a sample route to generate metrics. Alternatively, you can enable the plugin on a route. * Admin API * ADC Configure `datadog` to be a global plugin: curl "http://127.0.0.1:9180/apisix/admin/global_rules/datadog" -X PUT -d '{ "plugins": { "datadog": {} }}' Create a sample route on which you will collect metrics: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "dd-route", "uri": "/anything", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } }}' Configure `datadog` to be a global plugin and create a sample route on which you will collect metrics: adc-route.yaml global_rules: datadog: {}services: - name: httpbin Service routes: - uris: - /anything name: dd-route upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configurations to APISIX: adc sync -f adc-route.yaml -f adc-plugin-metadata.yaml In Datadog, select **Metrics** from the left menu and go to **Explorer**. You should see a number of APISIX metrics available: ![observe-apisix-metrics-available](https://static.api7.ai/uploads/2024/01/17/8o6oaNLT_dd-apisix-metrics.png) Generate a few requests to the route: curl "http://127.0.0.1:9080/anything" You should receive `HTTP/1.1 200 OK` responses. Navigate back to Datadog metric explorer and select `apisix.ingress.size.count` as the metric. You should see the count reflecting the number of requests generated: ![metric-showing-request-count](https://static.api7.ai/uploads/2024/01/17/Y0uHlIeS_dd-count.png) Next Steps[​](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#next-steps "Direct link to Next Steps") ------------------------------------------------------------------------------------------------------------------------------------------ You have now integrated Datadog with APISIX for metrics monitoring. With these metrics, you can now create [alerts](https://docs.datadoghq.com/monitors) and [dashboards](https://docs.datadoghq.com/dashboards) according to your system reliability requirements. For more information, see [Datadog documentation](https://docs.datadoghq.com/) . * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#prerequisites) * [Start Datadog Agent](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#start-datadog-agent) * [Connect Datadog Agent to APISIX](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#connect-datadog-agent-to-apisix) * [Monitor Route Metrics](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#monitor-route-metrics) * [Next Steps](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-datadog/#next-steps) --- # Log with ClickHouse | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page APISIX supports collecting route access information and recording it as logs, such as host, client IP, and request timestamp. This key information will be of great help in troubleshooting related problems. [ClickHouse](https://clickhouse.com/) is an open-source column-oriented database management system (DBMS) for online analytical processing (OLAP). It allows users to generate analytical reports such as log analytics using SQL queries in real-time. This guide will show you how to enable the `clickhouse-logger` plugin to record the APISIX logs into ClickHouse databases. Prerequisite(s)[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#prerequisites "Direct link to Prerequisite(s)") ----------------------------------------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker/) . * Install [cURL](https://curl.se/) to send requests to the services for validation. * Follow the [Getting Started tutorial](https://docs.api7.ai/apisix/getting-started/) to start a new APISIX instance in Docker or on Kubernetes. Configure ClickHouse[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#configure-clickhouse "Direct link to Configure ClickHouse") ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Start a ClickHouse instance named `quickstart-clickhouse-server` with a default database `quickstart_db`, a default user `quickstart-user` and password `quickstart-pass`: docker run -d \ --name quickstart-clickhouse-server \ --network=apisix-quickstart-net \ -e CLICKHOUSE_DB=quickstart_db \ -e CLICKHOUSE_USER=quickstart-user \ -e CLICKHOUSE_PASSWORD=quickstart-pass \ -e CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 \ --ulimit nofile=262144:262144 \ clickhouse/clickhouse-server Connect to the ClickHouse instance using the command line tool `clickhouse-client` in Docker: docker exec -it quickstart-clickhouse-server clickhouse-client Create a table `test` in database `quickstart_db` with fields `host`, `client_ip`, `route_id`, `@timestamp` of `String` type, or adjust the command accordingly based on your needs: CREATE TABLE quickstart_db.test ( `host` String, `client_ip` String, `route_id` String, `@timestamp` String, PRIMARY KEY(`@timestamp`)) ENGINE = MergeTree() If successful, you should see `Ok` on the output. Enter `exit` to exit the command line interface in Docker. Enable `clickhouse-logger` Plugin[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#enable-clickhouse-logger-plugin "Direct link to enable-clickhouse-logger-plugin") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Enable the `clickhouse-logger` plugin globally. Alternatively, you can enable the plugin on a route. * Admin API * ADC Enable the `clickhouse-logger` plugin globally: curl -i "http://127.0.0.1:9180/apisix/admin/global_rules" -X PUT -d '{ "id": "clickhouse", "plugins": { "clickhouse-logger": { "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr" }, "user": "quickstart-user", "password": "quickstart-pass", "database": "quickstart_db", "logtable": "test", "endpoint_addrs": ["http://quickstart-clickhouse-server:8123"] } }}' ➊ Specify fields corresponding to the ClickHouse table in the log format ➋ ClickHouse server information Create a sample route on which you will collect logs: curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "getting-started-ip", "uri": "/ip", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' Enable the `clickhouse-logger` plugin globally: adc-global-rule.yaml global_rules: clickhouse-logger: log_format: host: "$host" "@timestamp": "$time_iso8601" client_ip: "$remote_addr" user: "quickstart-user" password: "quickstart-pass" database: "quickstart_db" logtable: "test" endpoint_addrs: - "http://quickstart-clickhouse-server:8123" ➊ Specify fields corresponding to the ClickHouse table in the log format. ➋ ClickHouse server information. Create a sample route on which you will collect logs: adc-route.yaml services: - name: httpbin Service routes: - uris: - /ip name: getting-started-ip upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to APISIX: adc sync -f adc-global-rule.yaml -f adc-route.yaml Submit Logs in Batches[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#submit-logs-in-batches "Direct link to Submit Logs in Batches") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- The `clickhouse-logger` plugin supports using a batch processor to aggregate and process logs in batches. This avoids frequent submissions of log entries to ClickHouse, which slows down the operations. By default, the batch processor submits data every 5 seconds or when the data size in a batch reaches 1000 KB. You can adjust the time interval of submission `inactive_timeout` and maximum batch size `batch_max_size` for the plugin. For example, this is how you can set `inactive_timeout` to 10 seconds and `batch_max_size` to 2000 KB: * Admin API * ADC curl -i "http://127.0.0.1:9180/apisix/admin/global_rules/clickhouse" -X PATCH -d '{ "plugins": { "clickhouse-logger": { "batch_max_size": 2000, "inactive_timeout": 10 } }}' Update your global rule configuration to set `inactive_timeout` to 10 seconds and `batch_max_size` to 2000 KB: adc-global-rule.yaml global_rules: clickhouse-logger: log_format: host: "$host" "@timestamp": "$time_iso8601" client_ip: "$remote_addr" user: "quickstart-user" password: "quickstart-pass" database: "quickstart_db" logtable: "test" endpoint_addrs: - "http://quickstart-clickhouse-server:8123" batch_max_size: 2000 inactive_timeout: 10 Synchronize the configurations to APISIX: adc sync -f adc-global-rule.yaml -f adc-route.yaml Verify Logging[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#verify-logging "Direct link to Verify Logging") ---------------------------------------------------------------------------------------------------------------------------------------------- Send a request to the route to generate an access log entry: curl -i "http://127.0.0.1:9080/ip" Connect to the ClickHouse instance using the command line tool `clickhouse-client` in Docker: docker exec -it quickstart-clickhouse-server clickhouse-client Query all records in table `quickstart_db.test`: SELECT * from quickstart_db.test You should see an access record similar to the following, which verifies that the `clickhouse-logger` plugin works as intended. ┌─host──────┬─client_ip─┬─route_id─┬─@timestamp────────────────┐1. │ 127.0.0.1 │ 127.0.0.1 │ 5e835ead │ 2025-08-12T09:17:04+00:00 │ └───────────┴───────────┴──────────┴───────────────────────────┘ Next Steps[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#next-steps "Direct link to Next Steps") ---------------------------------------------------------------------------------------------------------------------------------- See [`clickhouse-logger`](https://docs.api7.ai/hub/clickhouse-logger) plugin doc to learn more about the plugin configuration options. * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#prerequisites) * [Configure ClickHouse](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#configure-clickhouse) * [Enable `clickhouse-logger` Plugin](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#enable-clickhouse-logger-plugin) * [Submit Logs in Batches](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#submit-logs-in-batches) * [Verify Logging](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#verify-logging) * [Next Steps](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-clickhouse/#next-steps) --- # Service Release | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/service-release/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page The core function of an API gateway is to expose backend services to developers and frontend applications via APIs. This process typically involves the following key steps: 1. **Creating Services**: The API gateway organizes and manages the microservices backend by defining services, allowing frontend applications to interact with backend services through a unified API interface. 2. **Configuring Routing Rules**: By setting up routing rules in the API gateway, external requests can be controlled and forwarded appropriately to internal microservices. 3. **Targeting Deployment Environment**: Selecting the appropriate gateway group for deployment ensures that the service configuration is effective in the intended environment. 4. **Adding Plugins**: The API gateway enhances its functionality through plugins, including authentication, data encryption, and rate limiting to meet requirements of API security, monitoring, and rate limiting. This entire process, from service definition to deployment, is collectively called service release. In practical business scenarios, effectively managing service releases and avoiding production environment failures caused by deployment errors has become a crucial task of API gateway management in enterprises. Two Main Approaches to Service Release[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#two-main-approaches-to-service-release "Direct link to Two Main Approaches to Service Release") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In production environments, companies choose various service release processes and rules based on their specific needs and development strategies. Broadly, these processes can be categorized into two main approaches: ### Rapid Service Release[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#rapid-service-release "Direct link to Rapid Service Release") Some enterprises opt for a rapid iteration approach. They release services directly to the production environment after developing and verifying the services in the development and testing phases. This method is typically suitable for scenarios where requirements change rapidly and there is a high demand for service responsiveness. To mitigate deployment risks, these companies often implement strategies such as canary releases and blue-green deployments, which enable phased rollouts of new service versions, ensuring that any anomalies can be quickly rolled back to a stable version. For instance, an e-commerce platform might directly release a newly configured payment service to the production environment after validation. During the deployment process, they use a canary release strategy to allocate the new service version to a specific percentage of users, allowing for immediate detection and adjustment in case of issues in the production environment. ### Version-Based Service Release[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#version-based-service-release "Direct link to Version-Based Service Release") Conversely, some companies prefer to ensure the stability and availability of services through a more rigorous process before releasing services to production. These organizations typically deploy services first to a testing environment, followed by thorough validation, then to a User Acceptance Testing (UAT) environment, and finally to the production environment. This multi-stage validation reduces the risk of failures in the production environment. For example, a financial institution might adopt this process by first validating a new version of its authentication service in the testing environment. After successful validation, it would proceed to the UAT environment for performance verification, ensuring the service can operate stably under high concurrency conditions, before finally deploying it to production. Both approaches have their advantages: rapid service release is ideal for fast-paced iteration, while version-based service release emphasizes service stability and reliability. Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#key-features "Direct link to Key Features") ---------------------------------------------------------------------------------------------------------------------------- In API7 Enterprise, service release is robust and capable of meeting various business needs. Key capabilities include: ### Service Templates[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#service-templates "Direct link to Service Templates") One of the key features is the introduction of service templates, which provide flexible and reusable configuration templates for service release. Users can define a standard set of service configurations as a template for reuse across different environments. This process simplifies the release process, enhances efficiency, and reduces human error. For example, users can create a "Generic Payment Service Template" that defines common payment service interfaces and routing rules. Whenever a new payment service needs to be published, teams can quickly generate a comprehensive service by selecting this template, deploying it to the desired environment, and inputting specific parameters. Service templates also support version control, allowing users to flexibly choose which version of the service template to use based on different requirements and historical versions. This way, when service configurations change, teams can track all published changes through version management, ensuring that each version has a clear historical record. ### Published Version Management[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#published-version-management "Direct link to Published Version Management") API7 Enterprise not only supports creating new services but also allows users to view and manage published service versions. Each service version can be managed independently, enabling users to review historical configuration settings, perform version rollbacks, or reference previous configurations when releasing new versions. This management approach greatly enhances the flexibility and security of service publishing. If an issue arises with a particular version during the publishing process, users can directly roll back to the previous version, avoiding service interruptions in the production environment. ### Historical Version Tracking[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#historical-version-tracking "Direct link to Historical Version Tracking") API7 Enterprise also provides a historical version feature that helps users track change records for each service release. This is particularly important for long-term service maintenance, especially in multi-team collaboration scenarios. By reviewing historical versions, developers can easily pinpoint issues caused by specific configuration changes, thereby reducing the time required for troubleshooting and fixing problems. For instance, if a performance issue arises in the payment service after a particular release, developers can quickly identify that a certain configuration item in the service template has changed by examining the historical versions, allowing them to trace the specific cause. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#use-cases "Direct link to Use Cases") ------------------------------------------------------------------------------------------------------------------- The service release feature of API7 Enterprise is applicable to various real-world scenarios, providing significant convenience in multi-environment deployment and version management. ### Multi-Environment Deployment[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#multi-environment-deployment "Direct link to Multi-Environment Deployment") In large enterprises, service deployment typically involves multiple environments, such as development, testing, UAT, and production environments. API7 Enterprise supports reusing configurations through service templates, greatly simplifying the service release process across different environments. For example, developers can complete the service configuration in the development environment and, once validated, easily migrate that configuration to the testing environment using service templates. After successful testing, the service can then be promoted to the UAT environment and finally deployed to production. ### Version Management and Rollback[​](https://docs.api7.ai/apisix/enterprise-feature/service-release/#version-management-and-rollback "Direct link to Version Management and Rollback") For services that require frequent updates, version management and rollback are crucial. The version control feature in API7 Enterprise helps organizations effectively manage the history of service deployments. In the event of an issue, services can be quickly rolled back to a stable version, preventing serious failures in the production environment. For instance, if an incompatible change in the API payment interface in a particular version leads to payment failures for some users, developers can swiftly use the rollback feature of API7 Enterprise to revert the service to the previous version, ensuring that payment functionality is restored. * [Two Main Approaches to Service Release](https://docs.api7.ai/apisix/enterprise-feature/service-release/#two-main-approaches-to-service-release) * [Rapid Service Release](https://docs.api7.ai/apisix/enterprise-feature/service-release/#rapid-service-release) * [Version-Based Service Release](https://docs.api7.ai/apisix/enterprise-feature/service-release/#version-based-service-release) * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/service-release/#key-features) * [Service Templates](https://docs.api7.ai/apisix/enterprise-feature/service-release/#service-templates) * [Published Version Management](https://docs.api7.ai/apisix/enterprise-feature/service-release/#published-version-management) * [Historical Version Tracking](https://docs.api7.ai/apisix/enterprise-feature/service-release/#historical-version-tracking) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/service-release/#use-cases) * [Multi-Environment Deployment](https://docs.api7.ai/apisix/enterprise-feature/service-release/#multi-environment-deployment) * [Version Management and Rollback](https://docs.api7.ai/apisix/enterprise-feature/service-release/#version-management-and-rollback) --- # Plugin Common Configurations | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Plugin common configurations are a set of configuration options that can be applied universally to all APISIX plugins through the `_meta` attribute. They can be used to configure: * [Plugin conditional execution](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metafilter) * [Plugin execution priorities](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metapriority) * [Plugin disablement](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metadisable) * [Plugin error response](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metaerror_response) * [Custom code execution before each plugin phase](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metapre_function) (Enterprise feature) `_meta.filter`[​](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metafilter "Direct link to _metafilter") -------------------------------------------------------------------------------------------------------------------------------- You can use `_meta.filter` to configure the conditional execution of plugins based on request parameters. Conditions are created with [APISIX expressions](https://docs.api7.ai/apisix/reference/apisix-expressions) and configured as an array. A plugin only executes when all conditions are met. For example, the following configuration sets a condition on the request's URI [query parameter](https://en.wikipedia.org/wiki/Query_string) . Only requests with the URI query parameter `version` being `v2` will trigger the execution of the `proxy-rewrite` plugin, which rewrites the request's URI path to `/api/v2` before forwarding it to the upstream: { ..., "plugins": { "proxy-rewrite": { "uri": "/api/v2", "_meta": { "filter": [ ["arg_version", "==", "v2"] ] } } }} Requests not meeting the condition will not have their URI paths rewritten and will be forwarded as-is. `_meta.priority`[​](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metapriority "Direct link to _metapriority") -------------------------------------------------------------------------------------------------------------------------------------- You can use `_meta.priority` to adjust the execution order of **plugins of the same type** (i.e. global or non-global) **within a given phase**. Once defined, the value will take precedence over the default plugin priority defined in the [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) . Suppose two plugins that run in the same [phase](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) , `limit-count` and `ip-restriction`, are configured on the same route. `limit-count` has a default priority of 1002 and `ip-restriction` has a default priority of 3000. When a request is sent to the route, `ip-restriction` is executed first as it has a higher default priority value. However, you can run `limit-count` before `ip-restriction` by assigning `_meta.priority` of `limit-count` a priority value higher than 3000, such as: { ..., "plugins": { "limit-count": { ..., "_meta": { "priority": 3010 } } }} To learn more about the execution order when global and non-global plugins are used together, see [plugin execution order](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-order) . `_meta.disable`[​](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metadisable "Direct link to _metadisable") ----------------------------------------------------------------------------------------------------------------------------------- You can use `_meta.disable` to disable a plugin without removing the plugin from the object it is bound to entirely. For example, you can disable the `proxy-rewrite` plugin with the following: { "plugins": { "proxy-rewrite": { "_meta": { "disable": true } } }} `_meta.error_response`[​](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metaerror_response "Direct link to _metaerror_response") -------------------------------------------------------------------------------------------------------------------------------------------------------- You can use `_meta.error_response` to customize the error response returned by a plugin to a fixed value. This could be used to mitigate complications that may arise from the default error response in some cases. For example, you can customize the error response of the `limit-count` plugin: { "plugins": { "limit-count": { "count": 1, "time_window": 60, "_meta": { "error_response": { "message": "You have exceeded the rate limiting threshold." } } } }} If more than one request is sent within the 60-second window to the route that the plugin binds to, you should see the following response: {"message":"You have exceeded the rate limiting threshold."} `_meta.pre_function`[​](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metapre_function "Direct link to _metapre_function") -------------------------------------------------------------------------------------------------------------------------------------------------- You can use `_meta.pre_function` to configure the custom code execution prior to each [phase](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) of plugin execution. The following example will show you how to declare a `_meta.pre_function` in the [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite) plugin to extract the `user_id` in the request path, register it as a variable, and use it to compose the new request path. In order to use parameters in the route's URI, you should first update the router in the [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) to be `radixtree_uri_with_parameter` as it is not the default setting: config.yaml apisix: router: http: radixtree_uri_with_parameter Then [reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for the changes to take effect. Create a route to the httpbin service to examine the rewritten path: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \-H "X-API-KEY: ${ADMIN_API_KEY}" \-d '{ "id": "pre-func-route", "uri": "/anything/:user_id/hello", "plugins": { "proxy-rewrite": { "_meta": { "pre_function": " return function(conf, ctx) local core = require \"apisix.core\" core.ctx.register_var(\"user_id\", function(ctx) return ctx.curr_req_matched.user_id end) end" }, "uri": "/anything/$user_id/world" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' ❶ Match requests to `/anything/:user_id/hello` where `user_id` is a parameter. ❷ Customize the `_meta.pre_function` to extract the `user_id` value from the requested path and save it to a variable of the same name. ❸ Rewrite the request path to `/anything/$user_id/world`, where `$user_id` will be replaced by the variable value. Send a request to the route: curl -i "http://127.0.0.1:9080/anything/johndoe/hello" You should observe the following response, showing the request path has been rewritten partially with the `user_id`: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "text/html..." ... }, "json": null, "method": "GET", "origin": "127.0.0.1, 59.71.xxx.xxx", "url": "http://127.0.0.1/anything/johndoe/world"} Differentiate from Plugin Metadata[​](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#differentiate-from-plugin-metadata "Direct link to Differentiate from Plugin Metadata") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- When working with plugins, it is important to understand the distinctions between the `_meta` common configurations, as outlined in this document, and the [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) . These two concepts serve different purposes and should not be mixed. While the `_meta` common configurations refer to configuration options that are available for all APISIX plugins, plugin metadata only applies to plugins that have metadata attributes. These metadata attributes could also be configured with the Admin API plugin metadata resource. See [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) to learn more. * [`_meta.filter`](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metafilter) * [`_meta.priority`](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metapriority) * [`_meta.disable`](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metadisable) * [`_meta.error_response`](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metaerror_response) * [`_meta.pre_function`](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#_metapre_function) * [Differentiate from Plugin Metadata](https://docs.api7.ai/apisix/reference/plugin-common-configurations/#differentiate-from-plugin-metadata) --- # How APISIX Works | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-apisix-works/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 Coming soon. --- # Serve Static Resources | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/production/serve-static-resources/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Static resources are files that are served directly to clients without any modification or processing at runtime. These files are typically intended to remain unchanged and are commonly used to deliver assets such as HTML, CSS, JavaScript, images, fonts, and other media files. Unlike dynamic resources that are generated or processed dynamically on the server, static resources are pre-existing files that are served as-is to the client. In this guide, you will learn how to configure APISIX to cache and serve static resources. Create a Route[​](https://docs.api7.ai/apisix/production/serve-static-resources/#create-a-route "Direct link to Create a Route") --------------------------------------------------------------------------------------------------------------------------------- For demonstration, the example below will use APISIX to reverse proxy static content from the Web Hypertext Application Technology Working Group (WHATWG) public GitHub repository. Create a route to the WHATWG GitHub repository, configure it to match the desired static resource extensions, and enable [`proxy-cache`](https://docs.api7.ai/hub/proxy-cache) plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "static-src-route", "uri": "/whatwg/*", "vars": [["uri", "~~", "(.jpeg|.html)$"]], "plugins": { "proxy-cache": {} }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "raw.githubusercontent.com": 1 } } }' ❶ Match requests looking for files with `.jpeg` and `.html` extensions. ❷ Enable [`proxy-cache`](https://docs.api7.ai/hub/proxy-cache) on the route. ### Verify[​](https://docs.api7.ai/apisix/production/serve-static-resources/#verify "Direct link to Verify") Send a request to the route for an HTML file: curl -i "http://127.0.0.1:9080/whatwg/html/main/404.html" You should receive an `HTTP/1.1 200 OK` response and see the following file content: 404 Not Found

Loading...

Not Found

The page you are looking for is no longer available at this URL.

Links to the multipage version of the specification are unfortunately likely to break over time. You might be able to find what you want from the contents page.

If you have found a broken link on the WHATWG site itself, please file an issue. If you found a broken link from another site pointing to the WHATWG site, please let that site know of the problem instead. Thanks!

Similarly, you could also send a request to the route to download a JPEG image: curl -Ov "http://127.0.0.1:9080/whatwg/html/main/images/abstract.jpeg" You should receive an `HTTP/1.1 200 OK` response and see the image saved in the current directory. * [Create a Route](https://docs.api7.ai/apisix/production/serve-static-resources/#create-a-route) * [Verify](https://docs.api7.ai/apisix/production/serve-static-resources/#verify) --- # AI RAG | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ai-rag/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ai-rag` plugin provides Retrieval-Augmented Generation (RAG) capabilities with LLMs. It facilitates the efficient retrieval of relevant documents or information from external data sources, which are used to enhance the LLM responses, thereby improving the accuracy and contextual relevance of the generated outputs. The plugin supports using [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) and [Azure AI Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search) services for generating embeddings and performing vector search. Example[​](https://docs.api7.ai/hub/ai-rag/#example "Direct link to Example") ------------------------------------------------------------------------------ To follow along the example, create an [Azure account](https://portal.azure.com/) and complete the following steps: * In [Azure AI Foundry](https://oai.azure.com/portal) , deploy a generative chat model, such as `gpt-4o`, and an embedding model, such as `text-embedding-3-large`. Obtain the API key and model endpoints. * Follow [Azure's example](https://github.com/Azure/azure-search-vector-samples/blob/main/demo-python/code/basic-vector-workflow/azure-search-vector-python-sample.ipynb) to prepare for a vector search in [Azure AI Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search) using Python. The example will create a search index called `vectest` with the desired schema and upload the [sample data](https://github.com/Azure/azure-search-vector-samples/blob/main/data/text-sample.json) which contains 108 descriptions of various Azure services, for embeddings `titleVector` and `contentVector` to be generated based on `title` and `content`. Complete all the setups before performing vector searches in Python. * In [Azure AI Search](https://azure.microsoft.com/en-us/products/ai-services/ai-search) , [obtain the Azure vector search API key and the search service endpoint](https://learn.microsoft.com/en-us/azure/search/search-get-started-vector?tabs=api-key#retrieve-resource-information) . Save the API keys and endpoints to environment variables: # replace with your valuesAZ_OPENAI_DOMAIN=https://ai-plugin-developer.openai.azure.comAZ_OPENAI_API_KEY=9m7VYroxITMDEqKKEnpOknn1rV7QNQT7DrIBApcwMLYJQQJ99ALACYeBjFXJ3w3AAABACOGXGcdAZ_CHAT_ENDPOINT=${AZ_OPENAI_DOMAIN}/openai/deployments/gpt-4o/chat/completions?api-version=2024-02-15-previewAZ_EMBEDDING_MODEL=text-embedding-3-largeAZ_EMBEDDINGS_ENDPOINT=${AZ_OPENAI_DOMAIN}/openai/deployments/${AZ_EMBEDDING_MODEL}/embeddings?api-version=2023-05-15AZ_AI_SEARCH_SVC_DOMAIN=https://ai-plugin-developer.search.windows.netAZ_AI_SEARCH_KEY=IFZBp3fKVdq7loEVe9LdwMvVdZrad9A4lPH90AzSeC06SlRAZ_AI_SEARCH_INDEX=vectestAZ_AI_SEARCH_ENDPOINT=${AZ_AI_SEARCH_SVC_DOMAIN}/indexes/${AZ_AI_SEARCH_INDEX}/docs/search?api-version=2024-07-01 ### Integrate with Azure for RAG-Enhaned Responses[​](https://docs.api7.ai/hub/ai-rag/#integrate-with-azure-for-rag-enhaned-responses "Direct link to Integrate with Azure for RAG-Enhaned Responses") The following example demonstrates how you can use the [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugin to proxy requests to Azure OpenAI LLM and use the `ai-rag` plugin to generate embeddings and perform vector search to enhance LLM responses. * Admin API * ADC * Ingress Controller Create a route as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-rag-route", "uri": "/rag", "plugins": { "ai-rag": { "embeddings_provider": { "azure_openai": { "endpoint": "'"$AZ_EMBEDDINGS_ENDPOINT"'", "api_key": "'"$AZ_OPENAI_API_KEY"'" } }, "vector_search_provider": { "azure_ai_search": { "endpoint": "'"$AZ_AI_SEARCH_ENDPOINT"'", "api_key": "'"$AZ_AI_SEARCH_KEY"'" } } }, "ai-proxy": { "provider": "openai", "auth": { "header": { "api-key": "'"$AZ_OPENAI_API_KEY"'" } }, "model": "gpt-4o", "override": { "endpoint": "'"$AZ_CHAT_ENDPOINT"'" } } }}' Create a route with the `ai-rag` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: adc.yaml services: - name: ai-rag-service routes: - name: ai-rag-route uris: - /rag methods: - POST plugins: ai-rag: embeddings_provider: azure_openai: endpoint: "${AZ_EMBEDDINGS_ENDPOINT}" api_key: "${AZ_OPENAI_API_KEY}" vector_search_provider: azure_ai_search: endpoint: "${AZ_AI_SEARCH_ENDPOINT}" api_key: "${AZ_AI_SEARCH_KEY}" ai-proxy: provider: openai auth: header: api-key: "${AZ_OPENAI_API_KEY}" model: gpt-4o override: endpoint: "${AZ_CHAT_ENDPOINT}" Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Create a route with the `ai-rag` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-rag-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-rag-plugin-configspec: plugins: - name: ai-rag config: embeddings_provider: azure_openai: endpoint: "https://ai-plugin-developer.openai.azure.com/openai/deployments/text-embedding-3-large/embeddings?api-version=2023-05-15" api_key: "9m7VYroxITMDEqKKEnpOknn1rV7QNQT7DrIBApcwMLYJQQJ99ALACYeBjFXJ3w3AAABACOGXGcd" vector_search_provider: azure_ai_search: endpoint: "https://ai-plugin-developer.search.windows.net/indexes/vectest/docs/search?api-version=2024-07-01" api_key: "IFZBp3fKVdq7loEVe9LdwMvVdZrad9A4lPH90AzSeC06SlR" - name: ai-proxy config: provider: openai auth: header: api-key: "9m7VYroxITMDEqKKEnpOknn1rV7QNQT7DrIBApcwMLYJQQJ99ALACYeBjFXJ3w3AAABACOGXGcd" model: gpt-4o override: endpoint: "https://ai-plugin-developer.openai.azure.com/openai/deployments/gpt-4o/chat/completions?api-version=2024-02-15-preview"---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: ai-rag-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /rag method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-rag-plugin-config Apply the configuration to your cluster: kubectl apply -f ai-rag-ic.yaml Create a route with the `ai-rag` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-rag-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: ai-rag-routespec: ingressClassName: apisix http: - name: ai-rag-route match: paths: - /rag methods: - POST plugins: - name: ai-rag enable: true config: embeddings_provider: azure_openai: endpoint: "https://ai-plugin-developer.openai.azure.com/openai/deployments/text-embedding-3-large/embeddings?api-version=2023-05-15" api_key: "9m7VYroxITMDEqKKEnpOknn1rV7QNQT7DrIBApcwMLYJQQJ99ALACYeBjFXJ3w3AAABACOGXGcd" vector_search_provider: azure_ai_search: endpoint: "https://ai-plugin-developer.search.windows.net/indexes/vectest/docs/search?api-version=2024-07-01" api_key: "IFZBp3fKVdq7loEVe9LdwMvVdZrad9A4lPH90AzSeC06SlR" - name: ai-proxy enable: true config: provider: openai auth: header: api-key: "9m7VYroxITMDEqKKEnpOknn1rV7QNQT7DrIBApcwMLYJQQJ99ALACYeBjFXJ3w3AAABACOGXGcd" model: gpt-4o override: endpoint: "https://ai-plugin-developer.openai.azure.com/openai/deployments/gpt-4o/chat/completions?api-version=2024-02-15-preview" Apply the configuration to your cluster: kubectl apply -f ai-rag-ic.yaml Send a POST request to the route with the vector fields name, embedding model dimensions, and an input prompt in the request body: curl "http://127.0.0.1:9080/rag" -X POST \ -H "Content-Type: application/json" \ -d '{ "ai_rag":{ "vector_search":{ "fields":"contentVector" }, "embeddings":{ "input":"Which Azure services are good for DevOps?", "dimensions":1024 } } }' You should receive an `HTTP/1.1 200 OK` response similar to the following: { "choices": [ { "content_filter_results": { ... }, "finish_reason": "length", "index": 0, "logprobs": null, "message": { "content": "Here is a list of Azure services categorized along with a brief description of each based on the provided JSON data:\n\n### Developer Tools\n- **Azure DevOps**: A suite of services that help you plan, build, and deploy applications, including Azure Boards, Azure Repos, Azure Pipelines, Azure Test Plans, and Azure Artifacts.\n- **Azure DevTest Labs**: A fully managed service to create, manage, and share development and test environments in Azure, supporting custom templates, cost management, and integration with Azure DevOps.\n\n### Containers\n- **Azure Kubernetes Service (AKS)**: A managed container orchestration service based on Kubernetes, simplifying deployment and management of containerized applications with features like automatic upgrades and scaling.\n- **Azure Container Instances**: A serverless container runtime to run and scale containerized applications without managing the underlying infrastructure.\n- **Azure Container Registry**: A fully managed Docker registry service to store and manage container images and artifacts.\n\n### Web\n- **Azure App Service**: A fully managed platform for building, deploying, and scaling web apps, mobile app backends, and RESTful APIs with support for multiple programming languages.\n- **Azure SignalR Service**: A fully managed real-time messaging service to build and scale real-time web applications.\n- **Azure Static Web Apps**: A serverless hosting service for modern web applications using static front-end technologies and serverless APIs.\n\n### Compute\n- **Azure Virtual Machines**: Infrastructure-as-a-Service (IaaS) offering for deploying and managing virtual machines in the cloud.\n- **Azure Functions**: A serverless compute service to run event-driven code without managing infrastructure.\n- **Azure Batch**: A job scheduling service to run large-scale parallel and high-performance computing (HPC) applications.\n- **Azure Service Fabric**: A platform to build, deploy, and manage scalable and reliable microservices and container-based applications.\n- **Azure Quantum**: A quantum computing service to build and run quantum applications.\n- **Azure Stack Edge**: A managed edge computing appliance to run Azure services and AI workloads on-premises or at the edge.\n\n### Security\n- **Azure Bastion**: A fully managed service providing secure and scalable remote access to virtual machines.\n- **Azure Security Center**: A unified security management service to protect workloads across Azure and on-premises infrastructure.\n- **Azure DDoS Protection**: A cloud-based service to protect applications and resources from distributed denial-of-service (DDoS) attacks.\n\n### Databases\n", "role": "assistant" } } ], "created": 1740625850, "id": "chatcmpl-B54gQdumpfioMPIybFnirr6rq9ZZS", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [ { "prompt_index": 0, "content_filter_results": { ... } } ], "system_fingerprint": "fp_65792305e4", "usage": { ... }} * [Example](https://docs.api7.ai/hub/ai-rag/#example) * [Integrate with Azure for RAG-Enhaned Responses](https://docs.api7.ai/hub/ai-rag/#integrate-with-azure-for-rag-enhaned-responses) --- # Graphql Proxy Cache | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/graphql-proxy-cache/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `graphql-proxy-cache` plugin provides the capability to cache responses for GraphQL queries. It uses [MD5](https://en.wikipedia.org/wiki/MD5) algorithm to generate cache key based on the plugin configurations and GraphQL queries. The plugin supports both disk-based and memory-based caching options to cache for [GET](https://graphql.org/learn/serving-over-http/#get-request) and [POST](https://graphql.org/learn/serving-over-http/#post-request) GraphQL requests. If a request contains a [mutation](https://graphql.org/learn/queries#mutations) operation, the plugin will not cache the data. Instead, it adds an `Apisix-Cache-Status: BYPASS` header to the response to show that the request bypasses the caching mechanism. Examples[​](https://docs.api7.ai/hub/graphql-proxy-cache/#examples "Direct link to Examples") ---------------------------------------------------------------------------------------------- The examples below use [GitHub GraphQL API](https://docs.github.com/en/graphql) as an upstream and demonstrate how you can configure `graphql-proxy-cache` for different scenarios. To follow along, create a GitHub [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with the appropriate scopes for the resources you want to interact with. ### Cache Data on Disk[​](https://docs.api7.ai/hub/graphql-proxy-cache/#cache-data-on-disk "Direct link to Cache Data on Disk") On-disk caching strategy offers the advantages of data persistency when system restarts and having larger storage capacity compared to in-memory cache. It is suitable for applications that prioritize durability and can tolerate slightly larger cache access latency. The following example demonstrates how you can use `graphql-proxy-cache` plugin on a route to cache data on disk. Create a route with the `graphql-proxy-cache` plugin with the default configuration to cache data on disk: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-proxy-cache-route", "uri": "/graphql", "plugins": { "graphql-proxy-cache": {} }, "upstream": { "type": "roundrobin", "pass_host": "node", "scheme": "https", "nodes": { "api.github.com:443": 1 } } }' Send a request with a GraphQL query to verify: curl -i "http://127.0.0.1:9080/graphql" -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -d '{"query": "query {viewer{login}}"}' You should see an `HTTP/1.1 200 OK` response with the following headers, showing the plugin is successfully enabled: APISIX-Cache-Key: e9c1624ee35f792548512ff9f6ff1bfaApisix-Cache-Status: MISS As there is no cache available before the first response, `Apisix-Cache-Status: MISS` is shown. Send the same request again within the cache TTL window. You should see an `HTTP/1.1 200 OK` response with the following headers, showing the cache is hit: APISIX-Cache-Key: e9c1624ee35f792548512ff9f6ff1bfaApisix-Cache-Status: HIT Wait for the cache to expire after the TTL and send the same request again. You should see an `HTTP/1.1 200 OK` response with the following headers, showing the cache has expired: APISIX-Cache-Key: e9c1624ee35f792548512ff9f6ff1bfaApisix-Cache-Status: EXPIRED ### Cache Data in Memory[​](https://docs.api7.ai/hub/graphql-proxy-cache/#cache-data-in-memory "Direct link to Cache Data in Memory") In-memory caching strategy offers the advantage of low-latency access to the cached data, as retrieving data from RAM is faster than retrieving data from disk storage. It also works well for storing temporary data that does not need to be persisted long-term, allowing for efficient caching of frequently changing data. The following example demonstrates how you can use `graphql-proxy-cache` plugin on a route to cache data in memory. Create a route with `graphql-proxy-cache` enabled and configure it to use memory-based caching: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-proxy-cache-route", "uri": "/graphql", "plugins": { "graphql-proxy-cache": { "cache_strategy": "memory", "cache_zone": "memory_cache", "cache_ttl": 10 } }, "upstream": { "type": "roundrobin", "pass_host": "node", "scheme": "https", "nodes": { "api.github.com:443": 1 } } }' ❶ `cache_strategy`: set to `memory` for in-memory setting. ❷ `cache_zone`: set to the name of an in-memory cache zone. ❸ `cache_ttl`: set the time to live for the in-memory cache. Send a request with a GraphQL query to verify: curl "http://127.0.0.1:9080/graphql" -i -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -d '{"query": "query {viewer{login}}"}' You should see an `HTTP/1.1 200 OK` response with the following headers, showing the plugin is successfully enabled: APISIX-Cache-Key: a661316c4b1b70ae2db5347743dec6b6Apisix-Cache-Status: MISS As there is no cache available before the first response, `Apisix-Cache-Status: MISS` is shown. Send the same request again within the cache TTL window. You should see an `HTTP/1.1 200 OK` response with the following headers, showing the cache is hit: APISIX-Cache-Key: a661316c4b1b70ae2db5347743dec6b6Apisix-Cache-Status: HIT ### Remove Cache Manually[​](https://docs.api7.ai/hub/graphql-proxy-cache/#remove-cache-manually "Direct link to Remove Cache Manually") While most of the time it is not necessary, there may be situations where you would want to manually remove cached data. The following example demonstrates how you can use the `public-api` plugin to expose the `/apisix/plugin/graphql-proxy-cache/{cache_strategy}/{route_id}/{key}` endpoint created by the `graphql-proxy-cache` plugin to manually remove cache. Create a route that matches the URI `/apisix/plugin/graphql-proxy-cache/*`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-cache-purge", "uri": "/apisix/plugin/graphql-proxy-cache/*", "plugins": { "public-api": {} } }' Send a PURGE request to remove data cached on disk for route `1`: curl -i "http://127.0.0.1:9080/apisix/plugin/graphql-proxy-cache/disk/1/e9c1624ee35f792548512ff9f6ff1bfa" -X PURGE An `HTTP/1.1 200 OK` response verifies that the cache corresponding to the key is successfully removed. If you send the same request again, you should see an `HTTP/1.1 404 Not Found` response, showing there is no cache on disk with this cache key after the cache removal. * [Examples](https://docs.api7.ai/hub/graphql-proxy-cache/#examples) * [Cache Data on Disk](https://docs.api7.ai/hub/graphql-proxy-cache/#cache-data-on-disk) * [Cache Data in Memory](https://docs.api7.ai/hub/graphql-proxy-cache/#cache-data-in-memory) * [Remove Cache Manually](https://docs.api7.ai/hub/graphql-proxy-cache/#remove-cache-manually) --- # Proxy Mirror | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/proxy-mirror/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `proxy-mirror` plugin duplicates ingress traffic to APISIX and forwards them to a designated upstream, without interrupting the regular services. You can configure the plugin to mirror all traffic or only a portion. The mechanism benefits a few use cases, including troubleshooting, security inspection, analytics, and more. Note that APISIX ignores any response from the upstream host receiving mirrored traffic. Examples[​](https://docs.api7.ai/hub/proxy-mirror/#examples "Direct link to Examples") --------------------------------------------------------------------------------------- The examples below demonstrate how to configure `proxy-mirror` for different scenarios. ### Mirror Partial Traffic[​](https://docs.api7.ai/hub/proxy-mirror/#mirror-partial-traffic "Direct link to Mirror Partial Traffic") The following example demonstrates how you can configure `proxy-mirror` to mirror 50% of the traffic to a route and forward them to another upstream service. Start a sample NGINX server for receiving mirrored traffic: docker run -p 8081:80 --name nginx nginx You should see NGINX access log and error log on the terminal session. Open a new terminal session and create a route with `proxy-mirror`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "traffic-mirror-route", "uri": "/get", "plugins": { "proxy-mirror": { "host": "http://127.0.0.1:8081", "sample_ratio": 0.5 } }, "upstream": { "nodes": { "httpbin.org": 1 }, "type": "roundrobin" } }' ❶ `host`: configure the scheme and host address to forward the mirrored traffic to. ❷ `sample_ratio`: configure the sampling ratio to 0.5 to mirror 50% of the traffic. Send Generate a few requests to the route: curl -i "http://127.0.0.1:9080/get" You should receive `HTTP/1.1 200 OK` responses for all requests. Navigating back to the NGINX terminal session, you should see a number of access log entries, roughly half the number of requests generated: 172.17.0.1 - - [29/Jan/2024:23:11:01 +0000] "GET /get HTTP/1.1" 404 153 "-" "curl/7.64.1" "-" This suggests APISIX has mirrored the request to the NGINX server. Here, the HTTP response status is `404` since the sample NGINX server does not implement the route. ### Configure Mirroring Timeouts[​](https://docs.api7.ai/hub/proxy-mirror/#configure-mirroring-timeouts "Direct link to Configure Mirroring Timeouts") The following example demonstrates how you can update the default connect, read, and send timeouts for the plugin. This could be useful when mirroring traffic to a very slow backend service. As the request mirroring was implemented as sub-requests, excessive delays in the sub-requests could lead to the blocking of the original requests. By default, the connect, read, and send timeouts are set to 60 seconds. To update these values, you can configure them in the `plugin_attr` section of the [configuration files](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) as such: conf/config.yaml plugin_attr: proxy-mirror: timeout: connect: 2000ms read: 2000ms send: 2000ms [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. * [Examples](https://docs.api7.ai/hub/proxy-mirror/#examples) * [Mirror Partial Traffic](https://docs.api7.ai/hub/proxy-mirror/#mirror-partial-traffic) * [Configure Mirroring Timeouts](https://docs.api7.ai/hub/proxy-mirror/#configure-mirroring-timeouts) --- # SOAP | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/soap/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `soap` plugin provides a convenient approach to transform between RESTful HTTP requests and SOAP requests, as well as their corresponding responses. With a single URL to the [WSDL](https://en.wikipedia.org/wiki/Web_Services_Description_Language) file, API7 automatically parses the file content and generates conversion logics to allow for the protocol transcoding. Examples[​](https://docs.api7.ai/hub/soap/#examples "Direct link to Examples") ------------------------------------------------------------------------------- ### Invoke an Operation[​](https://docs.api7.ai/hub/soap/#invoke-an-operation "Direct link to Invoke an Operation") The following example demonstrates how you can configure the plugin on a route and invoke an operation available on the upstream server as specified in the WSDL file. Create a route with the `soap` plugin: curl 'http://127.0.0.1:9180/apisix/admin/routes' -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "id": "soap-hello", "uri": "/SayHello", "methods": ["POST"], "plugins": { "soap": { "wsdl_url": "https://apps.learnwebservices.com/services/hello?wsdl" } } }' ❶ Set the URI to the name of the operation in the WSDL file. ❷ Allow only for the POST request method. ❸ Set the URL path to the WSDL file. Send a request to the route verify: curl 'http://127.0.0.1:9080/SayHello' -X POST -d '{"Name": "John Doe"}' You should see an `HTTP/1.1 200 OK` response with the following: "Hello John Doe!" * [Examples](https://docs.api7.ai/hub/soap/#examples) * [Invoke an Operation](https://docs.api7.ai/hub/soap/#invoke-an-operation) --- # Attach Consumer Label | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/attach-consumer-label/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `attach-consumer-label` plugin attaches custom consumer-related labels, in addition to `X-Consumer-Username` and `X-Credential-Identifier`, to authenticated requests, for upstream services to differentiate between consumers and implement additional logics. Example[​](https://docs.api7.ai/hub/attach-consumer-label/#example "Direct link to Example") --------------------------------------------------------------------------------------------- ### Attach Consumer Labels[​](https://docs.api7.ai/hub/attach-consumer-label/#attach-consumer-labels "Direct link to Attach Consumer Labels") The following example demonstrates how you can attach custom labels to request headers before authenticated requests are forwarded to upstream services. If the request is rejected, you should not see any consumer labels attached to request headers. If a certain label value is not configured on the consumer but referenced in the `attach-consumer-label` plugin, the corresponding header will also not be attached. Create a consumer `john` with custom labels: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john", "labels": { "department": "devops", "company": "api7" } }' ❶ Label the `department` information for the consumer. ❷ Label the `company` information for the consumer. Configure the `key-auth` credential for the consumer `john`: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Create a route enabling the `key-auth` and `attach-consumer-label` plugins: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "attach-consumer-label-route", "uri": "/get", "plugins": { "key-auth": {}, "attach-consumer-label": { "headers": { "X-Consumer-Department": "$department", "X-Consumer-Company": "$company", "X-Consumer-Role": "$role" } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Attach the `department` consumer label value in the `X-Consumer-Department` request header. ❷ Attach the `company` consumer label value in the `X-Consumer-Company` request header. ❸ Attach the `role` consumer label value in the `X-Consumer-Role` request header. As the `role` label is not configured on the consumer, it is expected that the header will not appear in the request forwarded to the upstream service. tip The consumer label references must be prefixed by a dollar sign (`$`). To verify, send a request to the route with the valid credential: curl -i "http://127.0.0.1:9080/get" -H 'apikey: john-key' You should see an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "headers": { "Accept": "*/*", "Apikey": "john-key", "Host": "127.0.0.1", "X-Consumer-Username": "john", "X-Credential-Identifier": "cred-john-key-auth", "X-Consumer-Company": "api7", "X-Consumer-Department": "devops", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66e5107c-5bb3e24f2de5baf733aec1cc", "X-Forwarded-Host": "127.0.0.1" }, "origin": "192.168.65.1, 205.198.122.37", "url": "http://127.0.0.1/get"} * [Example](https://docs.api7.ai/hub/attach-consumer-label/#example) * [Attach Consumer Labels](https://docs.api7.ai/hub/attach-consumer-label/#attach-consumer-labels) --- # Key Authentication | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/getting-started/key-authentication/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page An API gateway's primary role is to connect API consumers and providers. For security reasons, it should authenticate and authorize consumers before allowing them to access upstream resources. ![Key Authentication](https://static.api7.ai/uploads/2023/02/08/8mRaK3v1_consumer.png) APISIX has a flexible plugin extension system and a number of existing plugins for user authentication and authorization. For example: * [Key Authentication](https://docs.api7.ai/hub/key-auth) * [Basic Authentication](https://docs.api7.ai/hub/basic-auth) * [HMAC](https://docs.api7.ai/hub/hmac-auth) * [JSON Web Token (JWT) Authentication](https://docs.api7.ai/hub/jwt-auth) * [OpenID Connect](https://docs.api7.ai/hub/openid-connect) * [Keycloak Authorization](https://docs.api7.ai/hub/authz-keycloak) * [Casdoor Authorization](https://apisix.apache.org/docs/apisix/plugins/authz-casdoor/) * [Casbin Authorization](https://apisix.apache.org/docs/apisix/plugins/authz-casbin/) * [Open Policy Agent (OPA)](https://docs.api7.ai/hub/opa) * [Wolf RBAC](https://apisix.apache.org/docs/apisix/plugins/wolf-rbac/) * [Central Authentication Service (CAS)](https://apisix.apache.org/docs/apisix/plugins/cas-auth/) * [LDAP](https://apisix.apache.org/docs/apisix/plugins/ldap-auth/) * [Forward Authentication](https://docs.api7.ai/hub/forward-auth) In this tutorial, you will create a [consumer](https://docs.api7.ai/apisix/key-concepts/consumers) , configure its [credential](https://docs.api7.ai/apisix/key-concepts/consumers) with [key authentication](https://docs.api7.ai/hub/key-auth) , and learn how to enable and disable key authentication. Key Concepts[​](https://docs.api7.ai/apisix/getting-started/key-authentication/#key-concepts "Direct link to Key Concepts") ---------------------------------------------------------------------------------------------------------------------------- ### Consumer[​](https://docs.api7.ai/apisix/getting-started/key-authentication/#consumer "Direct link to Consumer") A _consumer_ is an application or a developer who consumes the API. In APISIX, a consumer requires a unique `username` to be created. As part of the key authentication configuration, you would also add one of the authentication plugins from the list above to the consumer's `plugin` field. ### Key Authentication[​](https://docs.api7.ai/apisix/getting-started/key-authentication/#key-authentication "Direct link to Key Authentication") Key authentication is a relatively simple but widely used authentication approach. The idea is as follows: 1. Administrator adds an authentication plugin to the route. 2. API consumers attach the key to the query string or headers for authentication when sending requests. Prerequisite(s)[​](https://docs.api7.ai/apisix/getting-started/key-authentication/#prerequisites "Direct link to Prerequisite(s)") ----------------------------------------------------------------------------------------------------------------------------------- 1. Complete [Get APISIX](https://docs.api7.ai/apisix/getting-started/) to install APISIX in Docker. 2. Complete [Configure Routes](https://docs.api7.ai/apisix/getting-started/configure-routes) . 3. Install [ADC](https://docs.api7.ai/apisix/reference/adc) or [APISIX-MCP](https://docs.api7.ai/apisix/reference/apisix-mcp) if you are using these tools. Configure Key Authentication[​](https://docs.api7.ai/apisix/getting-started/key-authentication/#configure-key-authentication "Direct link to Configure Key Authentication") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- * Admin API * ADC * APISIX-MCP ### Create a Consumer Create a consumer `tom`: caution Please use a complex key in the production environment. curl -i "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT -d '{ "username": "tom"}' You will receive an `HTTP/1.1 201 Created` response if the consumer was created successfully. ### Configure Consumer Credential Configure the consumer `key-auth` credential for `tom`: curl "http://127.0.0.1:9180/apisix/admin/consumers/tom/credentials" -X PUT -d '{ "id": "cred-tom-key-auth", "plugins": { "key-auth": { "key": "secret-key" } }}' You will receive an `HTTP/1.1 201 Created` response if the consumer credential was created. ### Enable Authentication Update the `getting-started-ip` route from [Configure Routes](https://docs.api7.ai/apisix/getting-started/configure-routes) to add the `key-auth` plugin: curl -i "http://127.0.0.1:9180/apisix/admin/routes/getting-started-ip" -X PATCH -d '{ "plugins": { "key-auth": {} }}' You will receive an `HTTP/1.1 200 OK` response if the route was updated successfully. Create an ADC configuration file containing a consumer and a route: adc.yaml consumers: - username: tom credentials: - name: tom-key type: key-auth config: key: secret-keyservices: - name: httpbin Service routes: - uris: - /ip name: getting-started-ip plugins: key-auth: {} upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to APISIX: adc sync -f adc.yaml Enter the following prompt in your AI client: Create a consumer named tom in APISIX and the key-auth credential for the consumer should be secret-key.Enable the key-auth plugin on the route getting-started-ip, requiring consumers to provide an API key when accessing that route. You should see a response similar to the following: Created consumer 'tom' with key-auth credential and enabled key-auth plugin on route 'getting-started-ip'. The consumer can now access the route using API key 'secret-key' in either the 'apikey' header or query parameter. Verify[​](https://docs.api7.ai/apisix/getting-started/key-authentication/#verify "Direct link to Verify") ---------------------------------------------------------------------------------------------------------- You will be verifying if the key authentication is successfully enabled in this section. * Admin API * ADC * APISIX-MCP ### Send a Request without Any Key Send a request without the `apikey` header. curl -i "http://127.0.0.1:9080/ip" Since the key is not provided, you will receive an unauthorized `HTTP/1.1 401 Unauthorized` response. ### Send a Request with a Wrong Key Send a request with a wrong key in the `apikey` header. curl -i "http://127.0.0.1:9080/ip" -H 'apikey: wrong-key' Since the key is incorrect, you will receive an `HTTP/1.1 401 Unauthorized` response. ### Send a Request with the Correct Key Send a request with the correct key in the `apikey` header. curl -i "http://127.0.0.1:9080/ip" -H 'apikey: secret-key' Since the correct key is provided, you will receive an `HTTP/1.1 200 OK` response. ### Send a Request without Any Key Send a request without the `apikey` header. curl -i "http://127.0.0.1:9080/ip" Since the key is not provided, you will receive an unauthorized `HTTP/1.1 401 Unauthorized` response. ### Send a Request with a Wrong Key Send a request with a wrong key in the `apikey` header. curl -i "http://127.0.0.1:9080/ip" -H 'apikey: wrong-key' Since the key is incorrect, you will receive an `HTTP/1.1 401 Unauthorized` response. ### Send a Request with the Correct Key Send a request with the correct key in the `apikey` header. curl -i "http://127.0.0.1:9080/ip" -H 'apikey: secret-key' Since the correct key is provided, you will receive an `HTTP/1.1 200 OK` response. ### Send a Request without Any Key Enter the following prompt in your AI client: Send a request to the route without any authentication header. You should see a response similar to the following: Unauthenticated request to '/ip' returned 401 as expected ### Send a Request with a Wrong Key Enter the following prompt in your AI client: Send a request to the route with wrong-key. You should see a response similar to the following: Invalid API key returns 401 (wrong credentials) ### Send a Request with the Correct Key Enter the following prompt in your AI client: Send a request to the route with secret-key. You should see a response similar to the following: Valid API key 'secret-key' authenticates successfully (200) Disable Authentication[​](https://docs.api7.ai/apisix/getting-started/key-authentication/#disable-authentication "Direct link to Disable Authentication") ---------------------------------------------------------------------------------------------------------------------------------------------------------- Disable the key authentication plugin by setting the `_meta.disable` parameter to `true`. * Admin API * ADC * APISIX-MCP curl "http://127.0.0.1:9180/apisix/admin/routes/getting-started-ip" -X PATCH -d '{ "plugins": { "key-auth": { "_meta": { "disable": true } } }}' Send a request without any key to verify: curl -i "http://127.0.0.1:9080/ip" Since key authentication is disabled, you will receive an `HTTP/1.1 200 OK` response. adc.yaml consumers: - username: tom plugins: key-auth: key: secret-keyservices: - name: httpbin Service routes: - uris: - /ip name: getting-started-ip plugins: key-auth: _meta: disable: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to APISIX: adc sync -f adc.yaml Send a request without any key to verify: curl -i "http://127.0.0.1:9080/ip" Since key authentication is disabled, you will receive an `HTTP/1.1 200 OK` response. Enter the following prompt in your AI client: Disable the key authentication plugin for the route getting-started-ip while keeping its configuration intact. You should see a response similar to the following: Successfully disabled the key-auth plugin for route 'getting-started-ip' while maintaining all other configuration. The route now:* Still matches requests to URI '/ip'* Forwards to upstream httpbin.org:80* No longer requires API key authenticationRetains all other settings including load balancing configuration Send a request without any key in your AI client: Send a request to the route without any key. You should see a response similar to the following: The request to the /ip route without an API key was successful (HTTP 200). What's Next[​](https://docs.api7.ai/apisix/getting-started/key-authentication/#whats-next "Direct link to What's Next") ------------------------------------------------------------------------------------------------------------------------ You have learned how to configure key authentication for a route. In the next tutorial, you will learn how to configure rate limiting. * [Key Concepts](https://docs.api7.ai/apisix/getting-started/key-authentication/#key-concepts) * [Consumer](https://docs.api7.ai/apisix/getting-started/key-authentication/#consumer) * [Key Authentication](https://docs.api7.ai/apisix/getting-started/key-authentication/#key-authentication) * [Prerequisite(s)](https://docs.api7.ai/apisix/getting-started/key-authentication/#prerequisites) * [Configure Key Authentication](https://docs.api7.ai/apisix/getting-started/key-authentication/#configure-key-authentication) * [Verify](https://docs.api7.ai/apisix/getting-started/key-authentication/#verify) * [Disable Authentication](https://docs.api7.ai/apisix/getting-started/key-authentication/#disable-authentication) * [What's Next](https://docs.api7.ai/apisix/getting-started/key-authentication/#whats-next) --- # Plugin Configs | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/plugin-configs/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of plugin configs in APISIX and why you may need them. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/plugin-configs/#overview "Direct link to Overview") --------------------------------------------------------------------------------------------------------- In APISIX, a _plugin config_ object extracts common plugin configurations, eliminating repetitive plugin configurations on [routes](https://docs.api7.ai/apisix/key-concepts/routes) . This makes API management more streamlined and efficient. To apply the configured [plugins](https://docs.api7.ai/apisix/key-concepts/plugins) on a plugin config object in routes, you can simply refer to the plugin config ID in route configurations. The following diagram illustrates the concept of plugin configs with two routes pointing to two different backend systems - one for orders and the other one for deliveries. A plugin config object is used in the APISIX configuration, such that `limit-count` and `proxy-rewrite` plugins are configured in a centralized place while being utilized by multiple routes: ![plugin configs diagram with two routes and one plugin config object](https://static.api7.ai/uploads/2023/03/22/0VcbwzBw_plugin_configs.svg) In addition to shared plugin configs, you can still configure route-specific plugins separately on routes. See [plugins configuration precedence](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-configuration-precedence) to learn more about how APISIX prioritizes configurations when the same plugin is configured in multiple objects. Additional Resource(s)[​](https://docs.api7.ai/apisix/key-concepts/plugin-configs/#additional-resources "Direct link to Additional Resource(s)") ------------------------------------------------------------------------------------------------------------------------------------------------- * Key Concepts - [Plugins](https://docs.api7.ai/apisix/key-concepts/plugins) * Admin API - [Plugin Configs](https://docs.api7.ai/apisix/reference/admin-api#tag/Plugin-Config) * [Plugin Hub](https://docs.api7.ai/hub) * [Overview](https://docs.api7.ai/apisix/key-concepts/plugin-configs/#overview) * [Additional Resource(s)](https://docs.api7.ai/apisix/key-concepts/plugin-configs/#additional-resources) --- # Services | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/services/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of _services_ in APISIX and the advantages of using services. Explore additional resources at the end for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/services/#overview "Direct link to Overview") --------------------------------------------------------------------------------------------------- A _service_ object in APISIX is an abstraction of a backend application providing logically related functionalities. The relationship between a service and routes is usually one-to-many (1:N). The following diagram illustrates an example of a service object used in architecting a data analytics (`da`) backend at Foodbar Company (a fictional company), where there are two routes with distinctive configurations - one for getting data ([HTTP GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) ) and the other one for uploading data ([HTTP POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) ): ![Services Diagram](https://static.api7.ai/uploads/2023/02/28/7Iudl0X8_services.svg) Note that the [rate-limiting](https://docs.api7.ai/apisix/getting-started/rate-limiting) plugin `limit-count` is configured once on the service object, regulating incoming client requests from the two routes. Similarly, the upstream address is also configured only once on the [upstream](https://docs.api7.ai/apisix/key-concepts/upstreams) object. While plugins and upstreams can also be configured in routes individually (and repetitively) to serve the same purpose, it is advised against adopting this approach, as things quickly become hard to manage when the system grows. Using upstreams and services help reduce the risk of data anomalies and minimize operational costs. For simplicity, the example above only pointed the traffic to one upstream node. You can add more upstream nodes, when needed, to [enable load balancing](https://docs.api7.ai/apisix/getting-started/load-balancing) , maintaining a smooth operation and response for users and avoiding a single point of failure in the architecture. Additional Resources[​](https://docs.api7.ai/apisix/key-concepts/services/#additional-resources "Direct link to Additional Resources") --------------------------------------------------------------------------------------------------------------------------------------- * Getting Started * [Configure Routes](https://docs.api7.ai/apisix/getting-started/configure-routes) * [Load Balancing](https://docs.api7.ai/apisix/getting-started/load-balancing) * Key Concepts * [Routes](https://docs.api7.ai/apisix/key-concepts/routes) * [Upstreams](https://docs.api7.ai/apisix/key-concepts/upstreams) * [Plugins](https://docs.api7.ai/apisix/key-concepts/plugins) * Admin API - [Service](https://docs.api7.ai/apisix/reference/admin-api#tag/Service) * [Overview](https://docs.api7.ai/apisix/key-concepts/services/#overview) * [Additional Resources](https://docs.api7.ai/apisix/key-concepts/services/#additional-resources) --- # Proxy Cache | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/proxy-cache/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `proxy-cache` plugin provides the capability to cache responses based on a cache key. The plugin supports both disk-based and memory-based caching options to cache for [GET](https://anything.org/learn/serving-over-http/#get-request) , [POST](https://anything.org/learn/serving-over-http/#post-request) , and [HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) requests. Responses can be conditionally cached based on request HTTP methods, response status codes, request header values, and more. Examples[​](https://docs.api7.ai/hub/proxy-cache/#examples "Direct link to Examples") -------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `proxy-cache` for different scenarios. ### Cache Data on Disk[​](https://docs.api7.ai/hub/proxy-cache/#cache-data-on-disk "Direct link to Cache Data on Disk") On-disk caching strategy offers the advantages of data persistency when system restarts and having larger storage capacity compared to in-memory cache. It is suitable for applications that prioritize durability and can tolerate slightly larger cache access latency. The following example demonstrates how you can use `proxy-cache` plugin on a route to cache data on disk. When using the on-disk caching strategy, the cache TTL is determined by value from the response header `Expires` or `Cache-Control`. If none of these headers is present or if APISIX returns `502 Bad Gateway` or `504 Gateway Timeout` due to unavailable upstreams, the cache TTL defaults to the value configured in the [configuration files](https://docs.api7.ai/hub/proxy-cache/configuration#static-configuration) . Create a route with the `proxy-cache` plugin to cache data on disk: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-cache-route", "uri": "/anything", "plugins": { "proxy-cache": { "cache_strategy": "disk" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should see an `HTTP/1.1 200 OK` response with the following header, showing the plugin is successfully enabled: Apisix-Cache-Status: MISS As there is no cache available before the first response, `Apisix-Cache-Status: MISS` is shown. Send the same request again within the cache TTL window. You should see an `HTTP/1.1 200 OK` response with the following headers, showing the cache is hit: Apisix-Cache-Status: HIT Wait for the cache to expire after the TTL and send the same request again. You should see an `HTTP/1.1 200 OK` response with the following headers, showing the cache has expired: Apisix-Cache-Status: EXPIRED ### Cache Data in Memory[​](https://docs.api7.ai/hub/proxy-cache/#cache-data-in-memory "Direct link to Cache Data in Memory") In-memory caching strategy offers the advantage of low-latency access to the cached data, as retrieving data from RAM is faster than retrieving data from disk storage. It also works well for storing temporary data that does not need to be persisted long-term, allowing for efficient caching of frequently changing data. The following example demonstrates how you can use `proxy-cache` plugin on a route to cache data in memory. Create a route with `proxy-cache` and configure it to use memory-based caching: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-cache-route", "uri": "/anything", "plugins": { "proxy-cache": { "cache_strategy": "memory", "cache_zone": "memory_cache", "cache_ttl": 10 } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ `cache_strategy`: set to `memory` for in-memory setting. ❷ `cache_zone`: set to the name of an in-memory cache zone. ❸ `cache_ttl`: set the time to live for the in-memory cache to 10 seconds. Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should see an `HTTP/1.1 200 OK` response with the following header, showing the plugin is successfully enabled: Apisix-Cache-Status: MISS As there is no cache available before the first response, `Apisix-Cache-Status: MISS` is shown. Send the same request again within the cache TTL window. You should see an `HTTP/1.1 200 OK` response with the following headers, showing the cache is hit: Apisix-Cache-Status: HIT ### Cache Responses Conditionally[​](https://docs.api7.ai/hub/proxy-cache/#cache-responses-conditionally "Direct link to Cache Responses Conditionally") The following example demonstrates how you can configure the `proxy-cache` plugin to conditionally cache responses. Create a route with the `proxy-cache` plugin and configure the `no_cache` attribute: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-cache-route", "uri": "/anything", "plugins": { "proxy-cache": { "no_cache": ["$arg_no_cache", "$http_no_cache"] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ `no_cache`: If at least one of the values of the URL parameter `no_cache` and header `no_cache` is not empty and is not equal to `0`, the response will not be cached. Send a few requests to the route with the URL parameter `no_cache` value indicating cache bypass: curl -i "http://127.0.0.1:9080/anything?no_cache=1" You should receive `HTTP/1.1 200 OK` responses for all requests and observe the following header every time: Apisix-Cache-Status: EXPIRED Send a few other requests to the route with the URL parameter `no_cache` value being zero: curl -i "http://127.0.0.1:9080/anything?no_cache=0" You should receive `HTTP/1.1 200 OK` responses for all requests and start seeing the cache being hit: Apisix-Cache-Status: HIT You can also specify the value in the `no_cache` header as such: curl -i "http://127.0.0.1:9080/anything" -H "no_cache: 1" The response should not be cached: Apisix-Cache-Status: EXPIRED ### Retrieve Responses from Cache Conditionally[​](https://docs.api7.ai/hub/proxy-cache/#retrieve-responses-from-cache-conditionally "Direct link to Retrieve Responses from Cache Conditionally") The following example demonstrates how you can configure the `proxy-cache` plugin to conditionally retrieve responses from cache. Create a route with the `proxy-cache` plugin and configure the `cache_bypass` attribute: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-cache-route", "uri": "/anything", "plugins": { "proxy-cache": { "cache_bypass": ["$arg_bypass", "$http_bypass"] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ `cache_bypass`: If at least one of the values of the URL parameter `bypass` and header `bypass` is not empty and is not equal to `0`, the response will not be retrieved from the cache. Send a request to the route with the URL parameter `bypass` value indicating cache bypass: curl -i "http://127.0.0.1:9080/anything?bypass=1" You should see an `HTTP/1.1 200 OK` response with the following header: Apisix-Cache-Status: BYPASS Send another request to the route with the URL parameter `bypass` value being zero: curl -i "http://127.0.0.1:9080/anything?bypass=0" You should see an `HTTP/1.1 200 OK` response with the following header: Apisix-Cache-Status: MISS You can also specify the value in the `bypass` header as such: curl -i "http://127.0.0.1:9080/anything" -H "bypass: 1" The cache should be bypassed: Apisix-Cache-Status: BYPASS ### Cache for 502 and 504 Error Response Code[​](https://docs.api7.ai/hub/proxy-cache/#cache-for-502-and-504-error-response-code "Direct link to Cache for 502 and 504 Error Response Code") When the upstream services return server errors in the 500 range, `proxy-cache` plugin will cache the responses if and only if the returned status is `502 Bad Gateway` or `504 Gateway Timeout`. The following example demonstrates the behavior of `proxy-cache` plugin when the upstream service returns `504 Gateway Timeout`. Create a route with the `proxy-cache` plugin and configure a dummy upstream service: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-cache-route", "uri": "/timeout", "plugins": { "proxy-cache": { } }, "upstream": { "type": "roundrobin", "nodes": { "12.34.56.78": 1 } } }' Generate a few requests to the route: seq 4 | xargs -I{} curl -I "http://127.0.0.1:9080/timeout" You should see a response similar to the following: HTTP/1.1 504 Gateway Time-out...Apisix-Cache-Status: MISSHTTP/1.1 504 Gateway Time-out...Apisix-Cache-Status: HITHTTP/1.1 504 Gateway Time-out...Apisix-Cache-Status: HITHTTP/1.1 504 Gateway Time-out...Apisix-Cache-Status: HIT However, if the upstream services returns `503 Service Temporarily Unavailable`, the response will not be cached. * [Examples](https://docs.api7.ai/hub/proxy-cache/#examples) * [Cache Data on Disk](https://docs.api7.ai/hub/proxy-cache/#cache-data-on-disk) * [Cache Data in Memory](https://docs.api7.ai/hub/proxy-cache/#cache-data-in-memory) * [Cache Responses Conditionally](https://docs.api7.ai/hub/proxy-cache/#cache-responses-conditionally) * [Retrieve Responses from Cache Conditionally](https://docs.api7.ai/hub/proxy-cache/#retrieve-responses-from-cache-conditionally) * [Cache for 502 and 504 Error Response Code](https://docs.api7.ai/hub/proxy-cache/#cache-for-502-and-504-error-response-code) --- # Fault Injection | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/fault-injection/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `fault-injection` plugin is designed to test your application’s resiliency by simulating controlled faults or delays. It executes before other configured plugins, ensuring that faults are applied consistently. This makes it ideal for scenarios like chaos engineering, where the behavior of your system under failure conditions is analyzed. The plugin supports two key actions: `abort`, which immediately terminates a request with a specified HTTP status code (e.g., `503 Service Unavailable`), skipping all subsequent plugins; and `delay`, which introduces a specified delay before processing the request further. These features allow you to simulate scenarios such as service outages or latency, helping you validate error-handling logic and improve system reliability. Examples[​](https://docs.api7.ai/hub/fault-injection/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------ The examples below demonstrate how you can configure the `fault-injection` plugin for different scenarios. ### Inject Faults[​](https://docs.api7.ai/hub/fault-injection/#inject-faults "Direct link to Inject Faults") The following example demonstrate how you can configure the `fault-injection` plugin on a route to intercept further request sending, and respond with a specific HTTP code. Create a route using the `fault-injection` plugin with the `abort` action to respond any request with `404` and the specified response body: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "fault-injection-route", "uri": "/anything", "plugins": { "fault-injection": { "abort": { "http_status": 404, "body": "APISIX Fault Injection" } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 404 Not Found` response and see the following response body, without the request being forwarded to the upstream service: APISIX Fault Injection ### Inject Latencies[​](https://docs.api7.ai/hub/fault-injection/#inject-latencies "Direct link to Inject Latencies") The following example demonstrate how you can configure the `fault-injection` plugin on a route to inject request latencies. Create a route using the `fault-injection` plugin with the `delay` action to delay response sending for 3 seconds: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "fault-injection-route", "uri": "/anything", "plugins": { "fault-injection": { "delay": { "duration": 3 } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Send a request to the route and use `time` command to summarize of how long the request took to complete: time curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response from the upstream service and see the following timing summary: 0.01s user0.01s system0% cpu3.685 total ### Inject Faults Conditionally[​](https://docs.api7.ai/hub/fault-injection/#inject-faults-conditionally "Direct link to Inject Faults Conditionally") The following example demonstrate how you can configure the `fault-injection` plugin on a route to intercept further request sending, and respond with a specific HTTP code. Create a route using the `fault-injection` plugin with the `abort` action as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "fault-injection-route", "uri": "/anything", "plugins": { "fault-injection": { "abort": { "http_status": 404, "body": "APISIX Fault Injection", "headers": { "X-APISIX-Remote-Addr": "$remote_addr" }, "vars": [ [ [ "arg_name","==","john" ] ] ] } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Respond requests with HTTP status code `404`. ❷ Respond requests with `APISIX Fault Injection` as the body. ❸ Respond requests with header `X-APISIX-Remote-Addr` and the IP the request originates from. ❹ Respond requests with the above specifications only if the URL parameter `name` value is `john`. Send a request to the route with the URL parameter `name` being `john`: curl -i "http://127.0.0.1:9080/anything?name=john" You should receive an `HTTP/1.1 404 Not Found` response: HTTP/1.1 404 Not Found...X-APISIX-Remote-Addr: 192.168.65.1APISIX Fault Injection Send a request to the route with the URL parameter `name` being a different value: curl -i "http://127.0.0.1:9080/anything?name=jane" You should receive an `HTTP/1.1 200 OK` response from the upstream service without the injections. * [Examples](https://docs.api7.ai/hub/fault-injection/#examples) * [Inject Faults](https://docs.api7.ai/hub/fault-injection/#inject-faults) * [Inject Latencies](https://docs.api7.ai/hub/fault-injection/#inject-latencies) * [Inject Faults Conditionally](https://docs.api7.ai/hub/fault-injection/#inject-faults-conditionally) --- # Traffic Label | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/traffic-label/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `traffic-label` plugin labels traffic based on user-defined rules and takes actions based on labels and the associated weights for actions. It provides a granular approach to traffic management, making it easy to conditionally action on requests with flexibility and precision. Examples[​](https://docs.api7.ai/hub/traffic-label/#examples "Direct link to Examples") ---------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `traffic-label` on a route in different scenarios. ### Define a Single Matching Condition[​](https://docs.api7.ai/hub/traffic-label/#define-a-single-matching-condition "Direct link to Define a Single Matching Condition") The following example demonstrates a simple rule with one matching condition and one associated action. If the URI of the request is `/headers`, the plugin will add the header `"X-Server-Id": "100"` to the request. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "traffic-label-route", "uri":"/headers", "plugins":{ "traffic-label": { "rules": [ { "match": [ ["uri", "==", "/headers"] ], "actions": [ { "set_headers": { "X-Server-Id": 100 } } ] } ] } }, "upstream":{ "type":"roundrobin", "nodes":{ "httpbin.org:80":1 } } }' Send a request to verify: curl "http://127.0.0.1:9080/headers" You should see a response similar to the following: { "headers": { "Accept": "*/*", ... "X-Server-Id": "100" }} ### Define Multiple Matching Conditions with Logical Operators[​](https://docs.api7.ai/hub/traffic-label/#define-multiple-matching-conditions-with-logical-operators "Direct link to Define Multiple Matching Conditions with Logical Operators") You can build more complex matching conditions with [logical operators](https://docs.api7.ai/apisix/reference/apisix-expressions#logical-operators) . The following example demonstrates a rule with two matching conditions logically grouped by `OR` and one associated action. If one of the conditions is met, the plugin will add the header `"X-Server-Id": "100"` to the request. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "traffic-label-route", "uri":"/headers", "plugins":{ "traffic-label": { "rules": [ { "match": [ "OR", ["arg_version", "==", "v1"], ["arg_env", "==", "dev"] ], "actions": [ { "set_headers": { "X-Server-Id": 100 } } ] } ] } }, "upstream":{ "type":"roundrobin", "nodes":{ "httpbin.org:80":1 } } }' Send a request to verify: curl "http://127.0.0.1:9080/headers?env=dev" You should see a response similar to the following: { "headers": { "Accept": "*/*", ... "X-Server-Id": "100" }} If you send a request that does not match any of the conditions, you will not see `"X-Server-Id": "100"` added to the request header. ### Create Weighted Actions[​](https://docs.api7.ai/hub/traffic-label/#create-weighted-actions "Direct link to Create Weighted Actions") The following example demonstrates a rule with one matching condition and multiple weighted actions, where incoming requests are distributed proportionally based on the weights. If a `weight` is not associated with any action, this portion of the requests will not have any action performed on them. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "traffic-label-route", "uri":"/headers", "plugins":{ "traffic-label": { "rules": [ { "match": [ ["uri", "==", "/headers"] ], "actions": [ { "set_headers": { "X-Server-Id": 100 }, "weight": 3 }, { "set_headers": { "X-API-Version": "v2" }, "weight": 2 }, { "weight": 5 } ] } ] } }, "upstream":{ "type":"roundrobin", "nodes":{ "httpbin.org:80":1 } } }' The proportion of times each action is executed is determined by the weight of the action relative to the total weight of all actions listed under the `actions` field. Here, the total weight is calculated as the sum of all action weights: 3 + 2 + 5 = 10. Therefore: ❶ 30% of the requests should have the `X-Server-Id: 100` request header. ❷ 20% of the requests should have the `X-API-Version: v2` request header. ❸ 50% of the requests should not have any action performed on them. Generate 50 consecutive requests to verify the weighted actions: resp=$(seq 50 | xargs -I{} curl "http://127.0.0.1:9080/headers" -sL) && \ count_w3=$(echo "$resp" | grep "X-Server-Id" | wc -l) && \ count_w2=$(echo "$resp" | grep "X-API-Version" | wc -l) && \ echo X-Server-Id: $count_w3, X-API-Version: $count_w2 The response shows that headers are added to requests in a weighted manner: X-Server-Id: 15, X-API-Version: 10 ### Define Multiple Matching Rules[​](https://docs.api7.ai/hub/traffic-label/#define-multiple-matching-rules "Direct link to Define Multiple Matching Rules") The following example demonstrates the use of multiple rules, each with their matching condition and action. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "traffic-label-route", "uri":"/headers", "plugins":{ "traffic-label": { "rules": [ { "match": [ ["arg_version", "==", "v1"] ], "actions": [ { "set_headers": { "X-Server-Id": 100 } } ] }, { "match": [ ["arg_version", "==", "v2"] ], "actions": [ { "set_headers": { "X-Server-Id": 200 } } ] } ] } }, "upstream":{ "type":"roundrobin", "nodes":{ "httpbin.org:80":1 } } }' Send a request to `/headers?version=v1` to verify: curl "http://127.0.0.1:9080/headers?version=v1" You should see a response similar to the following: { "headers": { "Accept": "*/*", ... "X-Server-Id": "100" }} Send a request to `/headers?version=v2` to verify: curl "http://127.0.0.1:9080/headers?version=v2" You should see a response similar to the following: { "headers": { "Accept": "*/*", ... "X-Server-Id": "200" }} * [Examples](https://docs.api7.ai/hub/traffic-label/#examples) * [Define a Single Matching Condition](https://docs.api7.ai/hub/traffic-label/#define-a-single-matching-condition) * [Define Multiple Matching Conditions with Logical Operators](https://docs.api7.ai/hub/traffic-label/#define-multiple-matching-conditions-with-logical-operators) * [Create Weighted Actions](https://docs.api7.ai/hub/traffic-label/#create-weighted-actions) * [Define Multiple Matching Rules](https://docs.api7.ai/hub/traffic-label/#define-multiple-matching-rules) --- # Exit transformer | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/exit-transformer/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `exit-transformer` plugin supports the customization of gateway responses based on the status codes, headers, and bodies returned from API7 plugins. When configured as a global plugin, it also supports the response customization when a route that does not exist is requested. The transformation logics are defined in the plugin using Lua functions, following the syntax: return (function(code, body, header) if {{ condition }} then return {{ modified_resp }} end return code, body, header end)(...) Examples[​](https://docs.api7.ai/hub/exit-transformer/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------- The examples below demonstrate how you can use `exit-transformer` for different scenarios. ### Modify 404 Route Not Found Response[​](https://docs.api7.ai/hub/exit-transformer/#modify-404-route-not-found-response "Direct link to Modify 404 Route Not Found Response") The following example demonstrates how you can use the plugin to update the `404 Not Found` response code and header when the route does not exist. In this case, the plugin needs to be configured as a global rule plugin. Create a global rule with the `exit-transformer` plugin, in which the function updates the response status code to `405` and add a custom `X-Custom-Header` header if the original status code was `404`: curl -i "http://127.0.0.1:9180/apisix/admin/global_rules" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "transform-404-not-found", "plugins": { "exit-transformer": { "functions": ["return (function(code, body, header) if code == 404 then header[\"X-Custom-Header\"] = \"Modified\" return 405, body, header end return code, body, header end)(...)"] } } }' Send a request to a route that does not exist: curl -i "http://127.0.0.1:9080/non-existent" You should receive an `HTTP/1.1 405 Not Allowed` response and observe the `X-Custom-Header: Modified` header. ### Modify 401 Unauthorized Response for Failed Authentication[​](https://docs.api7.ai/hub/exit-transformer/#modify-401-unauthorized-response-for-failed-authentication "Direct link to Modify 401 Unauthorized Response for Failed Authentication") The following example demonstrates how you can use the plugin to update the `401 Unauthorized` response when authentication fails. Create a route with the `exit-transformer` plugin, in which the function updates the response status code to `402` if the original status code was `401`; and enable `key-auth`: curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "transform-auth-route", "uri": "/get", "plugins": { "exit-transformer": { "functions": ["return (function(code, body, header) if code == 401 then return 402, body, header end return code, body, header end)(...)"] }, "key-auth":{} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create a consumer `john`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john" }' Configure `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Send a request to the route without the credential: curl -i "http://127.0.0.1:9080/get" You should receive an `HTTP/1.1 402 Payment Required` response for unauthorized access, where the response status code has been modified. ### Modify Response Conditionally on Request Headers[​](https://docs.api7.ai/hub/exit-transformer/#modify-response-conditionally-on-request-headers "Direct link to Modify Response Conditionally on Request Headers") The following example demonstrates how you can use the plugin to conditionally modify responses based on request headers. Create a route with the `exit-transformer` plugin, in which the function updates the response status code by `Content-Type` header. If the header value is `application/json` and the original status code is `404`, update the response status code to `405`. Print warning messages inside and outside the condition evaluation for demonstration purpose. curl -i "http://127.0.0.1:9180/apisix/admin/global_rules" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "transform-by-header-condition", "plugins": { "exit-transformer": { "functions": [ "return (function(code, body, header, ctx) local core = require(\"apisix.core\") local ct = core.request.headers(ctx)[\"Content-Type\"] core.log.warn(\"exit transformer logics running outside the condition\") if ct == \"application/json\" and code == 404 then core.log.warn(\"exit transformer logics running inside the condition\") return 405 end return code, body, header end) (...)" ] } } }' Send a request to a route that does not exist, without any header: curl -i "http://127.0.0.1:9080/non-existent" You should receive an `HTTP/1.1 404 Not Found` response and see the following message in the log: exit transformer logics running outside the condition Send a request to the non-existent route with the JSON `Content-Type` header: curl -i "http://127.0.0.1:9080/non-existent" -H "Content-Type: application/json" You should receive an `HTTP/1.1 405 Not Allowed` response, where the response status code has been modified, and see the following message in the log: exit transformer logics running outside the conditionexit transformer logics running inside the condition * [Examples](https://docs.api7.ai/hub/exit-transformer/#examples) * [Modify 404 Route Not Found Response](https://docs.api7.ai/hub/exit-transformer/#modify-404-route-not-found-response) * [Modify 401 Unauthorized Response for Failed Authentication](https://docs.api7.ai/hub/exit-transformer/#modify-401-unauthorized-response-for-failed-authentication) * [Modify Response Conditionally on Request Headers](https://docs.api7.ai/hub/exit-transformer/#modify-response-conditionally-on-request-headers) --- # Multi Auth | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/multi-auth/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `multi-auth` plugin allows consumers using different authentication methods to share the same route or service. It supports the configuration of multiple authentication plugins, so that a request would be allowed through if it authenticates successfully against any configured authentication method. Examples[​](https://docs.api7.ai/hub/multi-auth/#examples "Direct link to Examples") ------------------------------------------------------------------------------------- ### Allow Different Authentications on the Same Route[​](https://docs.api7.ai/hub/multi-auth/#allow-different-authentications-on-the-same-route "Direct link to Allow Different Authentications on the Same Route") The following example demonstrates how to have one consumer using basic authentication, while another consumer using key authentication, both sharing the same route. Create two consumers: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username":"consumer1" }' curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username":"consumer2" }' Configure basic authentication credential for `consumer1`: curl "http://127.0.0.1:9180/apisix/admin/consumers/consumer1/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-key-auth", "plugins": { "basic-auth": { "username":"consumer1", "password":"consumer1_pwd" } } }' Configure key authentication credential for `consumer2`: curl "http://127.0.0.1:9180/apisix/admin/consumers/consumer2/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-key-auth", "plugins": { "key-auth": { "key":"consumer2_pwd" } } }' Create a route with `multi-auth` and configure the two authentication plugins that consumers use: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "multi-auth-route", "uri": "/anything", "plugins": { "multi-auth":{ "auth_plugins":[ { "basic-auth":{} }, { "key-auth":{ "hide_credentials":true, "header":"apikey", "query":"apikey" } } ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Send a request to the route with `consumer1` basic authentication credentials: curl -i "http://127.0.0.1:9080/anything" -u consumer1:consumer1_pwd You should receive an `HTTP/1.1 200 OK` response. Send another request to the route with `consumer2` key authentication credential: curl -i "http://127.0.0.1:9080/anything" -H 'apikey: consumer2_pwd' You should again receive an `HTTP/1.1 200 OK` response. Send a request to the route without any credential: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 401 Unauthorized` response. This shows that consumers using different authentication methods are able to authenticate and access the resource behind the same route. * [Examples](https://docs.api7.ai/hub/multi-auth/#examples) * [Allow Different Authentications on the Same Route](https://docs.api7.ai/hub/multi-auth/#allow-different-authentications-on-the-same-route) --- # Response Rewrite | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/response-rewrite/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `response-rewrite` plugin offers options to rewrite responses that APISIX and its upstream services return to clients. With the plugin, you can modify HTTP status codes, request headers, response body, and more. For instance, you can use this plugin to: * Support [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) by setting `Access-Control-Allow-*` headers. * Indicate redirection by setting HTTP status codes and `Location` header. Examples[​](https://docs.api7.ai/hub/response-rewrite/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `response-rewrite` on a route in different scenarios. ### Rewrite Header and Body[​](https://docs.api7.ai/hub/response-rewrite/#rewrite-header-and-body "Direct link to Rewrite Header and Body") The following example demonstrates how to add response body and headers, only to responses with `200` HTTP status codes. Create a route with the `response-rewrite` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "response-rewrite-route", "methods": ["GET"], "uri": "/headers", "plugins": { "response-rewrite": { "body": "{\"code\":\"ok\",\"message\":\"new json body\"}", "headers": { "set": { "X-Server-id": 3, "X-Server-status": "on", "X-Server-balancer-addr": "$balancer_ip:$balancer_port" } }, "vars": [ [ "status","==",200 ] ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to verify: curl -i "http://127.0.0.1:9080/headers" You should receive a `HTTP/1.1 200 OK` response similar to the following: ...X-Server-id: 3X-Server-status: onX-Server-balancer-addr: 50.237.103.220:80{"code":"ok","message":"new json body"} ### Rewrite Header With RegEx Filter[​](https://docs.api7.ai/hub/response-rewrite/#rewrite-header-with-regex-filter "Direct link to Rewrite Header With RegEx Filter") The following example demonstrates how to use RegEx filter matching to replace `X-Amzn-Trace-Id` for responses. Create a route with the `response-rewrite` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "response-rewrite-route", "methods": ["GET"], "uri": "/headers", "plugins":{ "response-rewrite":{ "filters":[ { "regex":"X-Amzn-Trace-Id", "scope":"global", "replace":"X-Amzn-Trace-Id-Replace" } ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to verify: curl -i "http://127.0.0.1:9080/headers" You should see a response similar to the following: { "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.2.1", "X-Amzn-Trace-Id-Replace": "Root=1-6500095d-1041b05e2ba9c6b37232dbc7", "X-Forwarded-Host": "127.0.0.1" }} ### Decode Body from Base64[​](https://docs.api7.ai/hub/response-rewrite/#decode-body-from-base64 "Direct link to Decode Body from Base64") The following example demonstrates how to Decode Body from Base64 format. Create a route with the `response-rewrite` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "response-rewrite-route", "methods": ["GET"], "uri": "/get", "plugins":{ "response-rewrite": { "body": "SGVsbG8gV29ybGQ=", "body_base64": true } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to verify: curl "http://127.0.0.1:9080/get" You should see a response of the following: Hello World ### Rewrite Response and Its Connection with Execution Phases[​](https://docs.api7.ai/hub/response-rewrite/#rewrite-response-and-its-connection-with-execution-phases "Direct link to Rewrite Response and Its Connection with Execution Phases") The following example demonstrates the connection between the `response-rewrite` plugin and [execution phases](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) by configuring the plugin with the `key-auth` plugin, and see how the response is still rewritten to `200 OK` in the case of an unauthenticated request. Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-key-auth", "plugins": { "key-auth": { "key": "jack-key" } } }' Create a route with `key-auth` and configure `response-rewrite` to rewrite the response status code and body: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "response-rewrite-route", "uri": "/get", "plugins": { "key-auth": {}, "response-rewrite": { "status_code": 200, "body": "{\"code\": 200, \"msg\": \"success\"}" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to the route with the valid key: curl -i "http://127.0.0.1:9080/get" -H 'apikey: jack-key' You should receive an `HTTP/1.1 200 OK` response of the following: {"code": 200, "msg": "success"} Send a request to the route without any key: curl -i "http://127.0.0.1:9080/get" You should still receive an `HTTP/1.1 200 OK` response of the same, instead of `HTTP/1.1 401 Unauthorized` from the `key-auth` plugin. This shows that the `response-rewrite` plugin still rewrites the response. This is because **header\_filter** and **body\_filter** phase logics of the `response-rewrite` plugin will continue to run after [`ngx.exit`](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/#ngxexit) in the **access** or **rewrite** phases from other plugins. The following table summarizes the impact of `ngx.exit` on execution phases. | Phase | rewrite | access | header\_filter | body\_filter | | --- | --- | --- | --- | --- | | **rewrite** | ngx.exit | | | | | **access** | × | ngx.exit | | | | **header\_filter** | ✓ | ✓ | ngx.exit | | | **body\_filter** | ✓ | ✓ | × | ngx.exit | For example, if `ngx.exit` takes places in the **rewrite** phase, it will interrupt the execution of **access** phase but not interfere with **header\_filter** and **body\_filter** phases. * [Examples](https://docs.api7.ai/hub/response-rewrite/#examples) * [Rewrite Header and Body](https://docs.api7.ai/hub/response-rewrite/#rewrite-header-and-body) * [Rewrite Header With RegEx Filter](https://docs.api7.ai/hub/response-rewrite/#rewrite-header-with-regex-filter) * [Decode Body from Base64](https://docs.api7.ai/hub/response-rewrite/#decode-body-from-base64) * [Rewrite Response and Its Connection with Execution Phases](https://docs.api7.ai/hub/response-rewrite/#rewrite-response-and-its-connection-with-execution-phases) --- # Request ID | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/request-id/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `request-id` plugin assigns a unique ID to each request proxied through the gateway, which can be used for request tracking and debugging. If a request already includes an ID in the header specified by `header_name`, the plugin uses that value instead of generating a new one. The request ID is included in gateway logs by default. When the plugin is enabled, it is also added to the response header. Examples[​](https://docs.api7.ai/hub/request-id/#examples "Direct link to Examples") ------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `request-id` in different scenarios. ### Understand Request ID in Gateway Logs[​](https://docs.api7.ai/hub/request-id/#understand-request-id-in-gateway-logs "Direct link to Understand Request ID in Gateway Logs") The request ID is included in both access and error logs in APISIX from version 3.15.0 and in API7 Enterprise from version 3.3.0, regardless of whether the plugin is enabled. * When the plugin is disabled, the request ID defaults to Nginx's built-in `$request_id`. * When the plugin is enabled, the request ID is set to the unique ID generated by the plugin. This ensures request tracing is always available, with enhanced functionality when the plugin is enabled. The following example demonstrates how the request ID appears in the gateway logs when the plugin is disabled and when it is enabled. Create a route without the `request-id` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-id-route", "uri": "/anything", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. In the gateway log, you should see an entry similar to the following, where the last value is the request ID from Nginx's built-in `$request_id`: 192.168.215.1 - - [30/Jan/2026:07:21:31 +0000] localhost:9080 "GET /anything HTTP/1.1" 200 391 1.657 "-" "curl/8.6.0" 3.210.41.225:80 200 1.608 "http://localhost:9080" "8a14012e5d0414aff4f15f04b0bd8cb9" Update the route with the `request-id` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-id-route", "uri": "/anything", "plugins": { "request-id": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. In the gateway log, you should see an entry similar to the following, where the last value is the request ID generated by the plugin: 192.168.215.1 - - [30/Jan/2026:07:36:24 +0000] localhost:9080 "GET /anything HTTP/1.1" 200 391 0.685 "-" "curl/8.6.0" 52.20.30.6:80 200 0.653 "http://localhost:9080" "8c0ac818-f9d6-4160-be60-8fc74e76be73" ### Attach Request ID to Default Response Header[​](https://docs.api7.ai/hub/request-id/#attach-request-id-to-default-response-header "Direct link to Attach Request ID to Default Response Header") The following example demonstrates how to configure `request-id` on a route which attaches a generated request ID to the default `X-Request-Id` response header, if the header value is not passed in the request. When the `X-Request-Id` header is set in the request, the plugin will take the value in the request header as the request ID. Create a route with the `request-id` plugin using its default configurations (explicitly defined): curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-id-route", "uri": "/anything", "plugins": { "request-id": { "header_name": "X-Request-Id", "include_in_response": true, "algorithm": "uuid" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response and see the response includes the `X-Request-Id` header with a generated ID: X-Request-Id: b9b2c0d4-d058-46fa-bafc-dd91a0ccf441 Send a request to the route with a custom request ID in the header: curl -i "http://127.0.0.1:9080/anything" -H 'X-Request-Id: some-custom-request-id' You should receive an `HTTP/1.1 200 OK` response and see the response includes the `X-Request-Id` header with the custom request ID: X-Request-Id: some-custom-request-id ### Attach Request ID to Custom Response Header[​](https://docs.api7.ai/hub/request-id/#attach-request-id-to-custom-response-header "Direct link to Attach Request ID to Custom Response Header") The following example demonstrates how to configure `request-id` on a route which attaches a generated request ID to a specified header. Create a route with the `request-id` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-id-route", "uri": "/anything", "plugins": { "request-id": { "header_name": "X-Req-Identifier", "include_in_response": true } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Define a custom header that carries the request ID. ❷ Include the request ID in the response header. Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response and see the response includes the `X-Req-Identifier` header with a generated ID: X-Req-Identifier: 1c42ff59-ee4c-4103-a980-8359f4135b21 ### Hide Request ID in Response Header[​](https://docs.api7.ai/hub/request-id/#hide-request-id-in-response-header "Direct link to Hide Request ID in Response Header") The following example demonstrates how to configure `request-id` on a route which attaches a generated request ID to a specified header. The header containing the request ID should be forwarded to the upstream service but not returned in the response header. Create a route with the `request-id` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-id-route", "uri": "/anything", "plugins": { "request-id": { "header_name": "X-Req-Identifier", "include_in_response": false } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Define a custom header that carries the request ID. ❷ Do not include the request ID in the response header. Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response not and see `X-Req-Identifier` header among the response headers. In the response body, you should see: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-6752748c-7d364f48564508db1e8c9ea8", "X-Forwarded-Host": "127.0.0.1", "X-Req-Identifier": "268092bc-15e1-4461-b277-bf7775f2856f" }, ...} This shows the request ID is forwarded to the upstream service but not returned in the response header. ### Use `nanoid` Algorithm[​](https://docs.api7.ai/hub/request-id/#use-nanoid-algorithm "Direct link to use-nanoid-algorithm") The following example demonstrates how to configure `request-id` on a route and use the `nanoid` algorithm to generate the request ID. Create a route with the `request-id` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-id-route", "uri": "/anything", "plugins": { "request-id": { "algorithm": "nanoid" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response and see the response includes the `X-Req-Identifier` header with an ID generated using the `nanoid` algorithm: X-Request-Id: kepgHWCH2ycQ6JknQKrX2 ### Attach Request ID Globally and on a Route[​](https://docs.api7.ai/hub/request-id/#attach-request-id-globally-and-on-a-route "Direct link to Attach Request ID Globally and on a Route") The following example demonstrates how to configure `request-id` as a global plugin and on a route to attach two IDs. Create a global rule for the `request-id` plugin which adds request ID to a custom header: curl -i "http://127.0.0.1:9180/apisix/admin/global_rules" -X PUT -d '{ "id": "rule-for-request-id", "plugins": { "request-id": { "header_name": "Global-Request-ID" } }}' Create a route with the `request-id` plugin which adds request ID to a different custom header: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-id-route", "uri": "/anything", "plugins": { "request-id": { "header_name": "Route-Request-ID" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response and see the response includes the following headers: Global-Request-ID: 2e9b99c1-08ed-4a74-b347-49c0891b07adRoute-Request-ID: d755666b-732c-4f0e-a30e-a7a71ace4e26 * [Examples](https://docs.api7.ai/hub/request-id/#examples) * [Understand Request ID in Gateway Logs](https://docs.api7.ai/hub/request-id/#understand-request-id-in-gateway-logs) * [Attach Request ID to Default Response Header](https://docs.api7.ai/hub/request-id/#attach-request-id-to-default-response-header) * [Attach Request ID to Custom Response Header](https://docs.api7.ai/hub/request-id/#attach-request-id-to-custom-response-header) * [Hide Request ID in Response Header](https://docs.api7.ai/hub/request-id/#hide-request-id-in-response-header) * [Use `nanoid` Algorithm](https://docs.api7.ai/hub/request-id/#use-nanoid-algorithm) * [Attach Request ID Globally and on a Route](https://docs.api7.ai/hub/request-id/#attach-request-id-globally-and-on-a-route) --- # degraphql | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/degraphql/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `degraphql` plugin supports communicating with upstream GraphQL services over regular HTTP requests by mapping GraphQL queries to HTTP endpoints. Examples[​](https://docs.api7.ai/hub/degraphql/#examples "Direct link to Examples") ------------------------------------------------------------------------------------ The examples below use [Pokemon GraphQL API](https://graphql-pokemon.js.org/) as the upstream GraphQL server and demonstrate how you can configure `degraphql` to transform different types of GraphQL queries. ### Transform a Basic Query[​](https://docs.api7.ai/hub/degraphql/#transform-a-basic-query "Direct link to Transform a Basic Query") The following example demonstrates how you can transform a simple query below: query { getAllPokemon { key color }} Create a route with the `degraphql` plugin as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "degraphql-route", "methods": ["POST"], "uri": "/v7", "upstream": { "type": "roundrobin", "nodes": { "graphqlpokemon.favware.tech": 1 }, "scheme": "https", "pass_host": "node" }, "plugins": { "degraphql": { "query": "{\n getAllPokemon {\n key\n color\n }\n}" } } }' Send a request to the route to verify: curl "http://127.0.0.1:9080/v7" -X POST You should see a response similar to the following: { "data": { "getAllPokemon": [ { "key": "pokestarsmeargle", "color": "White" }, { "key": "pokestarufo", "color": "White" }, { "key": "pokestarufo2", "color": "White" }, ... { "key": "walkingwake", "color": "Blue" }, { "key": "ironleaves", "color": "Green" } ] }} ### Transform a Query with Variables[​](https://docs.api7.ai/hub/degraphql/#transform-a-query-with-variables "Direct link to Transform a Query with Variables") The following example demonstrates how you can transform the query below, with a variable: query ($pokemon: PokemonEnum!) { getPokemon( pokemon: $pokemon ) { color species }}variable:{ "pokemon": "pikachu"} Create a route with the `degraphql` plugin as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "degraphql-route", "uri": "/v7", "upstream": { "type": "roundrobin", "nodes": { "graphqlpokemon.favware.tech": 1 }, "scheme": "https", "pass_host": "node" }, "plugins": { "degraphql": { "query": "query ($pokemon: PokemonEnum!) {\n getPokemon(\n pokemon: $pokemon\n ) {\n color\n species\n }\n}\n", "variables": ["pokemon"] } } }' Send a request to the route to verify: curl "http://127.0.0.1:9080/v7" -X POST \ -d '{ "pokemon": "pikachu" }' You should see a response similar to the following: { "data": { "getPokemon": { "color": "Yellow", "species": "pikachu" } }} Alternatively, you can also pass the variable in the URL query string of a GET request: curl "http://127.0.0.1:9080/v7?pokemon=pikachu" -H "x-apollo-operation-name: GET" You should see the same response as the previous. * [Examples](https://docs.api7.ai/hub/degraphql/#examples) * [Transform a Basic Query](https://docs.api7.ai/hub/degraphql/#transform-a-basic-query) * [Transform a Query with Variables](https://docs.api7.ai/hub/degraphql/#transform-a-query-with-variables) --- # Configuration Files | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/configuration-files/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page APISIX has the following configuration files under `/conf`: * `config.yaml` * `config.yaml.example` * `apisix.yaml` * `debug.yaml` In addition, you can place `apisix.json` under `/conf` if you wish to work with JSON in the [file-driven standalone mode](https://docs.api7.ai/apisix/production/deployment-modes#file-driven) . This document provides a reference for how configuration files are used and how to manage configuration files by environments. Usage[​](https://docs.api7.ai/apisix/reference/configuration-files/#usage "Direct link to Usage") -------------------------------------------------------------------------------------------------- ### `config.yaml` and `config.yaml.example`[​](https://docs.api7.ai/apisix/reference/configuration-files/#configyaml-and-configyamlexample "Direct link to configyaml-and-configyamlexample") APISIX comes with a configuration file `config.yaml`, which is used to customize a number of parameters, including the listening interface, deployment mode, plugin attributes, and more. The default values for these parameters can be found in [`apisix/cli/config.lua`](https://github.com/apache/apisix/blob/master/apisix/cli/config.lua) . You may find the sample configuration file for `config.yaml` at [`config.yaml.example`](https://github.com/apache/apisix/blob/master/conf/config.yaml.example) : apisix: # node_listen: 9080 # APISIX listening port (single) node_listen: # APISIX listening ports (multiple) - 9080 # - port: 9081 # enable_http2: true # If not set, the default value is `false`. # - ip: 127.0.0.2 # port: 9082 # enable_http2: true enable_admin: true enable_dev_mode: false enable_reuseport: true ... Configurations in `config.yaml` is loaded once at startup. If you make any updates to this file, [reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. ### `apisix.yaml`[​](https://docs.api7.ai/apisix/reference/configuration-files/#apisixyaml "Direct link to apisixyaml") In APISIX file-driven standalone deployment mode, `apisix.yaml` is used to configure APISIX resources, such as [routes](https://docs.api7.ai/apisix/key-concepts/routes) , [upstreams](https://docs.api7.ai/apisix/key-concepts/upstreams) , [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) , and others. These configurations are loaded by APISIX into memory at startup. Changes to this file do not require a reload of APISIX as the file is monitored for changes at a regular interval. For more information about how to configure `apisix.yaml`, see [File-Driven Standalone Mode](https://docs.api7.ai/apisix/reference/file-standalone-configurations) . ### `apisix.json`[​](https://docs.api7.ai/apisix/reference/configuration-files/#apisixjson "Direct link to apisixjson") `apisix.json` is `apisix.yaml`'s JSON-equivalent in the file-driven standalone deployment mode to configure APISIX resources. For more information about how to configure `apisix.json`, see [File-Driven Standalone Mode](https://docs.api7.ai/apisix/reference/file-standalone-configurations) . ### `debug.yaml`[​](https://docs.api7.ai/apisix/reference/configuration-files/#debugyaml "Direct link to debugyaml") You can enable and customize APISIX debug mode using configuration options in `debug.yaml`. Changes to this file do not require a reload of APISIX as the file is monitored for changes at a regular interval. To learn more, see [Use Debug Mode](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode) . Manage Configuration Files by Environments[​](https://docs.api7.ai/apisix/reference/configuration-files/#manage-configuration-files-by-environments "Direct link to Manage Configuration Files by Environments") ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Keeping configuration files separate for different environments, such as development, staging, and production, can provide several benefits, including increased flexibility, improved security, and easier maintenance. APISIX supports separation of configuration files by environment. You can set the `APISIX_PROFILE` environment variable to differentiate which set of other configuration files APISIX should use. By default, when `APISIX_PROFILE` is not set, APISIX looks for the following configuration files: * `conf/config.yaml` * `conf/apisix.yaml` * `conf/debug.yaml` If the value of `APISIX_PROFILE` is set to `prod`, APISIX looks for the following configuration files: * `conf/config-prod.yaml` * `conf/apisix-prod.yaml` * `conf/debug-prod.yaml` You can set `APISIX_PROFILE` to any other value that matches your environment. Use Environment Variables in Configuration Files[​](https://docs.api7.ai/apisix/reference/configuration-files/#use-environment-variables-in-configuration-files "Direct link to Use Environment Variables in Configuration Files") ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In APISIX, you can use environment variables in the [`config.yaml`](https://docs.api7.ai/apisix/reference/configuration-files/#configyaml-and-configyamlexample) , [`apisix.yaml`](https://docs.api7.ai/apisix/reference/configuration-files/#apisixyaml) , or [`apisix.json`](https://docs.api7.ai/apisix/reference/configuration-files/#apisixjson) configuration files for values that should be configurable during deployments, using the `${{ENV_VAR}}` or `${{ENV_VAR:=default_value}}` syntax. ### Examples[​](https://docs.api7.ai/apisix/reference/configuration-files/#examples "Direct link to Examples") If you are running APISIX locally (outside Docker), you could use the export command to set the environment variable: export YOUR_VARIABLE=value If you are running APISIX in Docker, you should set the environment variable using the `-e` flag when starting the container. #### Use Environment Variables in `config.yaml`[​](https://docs.api7.ai/apisix/reference/configuration-files/#use-environment-variables-in-configyaml "Direct link to use-environment-variables-in-configyaml") The example below sets the listening ports of client requests and Admin API in environment variables. For instance, set `APISIX_NODE_LISTEN:8132` and `ADMIN_API_PORT:9232` in environment variables. In `config.yaml`, you can reference the environment variables as follows: config.yaml apisix: node_listen: - ${{APISIX_NODE_LISTEN}}deployment: admin: admin_listen: port: ${{ADMIN_API_PORT}} After being started, APISIX will listen on port `8132` for client requests and port `9232` for Admin API requests. #### Use Environment Variables in `apisix.yaml`[​](https://docs.api7.ai/apisix/reference/configuration-files/#use-environment-variables-in-apisixyaml "Direct link to use-environment-variables-in-apisixyaml") The example below sets the upstream node address of a route in an environment variable. For instance, set `UPSTREAM_ADDR:httpbin.org` in an environment variable. In `apisix.yaml`, you can reference the environment variable as follows: apisix.yaml routes: - uri: /ip upstream: nodes: "${{UPSTREAM_ADDR}}": 1 type: roundrobin In standalone mode, APISIX will hot reload the configurations and start proxying requests to this route to `httpbin.org`. #### Use Environment Variables in `apisix.json`[​](https://docs.api7.ai/apisix/reference/configuration-files/#use-environment-variables-in-apisixjson "Direct link to use-environment-variables-in-apisixjson") The example below sets the upstream node address of a route in an environment variable. For instance, set `UPSTREAM_ADDR:httpbin.org` in an environment variable. In `apisix.json`, you can reference the environment variable as follows: apisix.json { "routes": [ { "uri": "/ip", "upstream": { "nodes": { "${{UPSTREAM_ADDR}}": 1 }, "type": "roundrobin" } } ]} In standalone mode, APISIX will hot reload the configurations and start proxying requests to this route to `httpbin.org`. #### Set a Fallback Value[​](https://docs.api7.ai/apisix/reference/configuration-files/#set-a-fallback-value "Direct link to Set a Fallback Value") You can also configure default values to fall back to if no environment variables are set, for example: config.yaml apisix: node_listen: - ${{APISIX_NODE_LISTEN:=9080}}deployment: admin: admin_listen: port: ${{ADMIN_API_PORT:=9180}} If APISIX cannot resolve values for `APISIX_NODE_LISTEN` and `ADMIN_API_PORT` in the environment, it will default to listen on port `9080` for client requests and port `9180` for Admin API requests. * [Usage](https://docs.api7.ai/apisix/reference/configuration-files/#usage) * [`config.yaml` and `config.yaml.example`](https://docs.api7.ai/apisix/reference/configuration-files/#configyaml-and-configyamlexample) * [`apisix.yaml`](https://docs.api7.ai/apisix/reference/configuration-files/#apisixyaml) * [`apisix.json`](https://docs.api7.ai/apisix/reference/configuration-files/#apisixjson) * [`debug.yaml`](https://docs.api7.ai/apisix/reference/configuration-files/#debugyaml) * [Manage Configuration Files by Environments](https://docs.api7.ai/apisix/reference/configuration-files/#manage-configuration-files-by-environments) * [Use Environment Variables in Configuration Files](https://docs.api7.ai/apisix/reference/configuration-files/#use-environment-variables-in-configuration-files) * [Examples](https://docs.api7.ai/apisix/reference/configuration-files/#examples) --- # APISIX CLI | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/apisix-cli/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page The APISIX CLI (Command Line Interface) is a tool that allows you to start, stop, and manage your APISIX instances. apisix [action] Commands[​](https://docs.api7.ai/apisix/reference/apisix-cli/#commands "Direct link to Commands") -------------------------------------------------------------------------------------------------- ### `apisix help`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-help "Direct link to apisix-help") Print the APISIX CLI help menu. ### `apisix init`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-init "Direct link to apisix-init") Initialize the `nginx.conf` configuration. ### `apisix init_etcd`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-init_etcd "Direct link to apisix-init_etcd") Initialize data in etcd. ### `apisix start`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-start "Direct link to apisix-start") Initialize and start the APISIX instance. **Options:** * `-h` or `--help`: print help menu. * `-c` or `--config`: specify the path to the custom [configuration file](https://docs.api7.ai/apisix/reference/configuration-files) when starting APISIX. For example: apisix start -c /path/to/custom_config.yaml APISIX will fall back to the default configuration if `-c` is not set. * `--verbose`: show `init_etcd` debug information. ### `apisix stop`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-stop "Direct link to apisix-stop") Stop the running APISIX instance immediately. APISIX will stop all worker processes without waiting for them to finish serving any outstanding requests. ### `apisix quit`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-quit "Direct link to apisix-quit") Quit the running APISIX instance gracefully. APISIX will wait for all worker processes to finish serving any outstanding requests before stopping. ### `apisix restart`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-restart "Direct link to apisix-restart") Restart the APISIX instance. This command checks the generated `nginx.conf` configuration first before stopping and restarting APISIX. ### `apisix reload`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-reload "Direct link to apisix-reload") Reload the APISIX instance. Reinitialize `nginx.conf` and apply configuration changes without interrupting existing connections. ### `apisix test`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-test "Direct link to apisix-test") Test the generated `nginx.conf` to validate the configuration. ### `apisix version`[​](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-version "Direct link to apisix-version") Print APISIX version. * [Commands](https://docs.api7.ai/apisix/reference/apisix-cli/#commands) * [`apisix help`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-help) * [`apisix init`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-init) * [`apisix init_etcd`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-init_etcd) * [`apisix start`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-start) * [`apisix stop`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-stop) * [`apisix quit`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-quit) * [`apisix restart`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-restart) * [`apisix reload`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-reload) * [`apisix test`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-test) * [`apisix version`](https://docs.api7.ai/apisix/reference/apisix-cli/#apisix-version) --- # Environment Variables | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/environment-variables/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page APISIX supports the use of environment variables in static [configuration files](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) and certain plugins. There are a few environment variables reserved for special purposes, and others that can be created with custom names and referenced. Reserved Environment Variables[​](https://docs.api7.ai/apisix/reference/environment-variables/#reserved-environment-variables "Direct link to Reserved Environment Variables") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- APISIX currently reserves the following environment variables: | Variable Name | Description | | --- | --- | | `APISIX_DEPLOYMENT_ETCD_HOST` | etcd host address. | | `APISIX_PROFILE` | Deployment environment differentiating the [configuration files](https://docs.api7.ai/apisix/reference/configuration-files#manage-configuration-files-by-environments)
. | | `APISIX_WORKER_PROCESSES` | Number of worker processes. | To use these configurations, assign values to the environment variables before starting APISIX. Custom Environment Variables[​](https://docs.api7.ai/apisix/reference/environment-variables/#custom-environment-variables "Direct link to Custom Environment Variables") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- You can use custom environment variables in configuration files and for certain plugins. ### Configuration Files[​](https://docs.api7.ai/apisix/reference/environment-variables/#configuration-files "Direct link to Configuration Files") In APISIX, you can use environment variables in the [`config.yaml`](https://docs.api7.ai/apisix/reference/environment-variables/#configyaml-and-configyamlexample) or [`apisix.yaml`](https://docs.api7.ai/apisix/reference/environment-variables/#apisixyaml) configuration files for values that should be configurable during deployments, using the `${{ENV_VAR}}` or `${{ENV_VAR:=default_value}}` syntax. The example below sets the listening ports for client requests and Admin API in environment variables: export APISIX_NODE_LISTEN=8132export ADMIN_API_PORT=9232 In `config.yaml`, reference the environment variables as follows: config.yaml apisix: node_listen: - ${{APISIX_NODE_LISTEN}}deployment: admin: admin_listen: port: ${{ADMIN_API_PORT}} After being started, APISIX will listen on port `8132` for client requests and port `9232` for Admin API requests. You can also configure default values to fall back to if no environment variables are set, for example: config.yaml apisix: node_listen: - ${{APISIX_NODE_LISTEN:=9080}}deployment: admin: admin_listen: port: ${{ADMIN_API_PORT:=9180}} If APISIX cannot resolve values for `APISIX_NODE_LISTEN` and `ADMIN_API_PORT` in the environment, it will default to listen on port `9080` for client requests and port `9180` for Admin API requests. ### Plugins[​](https://docs.api7.ai/apisix/reference/environment-variables/#plugins "Direct link to Plugins") APISIX supports the use of environment variables in plugin configurations for confidential information, such as [Redis password](https://docs.api7.ai/hub/limit-count/configuration#parameters) and [authentication key](https://docs.api7.ai/hub/jwt-auth#manage-secrets-in-environment-variables) , through the [NGINX `env` directive](https://nginx.org/en/docs/ngx_core_module.html#env) . The following example demonstrates how you can configure the `key-auth` plugin to fetch user authentication key from an environment variable. Save the value of the key to an environment variable: export JACK_AUTH_KEY=jack-key tip If you are running APISIX in Docker, you should set the environment variable using the `-e` flag when starting the container. Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Configure the `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-key-auth", "plugins": { "key-auth": { "key": "$env://JACK_AUTH_KEY" } } }' Create a route and enable `key-auth`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "key-auth-route", "uri": "/anything", "plugins": { "key-auth": {} }, "upstream" : { "nodes": { "httpbin.org": 1 } } }' Send a request to the route with the authentication key: curl "http://127.0.0.1:9080/anything" -H 'apikey: jack-key' You should receive an `HTTP/1.1 200 OK` response. For more information on the environment variable support in plugins, see [plugin docs](https://docs.api7.ai/hub) . * [Reserved Environment Variables](https://docs.api7.ai/apisix/reference/environment-variables/#reserved-environment-variables) * [Custom Environment Variables](https://docs.api7.ai/apisix/reference/environment-variables/#custom-environment-variables) * [Configuration Files](https://docs.api7.ai/apisix/reference/environment-variables/#configuration-files) * [Plugins](https://docs.api7.ai/apisix/reference/environment-variables/#plugins) --- # Canary Deployment | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/production/upgrade/canary-deployment/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page A canary deployment is a strategy for rolling out a new release by gradually increasing the traffic directed to it. This strategy can help test the new release with real traffic to identify and fix any issues before making it generally available. ![Canary deployments using Apache APISIX](https://static.api7.ai/uploads/2024/03/11/c6Gc5bkb_canary.gif) This guide will walk you through configuring canary deployments in APISIX using the [`traffic-split`](https://docs.api7.ai/hub/traffic-split) plugin. Configure Canary Deployment[​](https://docs.api7.ai/apisix/production/upgrade/canary-deployment/#configure-canary-deployment "Direct link to Configure Canary Deployment") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- You will be using `httpbin.org` and `mock.api7.ai` as the old and new services. First, direct all traffic to your old service. To do this, create a route with the following configuration: * Admin API * ADC curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/headers", "id": "canary-deployment", "plugins": { "traffic-split": { "rules": [ { "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } }, "weight": 100 }, { "weight": 0 } ] } ] } }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } } }' ❶ 100% of the requests should be routed to `httpbin.org`. ❷ 0% of the requests should be routed to `mock.api7.ai`. apisix.yaml routes: - uris: - /headers name: canary-deployment plugins: traffic-split: rules: - weighted_upstreams: - weight: 100 upstream: type: roundrobin pass_host: node nodes: httpbin.org:443: 1 scheme: https - weight: 0 upstream: type: roundrobin pass_host: node nodes: mock.api7.ai:443: 1 scheme: https ❶ 100% of the requests should be routed to `httpbin.org`. ❷ 0% of the requests should be routed to `mock.api7.ai`. Synchronize the configuration to APISIX: adc sync -f apisix.yaml So, if you send 100 requests, APISIX will direct them all to `httpbin.org`: resp=$(seq 100 | xargs -I{} curl "http://127.0.0.1:9080/headers" -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 You will get the following response: httpbin.org: 100, mock.api7.ai: 0 Next, update the configuration to direct 5% of the requests to the new service: * Admin API * ADC curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/headers", "id": "canary-deployment" "plugins": { "traffic-split": { "rules": [ { "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } }, "weight": 95 }, { "weight": 5 } ] } ] } }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } } }' apisix.yaml routes: - uris: - /headers name: canary-deployment plugins: traffic-split: rules: - weighted_upstreams: - weight: 95 upstream: type: roundrobin pass_host: node nodes: httpbin.org:443: 1 scheme: https - weight: 5 upstream: type: roundrobin pass_host: node nodes: mock.api7.ai:443: 1 scheme: https Synchronize the configuration to APISIX: adc sync -f apisix.yaml Now, if you send 100 requests, 5 of them will be directed to `mock.api7.ai`: resp=$(seq 100 | xargs -I{} curl "http://127.0.0.1:9080/headers" -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 The response will be as follows: httpbin.org: 95, mock.api7.ai: 5 The idea behind a canary deployment is to test the new release with a small subset of the requests to minimize the impact of any issues. Once this release is tested, gradually increase the percentage of requests sent to the new release until all requests are routed to it: * Admin API * ADC curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/headers", "id": "canary-deployment" "plugins": { "traffic-split": { "rules": [ { "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } }, "weight": 0 }, { "weight": 100 } ] } ] } }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } } }' apisix.yaml routes: - uris: - /headers name: canary-deployment plugins: traffic-split: rules: - weighted_upstreams: - weight: 0 upstream: type: roundrobin pass_host: node nodes: httpbin.org:443: 1 scheme: https - weight: 100 upstream: type: roundrobin pass_host: node nodes: mock.api7.ai:443: 1 scheme: https Synchronize the configuration to APISIX: adc sync -f apisix.yaml Now, all requests will be directed to `mock.api7.ai`: resp=$(seq 100 | xargs -I{} curl "http://127.0.0.1:9080/headers" -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 This is evident from the response: httpbin.org: 0, mock.api7.ai: 100 If the new release has issues, rollback to the old service by updating the configuration until the issue is resolved. Configure Advanced Canary Deployment[​](https://docs.api7.ai/apisix/production/upgrade/canary-deployment/#configure-advanced-canary-deployment "Direct link to Configure Advanced Canary Deployment") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ You can also deploy your releases in a more granular fashion using the `traffic-split` plugin. The example below shows how to route requests based on a header. This is useful when you want to give your clients more control over which release they access. Clients can also easily fall back to the old release by just modifying/removing the header. Configure this in a route as shown below: * Admin API * ADC curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/headers", "id": "canary-deployment", "plugins": { "traffic-split": { "rules": [ { "match": [ { "vars": [ ["http_release","==","new"] ] } ], "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } } } ] } ] } }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } } }' apisix.yaml routes: - uris: - /headers name: canary-deployment plugins: traffic-split: rules: - match: - vars: - - http_release - == - new weighted_upstreams: - weight: 1 upstream: type: roundrobin pass_host: node nodes: mock.api7.ai:443: 1 scheme: https upstream: type: roundrobin pass_host: node nodes: httpbin.org:443: 1 scheme: https Synchronize the configuration to APISIX: adc sync -f apisix.yaml Now, when you send a request with the `release` header: curl "http://127.0.0.1:9080/headers" -H 'release: new' You will see a response back from `mock.api7.ai`: { "headers": { "accept": "*/*", "host": "mock.api7.ai", ... }} If you do not specify the `release` header: curl "http://127.0.0.1:9080/headers" APISIX will fall back to the `httpbin.org` upstream: { "headers": { "Accept": "*/*", "Host": "httpbin.org", ... }} Refer to the [`traffic-split`](https://docs.api7.ai/hub/traffic-split) plugin documentation to learn more. * [Configure Canary Deployment](https://docs.api7.ai/apisix/production/upgrade/canary-deployment/#configure-canary-deployment) * [Configure Advanced Canary Deployment](https://docs.api7.ai/apisix/production/upgrade/canary-deployment/#configure-advanced-canary-deployment) --- # Mocking | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/mocking/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `mocking` plugin allows you to simulate API responses without forwarding requests to upstream services. The plugin supports the customization of the response status code, body, headers, and more. This is particularly useful during development, testing, or debugging phases, where the actual upstream service might be unavailable, under maintenance, or expensive to call. By providing mock responses in a predefined format, the plugin enables you to test client-side integrations, validate request handling, and debug issues without relying on the upstream infrastructure. Examples[​](https://docs.api7.ai/hub/mocking/#examples "Direct link to Examples") ---------------------------------------------------------------------------------- The examples below demonstrate how you can configure `mocking` plugin for different scenarios. ### Generate Specific Mock Responses[​](https://docs.api7.ai/hub/mocking/#generate-specific-mock-responses "Direct link to Generate Specific Mock Responses") The following example demonstrates how to configure the plugin to generate a specific mock response and response status code without forwarding the request to the upstream service. Create a route using the `mocking` plugin and define a response body for the expected mock responses: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "mocking-route", "uri": "/anything", "plugins": { "mocking": { "response_status":201, "response_example":"{\"Lastname\":\"Brown\",\"Age\":56}" } } }' ❶ Configure the expected mock response status code to be `201`. ❷ Configure the expected mock response body to be `{"Lastname":"Brown","Age":56}`. Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 201 Created` mock response and see the following response body: {"Lastname":"Brown","Age":56} ### Generate Mock Response Headers[​](https://docs.api7.ai/hub/mocking/#generate-mock-response-headers "Direct link to Generate Mock Response Headers") The following example demonstrates how to configure the plugin to generate mock response headers and use a [built-in variable](https://docs.api7.ai/apisix/reference/built-in-variables) in the response body. Create a route using the `mocking` plugin, define response headers, and response body for the expected mock responses: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "mocking-route", "uri": "/anything", "plugins": { "mocking": { "response_headers": { "X-User-Id": 100, "X-Product-Id": "apac-398-472" }, "response_example":"Client IP: $remote_addr" } } }' ❶ Configure the expected mock response header `X-User-Id: 100`. ❷ Configure the expected mock response header `X-Product-Id: apac-398-472`. ❸ Configure the expected mock response body to display the client IP address. Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive a response similar to the following: HTTP/1.1 200 OK...X-Product-Id: apac-398-472X-User-Id: 100Client IP: 192.168.65.1 ### Generate Mock Responses using JSON Schema[​](https://docs.api7.ai/hub/mocking/#generate-mock-responses-using-json-schema "Direct link to Generate Mock Responses using JSON Schema") The following example demonstrates how to configure the plugin to generate mock responses following a specific [JSON schema](https://json-schema.org/) . Create a route using the `mocking` plugin and define a JSON schema for the expected mock responses: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "mocking-route", "uri": "/anything", "plugins": { "mocking": { "response_schema": { "type": "object", "properties": { "id": { "type": "string", "example": "abcd" }, "ip": { "type": "number", "example": 192.168.0.10 }, "random_str_arr": { "type": "array", "items": { "type": "string" } }, "nested_obj": { "type": "object", "properties": { "random_str": { "type": "string" }, "child_nested_obj": { "type": "object", "properties": { "random_bool": { "type": "boolean", "example": true }, "random_int_arr": { "type": "array", "items": { "type": "integer", "example": 155 } } } } } } } } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should see a mock response similar to the following, without the actual response from the upstream service: { "ip":192.168.0.10, "random_str_arr":[ "fb","lyquibkwc","r" ], "id":"abcd", "nested_obj":{ "random_str":"bzbb", "child_nested_obj":{ "random_bool":true, "random_int_arr":[155,155,155] } }} * [Examples](https://docs.api7.ai/hub/mocking/#examples) * [Generate Specific Mock Responses](https://docs.api7.ai/hub/mocking/#generate-specific-mock-responses) * [Generate Mock Response Headers](https://docs.api7.ai/hub/mocking/#generate-mock-response-headers) * [Generate Mock Responses using JSON Schema](https://docs.api7.ai/hub/mocking/#generate-mock-responses-using-json-schema) --- # OAS Validator | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/oas-validator/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `oas-validator` plugin allows validation of HTTP requests and responses against a defined API specification that complies with Open API v3 specification. Examples[​](https://docs.api7.ai/hub/oas-validator/#examples "Direct link to Examples") ---------------------------------------------------------------------------------------- The Swagger Petstore [Open API spec](https://petstore3.swagger.io/api/v3/openapi.json) will be used in the examples below. export OPEN_API_SPEC=$(curl -s "https://petstore3.swagger.io/api/v3/openapi.json" | sed 's/"/\\"/g') ### Validate Request Body[​](https://docs.api7.ai/hub/oas-validator/#validate-request-body "Direct link to Validate Request Body") This example demonstrates validation of request body against a given specification. Create the following route with the OAS validator plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "body_validation", "uri": "/*", "plugins": { "oas-validator": { "spec": "'"${OPEN_API_SPEC}"'" } }, "upstream": { "type": "roundrobin", "nodes": { "petstore3.swagger.io:443": 1 }, "scheme": "https", "pass_host": "node" } }' #### Failed Validation[​](https://docs.api7.ai/hub/oas-validator/#failed-validation "Direct link to Failed Validation") Send a request to the above route with a request body that does not satisfy the defined Open API Spec: curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{"invalid-body": "this is an invalid body"}' You should see an `HTTP/1.1 400 Bad Request` response with the response body similar to the following: {"message":"failed to validate request."} #### Successful Validation[​](https://docs.api7.ai/hub/oas-validator/#successful-validation "Direct link to Successful Validation") Send a request to the route with a valid request body: curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "id": 1, "name": "doggie", "category": { "id": 1, "name": "Dogs" }, "photoUrls": ["string"], "tags": [{ "id": 1, "name": "tag1" }], "status": "available" }' You should see an `HTTP/1.1 200 OK` response with the response body similar to the following: { "id": 1, "category": { "id": 1, "name": "Dogs" }, "name": "doggie", "photoUrls": ["string"], "tags": [{ "id": 1, "name": "tag1" }], "status": "available"} ### Get Verbose Error Response[​](https://docs.api7.ai/hub/oas-validator/#get-verbose-error-response "Direct link to Get Verbose Error Response") This example demonstrates getting verbose error response when the validation fails. Create a route with the OAS Validator plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "body_validation", "uri": "/*", "plugins": { "oas-validator": { "spec": "'"${OPEN_API_SPEC}"'", "verbose_errors": true } }, "upstream": { "type": "roundrobin", "nodes": { "petstore3.swagger.io:443": 1 }, "scheme": "https", "pass_host": "node" } }' Send a request to the route created above with an invalid request body: curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{"invalid-body": "this is an invalid body"}' You should see an `HTTP/1.1 400 Bad Request` response with the response body similar to the following: doesn't match schema #/components/schemas/Pet: Error at "/name": property "name" is missingSchema: { "properties": { "category": { "$ref": "#/components/schemas/Category" }, "id": { "example": 10, "format": "int64", "type": "integer" }, ... }Value: { "invalid-body": "this is an invalid body" } | Error at "/photoUrls": property "photoUrls" is missingSchema: { "properties": { "category": { "$ref": "#/components/schemas/Category" }, ... }Value: { "invalid-body": "this is an invalid body" } ### Monitor Violations Without Blocking Traffic[​](https://docs.api7.ai/hub/oas-validator/#monitor-violations-without-blocking-traffic "Direct link to Monitor Violations Without Blocking Traffic") Use `reject_if_not_match` to control whether non-compliant requests are blocked or allowed through. This example applies only to API7 Enterprise from version 3.9.6 and is not applicable in APISIX. #### Reject Non-Compliant Requests[​](https://docs.api7.ai/hub/oas-validator/#reject-non-compliant-requests "Direct link to Reject Non-Compliant Requests") When `reject_if_not_match` is set to `true` (default), requests that fail OAS validation are blocked with a `HTTP/1.1 400 Bad Request` response. Create a route with the OAS Validator plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "body_validation", "uri": "/*", "plugins": { "oas-validator": { "spec": "'"${OPEN_API_SPEC}"'", "reject_if_not_match": true } }, "upstream": { "type": "roundrobin", "nodes": { "petstore3.swagger.io:443": 1 }, "scheme": "https", "pass_host": "node" } }' Send a request with an invalid body: curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{"invalid-body": "this is an invalid body"}' You should see an `HTTP/1.1 400 Bad Request` response with the response body similar to the following: {"message":"failed to validate request."} #### Allow Non-Compliant Requests to Pass[​](https://docs.api7.ai/hub/oas-validator/#allow-non-compliant-requests-to-pass "Direct link to Allow Non-Compliant Requests to Pass") When `reject_if_not_match` is set to `false`, non-compliant requests are forwarded to the upstream instead of being blocked, and validation errors are recorded in the error logs. Update the route to set `reject_if_not_match` to `false`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "body_validation", "uri": "/*", "plugins": { "oas-validator": { "spec": "'"${OPEN_API_SPEC}"'", "reject_if_not_match": false } }, "upstream": { "type": "roundrobin", "nodes": { "petstore3.swagger.io:443": 1 }, "scheme": "https", "pass_host": "node" } }' Send a request with an invalid body: curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{"invalid-body": "this is an invalid body"}' You should see an `HTTP/1.1 500 Internal Server Error` response since `petstore3.swagger.io` does not correctly handle an invalid body. However, you can see that the request got passed successfully to the upstream. You should also see an error log capturing the method, URI, and validation error similar to this: [error] error occurred while validating request [POST /api/v3/pet], err: ... This lets you audit non-compliant traffic in your logs without breaking existing clients. * [Examples](https://docs.api7.ai/hub/oas-validator/#examples) * [Validate Request Body](https://docs.api7.ai/hub/oas-validator/#validate-request-body) * [Get Verbose Error Response](https://docs.api7.ai/hub/oas-validator/#get-verbose-error-response) * [Monitor Violations Without Blocking Traffic](https://docs.api7.ai/hub/oas-validator/#monitor-violations-without-blocking-traffic) --- # UA Restriction | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ua-restriction/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ua-restriction` plugin supports restricting access to upstream resources through either configuring an allowlist or denylist of user agents. A common use case is to prevent web crawlers from overloading the upstream resources and causing service degradation. Examples[​](https://docs.api7.ai/hub/ua-restriction/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `ua-restriction` for different scenarios. ### Reject Web Crawlers and Customize Error Message[​](https://docs.api7.ai/hub/ua-restriction/#reject-web-crawlers-and-customize-error-message "Direct link to Reject Web Crawlers and Customize Error Message") The following example demonstrates how you can configure the plugin to fend off unwanted web crawlers and customize the rejection message. Create a route as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ua-restriction-route", "uri": "/anything", "plugins": { "ua-restriction": { "bypass_missing": false, "denylist": [ "(Baiduspider)/(\\d+)\\.(\\d+)", "bad-bot-1" ], "message": "Access denied" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Do not allow bypassing UA restriction rules. ❷ Configure user agents that should not be able to access the upstream resource. ❸ Customize the error message for when the access is denied. Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Send another request to the route with a disallowed user agent: curl -i "http://127.0.0.1:9080/anything" -H 'User-Agent: Baiduspider/5.0' You should receive an `HTTP/1.1 403 Forbidden` response with the following message: {"message":"Access denied"} ### Bypass UA Restriction Checks[​](https://docs.api7.ai/hub/ua-restriction/#bypass-ua-restriction-checks "Direct link to Bypass UA Restriction Checks") The following example demonstrates how to configure the plugin to allow requests of a specific user agent to bypass the UA restriction. Create a route as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ua-restriction-route", "uri": "/anything", "plugins": { "ua-restriction": { "bypass_missing": true, "allowlist": [ "good-bot-1" ], "message": "Access denied" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Allow bypassing UA restriction rules. ❷ Configure user agents that should be allowed to access the upstream resource. Send a request to the route without modifying the user agent: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 403 Forbidden` response with the following message: {"message":"Access denied"} Send another request to the route with an empty user agent: curl -i "http://127.0.0.1:9080/anything" -H 'User-Agent: ' You should receive an `HTTP/1.1 200 OK` response. * [Examples](https://docs.api7.ai/hub/ua-restriction/#examples) * [Reject Web Crawlers and Customize Error Message](https://docs.api7.ai/hub/ua-restriction/#reject-web-crawlers-and-customize-error-message) * [Bypass UA Restriction Checks](https://docs.api7.ai/hub/ua-restriction/#bypass-ua-restriction-checks) --- # gRPC Web | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/grpc-web/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page gRPC is a high-performance RPC framework based on HTTP/2 and Protocol Buffers, but it is not natively supported by browsers. gRPC-Web defines a browser-compatible protocol for sending gRPC requests over HTTP/1.1 or HTTP/2. The `grpc-web` plugin translates gRPC-Web requests into native gRPC calls and forwards them to upstream gRPC services. Request Handling[​](https://docs.api7.ai/hub/grpc-web/#request-handling "Direct link to Request Handling") ----------------------------------------------------------------------------------------------------------- The `grpc-web` plugin processes client requests with specific HTTP methods, content types, and CORS rules. ### Supported HTTP Methods[​](https://docs.api7.ai/hub/grpc-web/#supported-http-methods "Direct link to Supported HTTP Methods") The plugin supports: * `POST` for gRPC-Web requests * `OPTIONS` for CORS preflight checks See [CORS support](https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support) for details. ### Supported Content Types[​](https://docs.api7.ai/hub/grpc-web/#supported-content-types "Direct link to Supported Content Types") The plugin recognizes the following content types: * `application/grpc-web` * `application/grpc-web-text` * `application/grpc-web+proto` * `application/grpc-web-text+proto` It automatically decodes messages in binary or base64 text format and translates them into standard gRPC for the upstream server. See [Protocol differences vs gRPC over HTTP2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2) for more details. ### CORS Handling[​](https://docs.api7.ai/hub/grpc-web/#cors-handling "Direct link to CORS Handling") The plugin automatically handles cross-origin requests. By default: * All origins (`*`) are allowed * `POST` requests are permitted * Accepted request headers: `content-type`, `x-grpc-web`, `x-user-agent` * Exposed response headers: `grpc-status`, `grpc-message` Examples[​](https://docs.api7.ai/hub/grpc-web/#examples "Direct link to Examples") ----------------------------------------------------------------------------------- The following examples demonstrate how to configure and use the `grpc-web` plugin with a gRPC-Web client. ### Prerequisites[​](https://docs.api7.ai/hub/grpc-web/#prerequisites "Direct link to Prerequisites") Before proceeding with the examples, complete the following preliminary steps to set up an upstream server and gRPC-Web client. #### Start an Upstream Server[​](https://docs.api7.ai/hub/grpc-web/#start-an-upstream-server "Direct link to Start an Upstream Server") Start a [grpcbin server](https://github.com/moul/grpcbin) in Docker to serve as the example upstream: docker run -d \ --name grpcbin \ -p 9000:9000 \ moul/grpcbin #### Generate gRPC-Web client code[​](https://docs.api7.ai/hub/grpc-web/#generate-grpc-web-client-code "Direct link to Generate gRPC-Web client code") Download the protocol buffer definition `hello.proto`: curl -O https://raw.githubusercontent.com/moul/pb/refs/heads/master/hello/hello.proto Install [`protobuf`](https://github.com/protocolbuffers/protobuf/releases) and [`protoc-gen-grpc-web`](https://github.com/grpc/grpc-web/releases) . Generate the gRPC-Web client code from `hello.proto`: protoc \ --js_out=import_style=commonjs:. \ --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. \ hello.proto You should see two files generated in the current directory: `hello_pb.js` for protocol buffers message classes and `hello_grpc_web_pb.js` for gRPC-Web client stubs. #### Create a Client[​](https://docs.api7.ai/hub/grpc-web/#create-a-client "Direct link to Create a Client") Create a Node.js project and install the required dependencies: npm init -ynpm install xhr2 grpc-web google-protobuf Create a client file: client.js const XMLHttpRequest = require('xhr2');const { HelloServiceClient } = require('./hello_grpc_web_pb');const { HelloRequest } = require('./hello_pb');global.XMLHttpRequest = XMLHttpRequest;function sayHello(){ const client = new HelloServiceClient('http://127.0.0.1:9080/grpc/web', null, { format: 'text', }); const req = new HelloRequest(); req.setGreeting('jack'); const call = client.sayHello(req, {}, (err, resp) => { if (err) { console.error('grpc error:', err.code, err.message); } else { console.log('reply:', resp.getReply()); } }); call.on('metadata', (metadata) => { console.log('Response headers:', metadata); });}function lotsOfReplies() { const client = new HelloServiceClient('http://127.0.0.1:9080/grpc/web', null, { format: 'text', }); const req = new HelloRequest(); req.setGreeting('rep'); const stream = client.lotsOfReplies(req, {}); stream.on('metadata', (metadata) => { console.log('Response headers:', metadata); });}lotsOfReplies()sayHello() You can later run the client with `node client.js` to send both unary and server-streaming requests to your gRPC server via the gateway. ### Proxy gRPC-Web (Prefix Match Route)[​](https://docs.api7.ai/hub/grpc-web/#proxy-grpc-web-prefix-match-route "Direct link to Proxy gRPC-Web (Prefix Match Route)") The following examples demonstrate how to configure and use the `grpc-web` plugin with the gRPC-Web client set up previously. Create a route with the `grpc-web` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "grpc-web-route", "uri": "/grpc/web/*", "plugins": { "grpc-web": {} }, "upstream": { "scheme": "grpc", "type": "roundrobin", "nodes": { "192.168.10.103:9000": 1 } }}' ❶ Configure the `uri` to prefix-match the route requested in `client.js`. ❷ Enable the `grpc-web` plugin. ❸ Set the upstream scheme to `grpc`. ❹ Replace with your upstream server address. understand the uri In APISIX versions prior to 3.15.0 and API7 Enterprise versions prior to 3.8.21, the route URI must use a prefix match because gRPC-Web clients include the package name, service name, and method name in the request URI. Using an absolute URI match in these versions will prevent the request from matching the route. [Absolute URI routes](https://docs.api7.ai/hub/grpc-web/#proxy-grpc-web-absolute-uri) are supported in later versions. In this example, the route URI must be configured as `/grpc/web/*` to correctly match client requests such as `/grpc/web/hello.HelloService/SayHello`. Using a broader prefix like `/grpc/*` would prevent the gateway from correctly extracting the full service path, resulting in errors such as `unknown service web/hello.HelloService`. Run the client to send requests to the gateway route: node client.js You should see a reply from the upstream gRPC server: Response headers: { ... 'access-control-allow-origin': '*', 'access-control-expose-headers': 'grpc-message,grpc-status'}Response headers: { ... 'access-control-allow-origin': '*', 'access-control-expose-headers': 'grpc-message,grpc-status'}reply: hello jack ### Proxy gRPC-Web (Absolute URI)[​](https://docs.api7.ai/hub/grpc-web/#proxy-grpc-web-absolute-uri "Direct link to Proxy gRPC-Web (Absolute URI)") This example applies to APISIX 3.15.0 and later, and API7 Enterprise 3.8.21 and later. When an absolute URI is used, the gateway does not automatically strip the URI path prefix. To forward requests correctly to the upstream gRPC server, use the `proxy-rewrite` plugin to adjust the request path. Create a route with the `grpc-web` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "grpc-web-route", "uri": "/grpc/web/hello.HelloService/SayHello", "plugins": { "grpc-web": {}, "proxy-rewrite": { "uri": "/hello.HelloService/SayHello", "set_ngx_uri": "true" } }, "upstream": { "scheme": "grpc", "type": "roundrobin", "nodes": { "192.168.10.103:9000": 1 } }}' ❶ Configure the `uri` to use the absolute path, including the base prefix and the gRPC full service path. ❷ Configure `proxy-rewrite` to rewrite the path and strip the route prefix. ❸ Set `set_ngx_uri` to `true` to update the requested path to the URI defined in the `proxy-rewrite` plugin. Without this setting, the gateway will not correctly forward the request to the upstream, resulting in errors such as `unknown service grpc/web/hello.HelloService`. ❹ Set the upstream scheme to `grpc`. ❺ Replace with your upstream server address. Run the client to send requests to the gateway route: node client.js You should see a reply from the upstream gRPC server: Response headers: { ... 'access-control-allow-origin': '*', 'access-control-expose-headers': 'grpc-message,grpc-status'}Response headers: { ... 'access-control-allow-origin': '*', 'access-control-expose-headers': 'grpc-message,grpc-status'}reply: hello jack * [Request Handling](https://docs.api7.ai/hub/grpc-web/#request-handling) * [Supported HTTP Methods](https://docs.api7.ai/hub/grpc-web/#supported-http-methods) * [Supported Content Types](https://docs.api7.ai/hub/grpc-web/#supported-content-types) * [CORS Handling](https://docs.api7.ai/hub/grpc-web/#cors-handling) * [Examples](https://docs.api7.ai/hub/grpc-web/#examples) * [Prerequisites](https://docs.api7.ai/hub/grpc-web/#prerequisites) * [Proxy gRPC-Web (Prefix Match Route)](https://docs.api7.ai/hub/grpc-web/#proxy-grpc-web-prefix-match-route) * [Proxy gRPC-Web (Absolute URI)](https://docs.api7.ai/hub/grpc-web/#proxy-grpc-web-absolute-uri) --- # gRPC Transcode | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/grpc-transcode/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `grpc-transcode` plugin transforms between HTTP requests and gRPC requests, as well as their corresponding responses. With this plugin enabled, APISIX accepts an HTTP request from the client, transcodes and forwards it to an upstream gRPC service. When APISIX receives the gRPC response, it will transform the response back to an HTTP response and send it to the client. Examples[​](https://docs.api7.ai/hub/grpc-transcode/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------- The examples below demonstrate how you can configure the `grpc-transcode` plugin for different scenarios. To follow along the examples, start an [example gRPC server](https://github.com/api7/grpc_server_example) in Docker: docker run -d \ --name grpc-example-server \ -p 50051:50051 \ api7/grpc-server-example:1.0.2 ### Transform between HTTP and gRPC Requests[​](https://docs.api7.ai/hub/grpc-transcode/#transform-between-http-and-grpc-requests "Direct link to Transform between HTTP and gRPC Requests") The following example demonstrates how to configure protobuf in APISIX and transform between HTTP and gRPC Requests using the `grpc-transcode` plugin. Create a proto resource to store the protobuf: curl "http://127.0.0.1:9180/apisix/admin/protos" -X PUT -d '{ "id": "echo-proto", "content": "syntax = \"proto3\"; package echo; service EchoService { rpc Echo (EchoMsg) returns (EchoMsg); } message EchoMsg { string msg = 1; }"}' Create a route with the `grpc-transcode` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "grpc-transcode-route", "methods": ["GET"], "uri": "/echo", "plugins": { "grpc-transcode": { "proto_id": "echo-proto", "service": "echo.EchoService", "method": "Echo" } }, "upstream": { "scheme": "grpc", "type": "roundrobin", "nodes": { "grpc-example-server:50051": 1 } }}' ❶ `proto_id`: ID of the proto object which defines gRPC services ❷ `service`: gRPC service to interact with ❸ `method`: gRPC method to use To verify, send an HTTP request to the route with parameters defined in `EchoMsg`: curl "http://127.0.0.1:9080/echo?msg=Hello" You should receive the following response: {"msg":"Hello"} ### Configure Protobuf with `.pb` File[​](https://docs.api7.ai/hub/grpc-transcode/#configure-protobuf-with-pb-file "Direct link to configure-protobuf-with-pb-file") The following example demonstrates how to configure protobuf with `.pb` file in APISIX and transform between HTTP and gRPC Requests using the `grpc-transcode` plugin. If your proto file contains imports, or if you want to combine multiple proto files, you can generate a `.pb` file using the [protoc](https://google.github.io/proto-lens/installing-protoc.html) utility and use it in APISIX, following the below steps. Save the protocol buffer definition to a file called `echo.proto`: echo.proto syntax = "proto3";package echo;service EchoService { rpc Echo (EchoMsg) returns (EchoMsg);}message EchoMsg { string msg = 1;} Generate the `.pb` file with the [protoc](https://google.github.io/proto-lens/installing-protoc.html) utility and output it to a new file called `echo_proto.pb`: protoc --include_imports --descriptor_set_out=echo_proto.pb echo.proto Convert the `.pb` file from binary to base64 and configure it in APISIX: curl "http://127.0.0.1:9180/apisix/admin/protos" -X PUT -d '{ "id": "echo-proto", "content" : "'"$(base64 -w0 /path/to/echo_proto.pb)"'"}' Create a route with the `grpc-transcode` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "grpc-transcode-route", "methods": ["GET"], "uri": "/echo", "plugins": { "grpc-transcode": { "proto_id": "echo-proto", "service": "echo.EchoService", "method": "Echo" } }, "upstream": { "scheme": "grpc", "type": "roundrobin", "nodes": { "grpc-example-server:50051": 1 } }}' To verify, send an HTTP request to the route with parameters defined in `EchoMsg`: curl "http://127.0.0.1:9080/echo?msg=Hello" You should receive the following response: {"msg":"Hello"} ### Display Error Details in Response Body[​](https://docs.api7.ai/hub/grpc-transcode/#display-error-details-in-response-body "Direct link to Display Error Details in Response Body") The following example demonstrates how to configure the `grpc-transcode` plugin to include the `grpc-status-details-bin` field in the response header for error reporting, when made available by the gRPC server; and decode the message to be displayed in the response body. Create a proto resource to store the protobuf: curl "http://127.0.0.1:9180/apisix/admin/protos" -X PUT -d '{ "id": "hello-proto", "content": "syntax = \"proto3\"; package helloworld; service Greeter { rpc GetErrResp (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; repeated string items = 2; } message HelloReply { string message = 1; repeated string items = 2; }"}' Create a route with the `grpc-transform` plugin and set `show_status_in_body` to `true`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "grpc-transcode-route", "uri": "/hello", "plugins": { "grpc-transcode": { "proto_id": "hello-proto", "service": "helloworld.Greeter", "method": "GetErrResp", "show_status_in_body": true } }, "upstream": { "scheme": "grpc", "type": "roundrobin", "nodes": { "grpc-example-server:50051": 1 } }}' Send a request to the route: curl -i "http://127.0.0.1:9080/hello?name=world" You should see an error response similar to the following: HTTP/1.1 503 Service Temporarily UnavailableDate: Wed, 21 Feb 2024 03:08:30 GMTContent-Type: application/jsonTransfer-Encoding: chunkedConnection: keep-alivegrpc-status: 14grpc-message: Out of servicegrpc-status-details-bin: CA4SDk91dCBvZiBzZXJ2aWNlGlcKKnR5cGUuZ29vZ2xlYXBpcy5jb20vaGVsbG93b3JsZC5FcnJvckRldGFpbBIpCAESHFRoZSBzZXJ2ZXIgaXMgb3V0IG9mIHNlcnZpY2UaB3NlcnZpY2UServer: APISIX/3.8.0{"error":{"message":"Out of service","code":14,"details":[{"value":"\b\u0001\u0012\u001cThe server is out of service\u001a\u0007service","type_url":"type.googleapis.com/helloworld.ErrorDetail"}]}} Note that certain information are not fully decoded in the error response message. To decode the message, update the protobuf definition: curl "http://127.0.0.1:9180/apisix/admin/protos" -X PUT -d '{ "id": "hello-proto", "content": "syntax = \"proto3\"; package helloworld; service Greeter { rpc GetErrResp (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; repeated string items = 2; } message HelloReply { string message = 1; repeated string items = 2; } message ErrorDetail { int64 code = 1; string message = 2; string type = 3; }"}' Configure the route with `grpc-transcode` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "grpc-transcode-route", "uri": "/hello", "plugins": { "grpc-transcode": { "proto_id": "hello-proto", "service": "helloworld.Greeter", "method": "GetErrResp", "show_status_in_body": true, "status_detail_type": "helloworld.ErrorDetail" } }, "upstream": { "scheme": "grpc", "type": "roundrobin", "nodes": { "grpc-example-server:50051": 1 } }}' Send another request to the route: curl -i "http://127.0.0.1:9080/hello?name=world" You should see a response with error message fully decoded: HTTP/1.1 503 Service Temporarily UnavailableDate: Wed, 21 Feb 2024 03:11:43 GMTContent-Type: application/jsonTransfer-Encoding: chunkedConnection: keep-alivegrpc-status: 14grpc-message: Out of servicegrpc-status-details-bin: CA4SDk91dCBvZiBzZXJ2aWNlGlcKKnR5cGUuZ29vZ2xlYXBpcy5jb20vaGVsbG93b3JsZC5FcnJvckRldGFpbBIpCAESHFRoZSBzZXJ2ZXIgaXMgb3V0IG9mIHNlcnZpY2UaB3NlcnZpY2UServer: APISIX/3.8.0{"error":{"message":"Out of service","code":14,"details":[{"message":"The server is out of service","code":1,"type":"service"}]}} ### Configure Encoder/Decoder Options[​](https://docs.api7.ai/hub/grpc-transcode/#configure-encoderdecoder-options "Direct link to Configure Encoder/Decoder Options") The following example demonstrates how to configure encoder and decoder [options](https://github.com/starwing/lua-protobuf?tab=readme-ov-file#options) for the `grpc-transcode` plugin. Specifically, you will be applying the `int64_as_string` option to a method that performs addition operation and understand its effect. Create a proto resource to store the protobuf: curl "http://127.0.0.1:9180/apisix/admin/protos" -X PUT -d '{ "id": "plus-proto", "content": "syntax = \"proto3\"; package helloworld; service Greeter { rpc Plus (PlusRequest) returns (PlusReply) {} } message PlusRequest { int64 a = 1; int64 b = 2; } message PlusReply { int64 result = 1; }"}' Configure the route with `grpc-transcode` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "grpc-transcode-route", "uri": "/plus", "plugins": { "grpc-transcode": { "proto_id": "plus-proto", "service": "helloworld.Greeter", "method": "Plus" } }, "upstream": { "scheme": "grpc", "type": "roundrobin", "nodes": { "grpc-example-server:50051": 1 } }}' Send a request to the route: curl "http://127.0.0.1:9080/plus?a=1237528374197491&b=1237528374197491" You should see a response showing a sum of the two numbers: {"result":2.475056748395e+15} Update the route to use the `int64_as_string` option: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "grpc-transcode-route", "uri": "/plus", "plugins": { "grpc-transcode": { "proto_id": "plus-proto", "service": "helloworld.Greeter", "method": "Plus", "pb_option":["int64_as_string"] } }, "upstream": { "scheme": "grpc", "type": "roundrobin", "nodes": { "grpc-example-server:50051": 1 } }}' Send another request to the route: curl "http://127.0.0.1:9080/plus?a=1237528374197491&b=1237528374197491" You should see a response showing a sum of the two numbers with better precision: {"result":"#2475056748394982"} * [Examples](https://docs.api7.ai/hub/grpc-transcode/#examples) * [Transform between HTTP and gRPC Requests](https://docs.api7.ai/hub/grpc-transcode/#transform-between-http-and-grpc-requests) * [Configure Protobuf with `.pb` File](https://docs.api7.ai/hub/grpc-transcode/#configure-protobuf-with-pb-file) * [Display Error Details in Response Body](https://docs.api7.ai/hub/grpc-transcode/#display-error-details-in-response-body) * [Configure Encoder/Decoder Options](https://docs.api7.ai/hub/grpc-transcode/#configure-encoderdecoder-options) --- # Request Validation | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/request-validation/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `request-validation` plugin validates requests before forwarding them to upstream services. This plugin uses [JSON Schema](https://github.com/api7/jsonschema) for validation and can validate headers and body of a request. See [JSON schema specification](https://json-schema.org/specification) to learn more about the syntax. Examples[​](https://docs.api7.ai/hub/request-validation/#examples "Direct link to Examples") --------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `request-validation` for different scenarios. ### Validate Request Header[​](https://docs.api7.ai/hub/request-validation/#validate-request-header "Direct link to Validate Request Header") The following example demonstrates how to validate request headers against a defined JSON schema. Create a route with `request-validation` plugin as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-validation-route", "uri": "/get", "plugins": { "request-validation": { "header_schema": { "type": "object", "required": ["User-Agent", "Host"], "properties": { "User-Agent": { "type": "string", "pattern": "^curl\/" }, "Host": { "type": "string", "enum": ["httpbin.org", "httpbin"] } } } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ `required`: require requests to include the specified headers. ❷ `properties`: require headers to conform to the specified requirements. #### Verify with Request Conforming to the Schema[​](https://docs.api7.ai/hub/request-validation/#verify-with-request-conforming-to-the-schema "Direct link to Verify with Request Conforming to the Schema") Send a request with header `Host: httpbin`, which complies with the schema: curl -i "http://127.0.0.1:9080/get" -H "Host: httpbin" You should receive an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin", "User-Agent": "curl/7.74.0", "X-Amzn-Trace-Id": "Root=1-6509ae35-63d1e0fd3934e3f221a95dd8", "X-Forwarded-Host": "httpbin" }, "origin": "127.0.0.1, 183.17.233.107", "url": "http://httpbin/get"} #### Verify with Request Not Conforming to the Schema[​](https://docs.api7.ai/hub/request-validation/#verify-with-request-not-conforming-to-the-schema "Direct link to Verify with Request Not Conforming to the Schema") Send a request without any header: curl -i "http://127.0.0.1:9080/get" You should receive an `HTTP/1.1 400 Bad Request` response, showing that the request fails to pass validation: property "Host" validation failed: matches none of the enum value Send a request with the required headers but with non-conformant header value: curl -i "http://127.0.0.1:9080/get" -H "Host: httpbin" -H "User-Agent: cli-mock" You should receive an `HTTP/1.1 400 Bad Request` response showing the `User-Agent` header value does not match the expected pattern: property "User-Agent" validation failed: failed to match pattern "^curl/" with "cli-mock" ### Customize Rejection Message and Status Code[​](https://docs.api7.ai/hub/request-validation/#customize-rejection-message-and-status-code "Direct link to Customize Rejection Message and Status Code") The following example demonstrates how to customize response status and message when the validation fails. Configure the route with `request-validation` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-validation-route", "uri": "/get", "plugins": { "request-validation": { "header_schema": { "type": "object", "required": ["Host"], "properties": { "Host": { "type": "string", "enum": ["httpbin.org", "httpbin"] } } }, "rejected_code": 403, "rejected_msg": "Request header validation failed." } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ `rejected_code`: customize rejection code. ❷ `rejected_msg`: customize rejection message. Send a request with a misconfigured `Host` in the header: curl -i "http://127.0.0.1:9080/get" -H "Host: httpbin2" You should receive an `HTTP/1.1 403 Forbidden` response with the custom message: Request header validation failed. ### Validate Request Body[​](https://docs.api7.ai/hub/request-validation/#validate-request-body "Direct link to Validate Request Body") The following example demonstrates how to validate request body against a defined JSON schema. The `request-validation` plugin supports validation of two types of media types: * `application/json` * `application/x-www-form-urlencoded` #### Validate JSON Request Body[​](https://docs.api7.ai/hub/request-validation/#validate-json-request-body "Direct link to Validate JSON Request Body") Create a route with `request-validation` plugin as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-validation-route", "uri": "/post", "plugins": { "request-validation": { "header_schema": { "type": "object", "required": ["Content-Type"], "properties": { "Content-Type": { "type": "string", "pattern": "^application\/json$" } } }, "body_schema": { "type": "object", "required": ["required_payload"], "properties": { "required_payload": {"type": "string"}, "boolean_payload": {"type": "boolean"}, "array_payload": { "type": "array", "minItems": 1, "items": { "type": "integer", "minimum": 200, "maximum": 599 }, "uniqueItems": true, "default": [200] } } } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request with JSON body that conforms to the schema to verify: curl -i "http://127.0.0.1:9080/post" -X POST \ -H "Content-Type: application/json" \ -d '{"required_payload":"hello", "array_payload":[301]}' You should receive an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "data": "{\"array_payload\":[301],\"required_payload\":\"hello\"}", "files": {}, "form": {}, "headers": { ... }, "json": { "array_payload": [ 301 ], "required_payload": "hello" }, "origin": "127.0.0.1, 183.17.233.107", "url": "http://127.0.0.1/post"} If you send a request without specifying `Content-Type: application/json`: curl -i "http://127.0.0.1:9080/post" -X POST \ -d '{"required_payload":"hello,world"}' You should receive an `HTTP/1.1 400 Bad Request` response similar to the following: property "Content-Type" validation failed: failed to match pattern "^application/json$" with "application/x-www-form-urlencoded" Similarly, if you send a request without the required JSON field `required_payload`: curl -i "http://127.0.0.1:9080/post" -X POST \ -H "Content-Type: application/json" \ -d '{}' You should receive an `HTTP/1.1 400 Bad Request` response: property "required_payload" is required #### Validate URL-Encoded Form Body[​](https://docs.api7.ai/hub/request-validation/#validate-url-encoded-form-body "Direct link to Validate URL-Encoded Form Body") Create a route with `request-validation` plugin as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "request-validation-route", "uri": "/post", "plugins": { "request-validation": { "header_schema": { "type": "object", "required": ["Content-Type"], "properties": { "Content-Type": { "type": "string", "pattern": "^application\/x-www-form-urlencoded$" } } }, "body_schema": { "type": "object", "required": ["required_payload","enum_payload"], "properties": { "required_payload": {"type": "string"}, "enum_payload": { "type": "string", "enum": ["enum_string_1", "enum_string_2"], "default": "enum_string_1" } } } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request with URL-encoded form data to verify: curl -i "http://127.0.0.1:9080/post" -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "required_payload=hello&enum_payload=enum_string_1" You should receive an `HTTP/1.1 400 Bad Request` response similar to the following: { "args": {}, "data": "", "files": {}, "form": { "enum_payload": "enum_string_1", "required_payload": "hello" }, "headers": { ... }, "json": null, "origin": "127.0.0.1, 183.17.233.107", "url": "http://127.0.0.1/post"} Send a request without the URL-encoded field `enum_payload`: curl -i "http://127.0.0.1:9080/post" -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "required_payload=hello" You should receive an `HTTP/1.1 400 Bad Request` of the following: property "enum_payload" is required Appendix: JSON Schema[​](https://docs.api7.ai/hub/request-validation/#appendix-json-schema "Direct link to Appendix: JSON Schema") ----------------------------------------------------------------------------------------------------------------------------------- The following section provides boilerplate JSON schema for you to adjust, combine, and use with this plugin. For a complete reference, see [JSON schema specification](https://json-schema.org/specification) . ### Enumerated Values[​](https://docs.api7.ai/hub/request-validation/#enumerated-values "Direct link to Enumerated Values") { "body_schema": { "type": "object", "required": ["enum_payload"], "properties": { "enum_payload": { "type": "string", "enum": ["enum_string_1", "enum_string_2"], "default": "enum_string_1" } } }} ### Boolean Values[​](https://docs.api7.ai/hub/request-validation/#boolean-values "Direct link to Boolean Values") { "body_schema": { "type": "object", "required": ["bool_payload"], "properties": { "bool_payload": { "type": "boolean", "default": true } } }} ### Numeric Values[​](https://docs.api7.ai/hub/request-validation/#numeric-values "Direct link to Numeric Values") { "body_schema": { "type": "object", "required": ["integer_payload"], "properties": { "integer_payload": { "type": "integer", "minimum": 1, "maximum": 65535 } } }} ### Strings[​](https://docs.api7.ai/hub/request-validation/#strings "Direct link to Strings") { "body_schema": { "type": "object", "required": ["string_payload"], "properties": { "string_payload": { "type": "string", "minLength": 1, "maxLength": 32 } } }} ### RegEx for Strings[​](https://docs.api7.ai/hub/request-validation/#regex-for-strings "Direct link to RegEx for Strings") { "body_schema": { "type": "object", "required": ["regex_payload"], "properties": { "regex_payload": { "type": "string", "minLength": 1, "maxLength": 32, "pattern": "[[^[a-zA-Z0-9_]+$]]" } } }} ### Arrays[​](https://docs.api7.ai/hub/request-validation/#arrays "Direct link to Arrays") { "body_schema": { "type": "object", "required": ["array_payload"], "properties": { "array_payload": { "type": "array", "minItems": 1, "items": { "type": "integer", "minimum": 200, "maximum": 599 }, "uniqueItems": true, "default": [200, 302] } } }} * [Examples](https://docs.api7.ai/hub/request-validation/#examples) * [Validate Request Header](https://docs.api7.ai/hub/request-validation/#validate-request-header) * [Customize Rejection Message and Status Code](https://docs.api7.ai/hub/request-validation/#customize-rejection-message-and-status-code) * [Validate Request Body](https://docs.api7.ai/hub/request-validation/#validate-request-body) * [Appendix: JSON Schema](https://docs.api7.ai/hub/request-validation/#appendix-json-schema) * [Enumerated Values](https://docs.api7.ai/hub/request-validation/#enumerated-values) * [Boolean Values](https://docs.api7.ai/hub/request-validation/#boolean-values) * [Numeric Values](https://docs.api7.ai/hub/request-validation/#numeric-values) * [Strings](https://docs.api7.ai/hub/request-validation/#strings) * [RegEx for Strings](https://docs.api7.ai/hub/request-validation/#regex-for-strings) * [Arrays](https://docs.api7.ai/hub/request-validation/#arrays) --- # AI Request Rewrite | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ai-request-rewrite/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ai-request-rewrite` plugin processes client requests by forwarding them to LLM services for transformation before relaying them to upstream services. This enables LLM-powered modifications such as data redaction, content enrichment, or reformatting. The plugin supports the integration with OpenAI, DeepSeek, Gemini, Vertex AI, Anthropic, OpenRouter, and other OpenAI-compatible APIs. Examples[​](https://docs.api7.ai/hub/ai-request-rewrite/#examples "Direct link to Examples") --------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `ai-request-rewrite` for different scenarios. The examples will use OpenAI as the LLM service. To follow along, obtain the OpenAI [API key](https://openai.com/blog/openai-api) and save it to an environment variable: export OPENAI_API_KEY=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 # replace with your API key ### Redact Sensitive Information[​](https://docs.api7.ai/hub/ai-request-rewrite/#redact-sensitive-information "Direct link to Redact Sensitive Information") The following example demonstrates how you can use the `ai-request-rewrite` plugin to redact sensitive information before the request reaches the upstream service. * Admin API * ADC * Ingress Controller Create a route and configure the `ai-request-rewrite` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-request-rewrite-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-request-rewrite": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options":{ "model": "gpt-4" }, "prompt": "Given a JSON request body, identify and mask any sensitive information such as credit card numbers, social security numbers, and personal identification numbers (e.g., passport or driver'\''s license numbers). Replace detected sensitive values with a masked format (e.g., \"*** **** **** 1234\") for credit card numbers. Ensure the JSON structure remains unchanged." } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify what information to redact before the request reaches the upstream service. Create a route with the `ai-request-rewrite` plugin configured as such: adc.yaml services: - name: ai-request-rewrite-service routes: - name: ai-request-rewrite-route uris: - /anything methods: - POST plugins: ai-request-rewrite: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 prompt: "Given a JSON request body, identify and mask any sensitive information such as credit card numbers, social security numbers, and personal identification numbers (e.g., passport or driver's license numbers). Replace detected sensitive values with a masked format (e.g., \"*** **** **** 1234\") for credit card numbers. Ensure the JSON structure remains unchanged." upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify what information to redact before the request reaches the upstream service. * Gateway API * APISIX CRD Create a route with the `ai-request-rewrite` plugin configured as such: ai-request-rewrite-ic.yaml apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-request-rewrite-plugin-configspec: plugins: - name: ai-request-rewrite config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 prompt: "Given a JSON request body, identify and mask any sensitive information such as credit card numbers, social security numbers, and personal identification numbers (e.g., passport or driver's license numbers). Replace detected sensitive values with a masked format (e.g., \"*** **** **** 1234\") for credit card numbers. Ensure the JSON structure remains unchanged."---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: ai-request-rewrite-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-request-rewrite-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f ai-request-rewrite-ic.yaml ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify what information to redact before the request reaches the upstream service. Create a route with the `ai-request-rewrite` plugin configured as such: ai-request-rewrite-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: ai-request-rewrite-routespec: ingressClassName: apisix http: - name: ai-request-rewrite-route match: paths: - /anything methods: - POST upstreams: - name: httpbin-external-domain plugins: - name: ai-request-rewrite enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 prompt: "Given a JSON request body, identify and mask any sensitive information such as credit card numbers, social security numbers, and personal identification numbers (e.g., passport or driver's license numbers). Replace detected sensitive values with a masked format (e.g., \"*** **** **** 1234\") for credit card numbers. Ensure the JSON structure remains unchanged." Apply the configuration to your cluster: kubectl apply -f ai-request-rewrite-ic.yaml ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify what information to redact before the request reaches the upstream service. Send a POST request to the route with some personally identifiable information: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "content": "John said his debit card number is 4111 1111 1111 1111 and SIN is 123-45-6789." }' You should receive a response similar to the following: { "args": {}, "data": "{\n \"content\": \"John said his debit card number is **** **** **** 1111 and SIN is ***-**-****.\"\n }" ..., "json": { "messages": [ { "content": "Client information from customer service calls", "role": "system" }, { "content": "John said his debit card number is **** **** **** 1111 and SIN is ***-**-****." "role": "user" } ], "model": "openai" }, "method": "POST", "origin": "192.168.97.1, 103.97.2.170", "url": "http://127.0.0.1/anything"} ### Reformat Data[​](https://docs.api7.ai/hub/ai-request-rewrite/#reformat-data "Direct link to Reformat Data") The following example demonstrates how you can use the `ai-request-rewrite` plugin to reformat data before the request reaches the upstream service. * Admin API * ADC * Ingress Controller Create a route and configure the `ai-request-rewrite` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-request-rewrite-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-request-rewrite": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options":{ "model": "gpt-4" }, "prompt": "Convert natural language queries into structured JSON format with intent and extracted parameters." } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify how to reformat before the request reaches the upstream service. Create a route with the `ai-request-rewrite` plugin configured as such: adc.yaml services: - name: ai-request-rewrite-service routes: - name: ai-request-rewrite-route uris: - /anything methods: - POST plugins: ai-request-rewrite: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 prompt: "Convert natural language queries into structured JSON format with intent and extracted parameters." upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify how to reformat before the request reaches the upstream service. * Gateway API * APISIX CRD Create a route with the `ai-request-rewrite` plugin configured as such: ai-request-rewrite-ic.yaml apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-request-rewrite-plugin-configspec: plugins: - name: ai-request-rewrite config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 prompt: "Convert natural language queries into structured JSON format with intent and extracted parameters."---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: ai-request-rewrite-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-request-rewrite-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f ai-request-rewrite-ic.yaml ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify how to reformat before the request reaches the upstream service. Create a route with the `ai-request-rewrite` plugin configured as such: ai-request-rewrite-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: ai-request-rewrite-routespec: ingressClassName: apisix http: - name: ai-request-rewrite-route match: paths: - /anything methods: - POST upstreams: - name: httpbin-external-domain plugins: - name: ai-request-rewrite enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 prompt: "Convert natural language queries into structured JSON format with intent and extracted parameters." Apply the configuration to your cluster: kubectl apply -f ai-request-rewrite-ic.yaml ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify how to reformat before the request reaches the upstream service. Send a POST request to the route with some personally identifiable information: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "content": "Book a flight from NYC to LA on April 10, 2022." }' You should receive a response similar to the following: { "args": {}, "data": "{\n \"intent\": \"BookFlight\",\n \"parameters\": {\n \"origin\": \"NYC\",\n \"destination\": \"LA\",\n \"date\": \"2022-04-10\"\n }\n}", ..., "json": { "intent": "BookFlight", "parameters": { "date": "2022-04-10", "destination": "LA", "origin": "NYC" } }, "method": "POST", "origin": "192.168.97.1, 103.97.2.167", "url": "http://127.0.0.1/anything"} ### Summarize Information[​](https://docs.api7.ai/hub/ai-request-rewrite/#summarize-information "Direct link to Summarize Information") The following example demonstrates how you can use the `ai-request-rewrite` plugin to summarize information before the request reaches the upstream service. * Admin API * ADC * Ingress Controller Create a route and configure the `ai-request-rewrite` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-request-rewrite-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-request-rewrite": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options":{ "model": "gpt-4" }, "prompt": "Summarize lengthy input while preserving key details. Ensure the summary remains concise and informative." } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify the requirements to summarize information before the request reaches the upstream service. Create a route with the `ai-request-rewrite` plugin configured as such: adc.yaml services: - name: ai-request-rewrite-service routes: - name: ai-request-rewrite-route uris: - /anything methods: - POST plugins: ai-request-rewrite: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 prompt: "Summarize lengthy input while preserving key details. Ensure the summary remains concise and informative." upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify the requirements to summarize information before the request reaches the upstream service. * Gateway API * APISIX CRD Create a route with the `ai-request-rewrite` plugin configured as such: ai-request-rewrite-ic.yaml apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-request-rewrite-plugin-configspec: plugins: - name: ai-request-rewrite config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 prompt: "Summarize lengthy input while preserving key details. Ensure the summary remains concise and informative."---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: ai-request-rewrite-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-request-rewrite-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f ai-request-rewrite-ic.yaml ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify the requirements to summarize information before the request reaches the upstream service. Create a route with the `ai-request-rewrite` plugin configured as such: ai-request-rewrite-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: ai-request-rewrite-routespec: ingressClassName: apisix http: - name: ai-request-rewrite-route match: paths: - /anything methods: - POST upstreams: - name: httpbin-external-domain plugins: - name: ai-request-rewrite enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 prompt: "Summarize lengthy input while preserving key details. Ensure the summary remains concise and informative." Apply the configuration to your cluster: kubectl apply -f ai-request-rewrite-ic.yaml ❶ Specify the provider to be `openai`. ❷ Attach OpenAI API key in the `Authorization` header. ❸ Specify the name of the model. ❹ Specify the requirements to summarize information before the request reaches the upstream service. Send a POST request to the route with some personally identifiable information: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "content": "Hey! So, I’m planning a trip to Japan next spring for about three weeks, and I want to visit Tokyo, Kyoto, and Osaka, but I’m not sure how to split my time between them. I really love history and cultural sites, so temples and shrines are a must. I’m also a big foodie, especially into ramen and sushi, so I’d love recommendations on the best spots. I prefer quieter areas for accommodation, but I don’t mind traveling into busy areas for sightseeing. Oh, and I’d also like to do a day trip somewhere outside these cities—maybe Hakone or Nara? I heard the cherry blossoms might still be in bloom in early April, so I’d love to catch them if possible. Also, what’s the best way to get around—should I get a JR Pass, or would individual tickets be better? Thanks!" }' You should receive a response similar to the following: { "args": {}, "data": "The individual is planning a three-week trip to Japan in the spring, looking to visit Tokyo, Kyoto, and Osaka. They are interested in history, culture, temples, and shrines. They love ramen and sushi, so are seeking food recommendations. Accommodation should be in quieter areas, but they are open to busy sites for sightseeing. Along with these cities, they plan to make a day trip to either Hakone or Nara, hoping to see the cherry blossoms in early April. The best transport method between buying the JR Pass or individual tickets is also a query.", ..., "method": "POST", "origin": "192.168.97.1, 103.97.2.171", "url": "http://127.0.0.1/anything"} * [Examples](https://docs.api7.ai/hub/ai-request-rewrite/#examples) * [Redact Sensitive Information](https://docs.api7.ai/hub/ai-request-rewrite/#redact-sensitive-information) * [Reformat Data](https://docs.api7.ai/hub/ai-request-rewrite/#reformat-data) * [Summarize Information](https://docs.api7.ai/hub/ai-request-rewrite/#summarize-information) --- # Datadog | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/datadog/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `datadog` plugin supports the integration with [Datadog](https://www.datadoghq.com/) , one of the most used observability service for cloud applications. When enabled, the plugin pushes metrics to [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/?tab=hostagent) server, which comes bundled with the [Datadog agent](https://docs.datadoghq.com/agent) , over UDP protocol. Metrics[​](https://docs.api7.ai/hub/datadog/#metrics "Direct link to Metrics") ------------------------------------------------------------------------------- The plugin exports the following metrics by default. All metrics will be prefixed by the `namespace` configured in metadata. For example, if the `namespace` is configured to be `apisix`, you will see the `request.counter` metric exported as `apisix.request.counter` in Datadog. | Name | Type | Description | | --- | --- | --- | | request.counter | counter | Number of requests received. | | request.latency | histogram | Time taken to process the request, in milliseconds. | | upstream.latency | histogram | Time taken to proxy the request to the upstream server until a response is received, in milliseconds. | | apisix.latency | histogram | Time taken by APISIX agent to process the request, in milliseconds. | | ingress.size | timer | Request body size in bytes. | | egress.size | timer | Response body size in bytes. | Tags[​](https://docs.api7.ai/hub/datadog/#tags "Direct link to Tags") ---------------------------------------------------------------------- The plugin exports metrics with the following [tags](https://docs.datadoghq.com/getting_started/tagging) . When there are no suitable values for any particular tag, the tag will be omitted. | Name | Description | | --- | --- | | route\_name | Name of the route. If not present or if the attribute `prefer_name` is set to false, fall back to the route ID. | | service\_name | Name of the service. If not present or if the attribute `prefer_name` is set to false, fall back to the service ID. | | consumer | Username of the consumer if the route is connected to a consumer. | | balancer\_ip | IP address of the upstream balancer that processes the current request. | | response\_status | HTTP response status code, such as `201`, `404`, or `503`. | | response\_status\_class | HTTP response status code class, such as `2xx`, `4xx`, or `5xx`. Available in APISIX from version 3.14.0 and API7 Enterprise from version 3.9.0. | | scheme | Request scheme, such as HTTP and gRPC. | | path | HTTP path pattern. Only available if the parameter `include_path` is set to `true`. Available in APISIX from version 3.14.0 and API7 Enterprise from version 3.9.0. | | method | HTTP method. Only available if the attribute `include_method` is set to true. Available in APISIX from version 3.14.0 and API7 Enterprise from version 3.9.0. | Examples[​](https://docs.api7.ai/hub/datadog/#examples "Direct link to Examples") ---------------------------------------------------------------------------------- The examples below demonstrate how you can configure `datadog` plugin for different scenarios. Before proceeding, please make sure you have installed [Datadog agent](https://docs.datadoghq.com/agent) which collects events and metrics from monitored objects and sends them to Datadog. Start the Datadog agent in Docker: docker run -d \ --name dogstatsd-agent \ -e DD_API_KEY=35ebe12345678dec56218930b79fdb4cf \ -e DD_SITE="us5.datadoghq.com" \ -e DD_HOSTNAME=apisix.quickstart \ -e DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true \ -p 8125:8125/udp \ datadog/dogstatsd:latest ❶ `DD_API_KEY`: replace with your API key. ❷ `DD_SITE`: replace with your Datadog site. ❸ `DD_HOSTNAME`: replace with your hostname. ❹ `DD_DOGSTATSD_NON_LOCAL_TRAFFIC`: set to true to listen to DogStatsD packets from other containers. You can configure most options in the agent’s main configuration file `datadog.yaml` through environment variables, prefixed with `DD_`. For more information, see [agent environment variables](https://docs.datadoghq.com/agent/guide/environment-variables) . ### Update Datadog Agent Address and Other Metadata[​](https://docs.api7.ai/hub/datadog/#update-datadog-agent-address-and-other-metadata "Direct link to Update Datadog Agent Address and Other Metadata") By default, the plugin expects the DogStatsD server to be available at `127.0.0.1:8125`. To customize the address and other metadata, update the [plugin metadata](https://docs.api7.ai/hub/datadog/configuration#metadata) as such: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/datadog" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "host": "192.168.0.90", "port": 8125, "namespace": "apisix", "constant_tags": [ "source:apisix", "service:custom" ] }' ❶ Replace with your private IP address if you are running both APISIX and Datadog agent in Docker. ❷ Set to Datadog agent listening port. ❸ Set namespace which prefixes all metrics. ❹ Configure constant tags. To reset to default configuration, send a request to the `datadog` plugin metadata with an empty body: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/datadog" -X PUT -d '{}' ### Monitor Route Metrics[​](https://docs.api7.ai/hub/datadog/#monitor-route-metrics "Direct link to Monitor Route Metrics") The example below shows how you can send the metrics of a particular route to Datadog. Create a route with the `datadog` plugin and a few optional configuration options: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "datadog-route", "uri": "/anything", "plugins": { "datadog": { "batch_max_size" : 1, "max_retry_count": 0 } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ `batch_max_size`: set to 1 to send the metric immediately. ❷ `max_retry_count`: set to 0 to disallow retries if metrics were unsuccessfully sent. Generate a few requests to the previously created route: curl "http://127.0.0.1:9080/anything" In Datadog, Select **Metrics** from the left menu and go to **Explorer**. Select `apisix.ingress.size.count` as the metric. You should see the count reflecting the number of requests generated: ![apisix-datadog-ingress-size-count](https://static.api7.ai/uploads/2024/01/17/Y0uHlIeS_dd-count.png) * [Metrics](https://docs.api7.ai/hub/datadog/#metrics) * [Tags](https://docs.api7.ai/hub/datadog/#tags) * [Examples](https://docs.api7.ai/hub/datadog/#examples) * [Update Datadog Agent Address and Other Metadata](https://docs.api7.ai/hub/datadog/#update-datadog-agent-address-and-other-metadata) * [Monitor Route Metrics](https://docs.api7.ai/hub/datadog/#monitor-route-metrics) --- # Secret Providers | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page As a critical infrastructure component, API gateways interact with various upstream services and handle API traffic, which may involve sensitive data such as passwords, tokens, and SSL certificates. This sensitive information must be properly stored, encrypted, rotated, and retired to ensure system security. Users can store and manage these secrets directly in API7 Enterprise, which will encrypt and store them according to [FIPS standards](https://docs.api7.ai/apisix/enterprise-feature/apisix/enterprise-feature/compliance#1-fips-140-2) . Alternatively, they can store them in third-party secrets managers like HashiCorp Vault or AWS Secrets Manager, referencing them in API7 Enterprise via variables. Why Use Secret Providers[​](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#why-use-secret-providers "Direct link to Why Use Secret Providers") ----------------------------------------------------------------------------------------------------------------------------------------------------------------- API7 Enterprise employs Secret Providers to decouple secret storage and management from API gateways, thus enhancing overall security. Currently, secret providers integrate with HashiCorp Vault and AWS Secrets Manager. More popular secret managers, like GCP Secret Manager, will be supported in future releases. A secret provider object contains connection and authentication information for a specific secret manager: ![Secret Provider Configuration](https://static.api7.ai/uploads/2024/12/05/u49Cs1CU_secret-providers-1.png) Users can store secrets such as credentials used for consumer authentication or SSL certificates used during HTTPS requests in the secret manager. These secrets are then integrated and referenced in API7 Enterprise via secret providers. This setup allows API7 Enterprise to securely reference the corresponding secrets, such as the key needed to authenticate a consumer. ![Reference Secret from AWS Secrets Manager](https://static.api7.ai/uploads/2024/12/05/STJu4ZzU_secret-providers-2.png) The diagram below illustrates the consumer authentication flow, where secrets are stored in the secret provider, and API7 Enterprise retrieves secrets from the secret providers after connecting and authenticating with them. This process decouples the secrets from the API gateway. ![Diagram of Retrieving Secrets from Secret Providers](https://static.api7.ai/uploads/2024/12/06/fHE6vTIf_secret-providers.png) Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#key-features "Direct link to Key Features") ----------------------------------------------------------------------------------------------------------------------------- * Store sensitive information using third-party secret providers, decoupling secrets from API gateways and enhancing security. * Reference externally stored secrets as variables, simplifying configuration management. * Support multiple secret providers to accommodate complex enterprise architectures and unify secret management. * Precisely control access to sensitive information, preventing unauthorized usage and data breaches. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#use-cases "Direct link to Use Cases") -------------------------------------------------------------------------------------------------------------------- ### Enhanced Data Security[​](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#enhanced-data-security "Direct link to Enhanced Data Security") Managing large volumes of sensitive information is a significant challenge for many enterprises. API7 Enterprise offers a secure solution by integrating with third-party secret providers such as HashiCorp Vault and AWS Secrets Manager. Enterprises can store sensitive data on these third-party platforms and reference them as variables within API7 Enterprise, decoupling API gateway and secret management. This prevents sensitive information from being exposed in system configurations, greatly enhancing data security. Additionally, centralizing sensitive data and keys on a dedicated management platform improves efficiency, enables audit tracking, and allows flexible access control. This helps enterprises effectively address complex security challenges. ### Transparent Key Rotation for API Gateways[​](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#transparent-key-rotation-for-api-gateways "Direct link to Transparent Key Rotation for API Gateways") Before implementing the dynamic key retrieval mechanism, enterprises typically managed sensitive information statically by hardcoding keys and other sensitive data in configuration files for access. In API7 Enterprise, dynamic key retrieval is achieved by specifying the path for key access. As long as the key storage path remains the same, any changes to the key will not affect its usage in API7 Enterprise. This approach eliminates the need for manual key management, reducing errors, simplifying key management, and significantly improving security. ### Audit and Compliance[​](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#audit-and-compliance "Direct link to Audit and Compliance") Using keys and sensitive information typically needs to comply with industry standards and regulatory requirements. After integrating with third-party secrets managers, API7 Enterprise can record detailed access logs and audit information for secret provider resources, such as creating, editing, or deleting secret providers. This helps enterprises with compliance checks and security analysis. Through audit tracking, businesses can promptly detect abnormal access behavior and take appropriate protective measures. The reference relationships for variables in consumer credentials can be viewed in the reference list of the secret provider on the API7 dashboard. When editing or deleting a secret provider, reference checks are performed to prevent configuration errors due to invalid variable references. * [Why Use Secret Providers](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#why-use-secret-providers) * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#key-features) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#use-cases) * [Enhanced Data Security](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#enhanced-data-security) * [Transparent Key Rotation for API Gateways](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#transparent-key-rotation-for-api-gateways) * [Audit and Compliance](https://docs.api7.ai/apisix/enterprise-feature/secret-providers/#audit-and-compliance) --- # Serverless Functions | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/serverless-functions/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The serverless functions consist of two plugins, `serverless-pre-function` and `serverless-post-function`. These plugins enable the execution of user-defined logic at the beginning and end of the [execution phases](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) the functions hook to. Tips for Writing Functions[​](https://docs.api7.ai/hub/serverless-functions/#tips-for-writing-functions "Direct link to Tips for Writing Functions") ----------------------------------------------------------------------------------------------------------------------------------------------------- Only Lua functions are allowed in the serverless plugins and not other Lua code. For example, anonymous functions are legal: return function() ngx.log(ngx.ERR, 'one')end Closures are also legal: local count = 1return function() count = count + 1 ngx.say(count)end But code other than functions are illegal: local count = 1ngx.say(count) Examples[​](https://docs.api7.ai/hub/serverless-functions/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure the `serverless-pre-function` and `serverless-post-function` plugins for different scenarios. ### Log Information before and after a Phase[​](https://docs.api7.ai/hub/serverless-functions/#log-information-before-and-after-a-phase "Direct link to Log Information before and after a Phase") The example below demonstrates how you can configure the serverless plugins to execute custom logics to log information to error logs before and after the `rewrite` [phase](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) . Create a route as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "id": "serverless-pre-route", "uri": "/anything", "plugins": { "serverless-pre-function": { "phase": "rewrite", "functions" : [ "return function() ngx.log(ngx.ERR, \"serverless pre function\"); end" ] }, "serverless-post-function": { "phase": "rewrite", "functions" : [ "return function(conf, ctx) ngx.log(ngx.ERR, \"match uri \", ctx.curr_req_matched and ctx.curr_req_matched._path); end" ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Hook the serverless pre-function logic to the `rewrite` [phase](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) . ❷ Define a Lua function that logs a message of `serverless pre function` in the error log. ❸ Hook the serverless post-function logic to the `rewrite` [phase](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) . ❹ Define a Lua function that logs the matched URI in the error log. `conf` and `ctx` can be passed as the first two arguments like other plugins, where `conf` is the plugin configurations and `ctx` is the request context. Send the request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response and see the following entries in the error log: 2024/05/09 15:07:09 [error] 51#51: *3963 [lua] [string "return function() ngx.log(ngx.ERR, "serverles..."]:1: func(): serverless pre function, client: 172.21.0.1, server: _, request: "GET /test HTTP/1.1", host: "127.0.0.1:9080"2024/05/09 15:16:58 [error] 50#50: *9343 [lua] [string "return function(conf, ctx) ngx.log(ngx.ERR, "..."]:1: func(): match uri /test, client: 172.21.0.1, server: _, request: "GET /test HTTP/1.1", host: "127.0.0.1:9080" The first entry is added by the pre-function and the second entry is added by the post-function. ### Register Custom Variables[​](https://docs.api7.ai/hub/serverless-functions/#register-custom-variables "Direct link to Register Custom Variables") The example below demonstrates how you can register [custom built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables/) using the serverless plugins and use the newly created variable in logs. Start an example rsyslog server in Docker: docker run -d -p 514:514 --name example-rsyslog-server rsyslog/syslog_appliance_alpine Create a [service](https://docs.api7.ai/apisix/key-concepts/services) with a serverless function to register a custom variable `a6_route_labels`, enable a logging plugin to later log the custom variable, and configure an upstream: curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "id":"srv_custom_var", "plugins": { "serverless-pre-function": { "phase": "rewrite", "functions": [ "return function() local core = require \"apisix.core\" core.ctx.register_var(\"a6_route_labels\", function(ctx) local route = ctx.matched_route and ctx.matched_route.value if route and route.labels then return route.labels end return nil end); end" ] }, "syslog": { "host" : "172.0.0.1", "port" : 514, "flush_limit" : 1 } }, "upstream": { "nodes": { "httpbin.org:80": 1 } } }' ❶ `functions`: register a custom variable `a6_route_labels` and fetch the variable value from the matched route's `labels` property. ❷ `host` and `port`: replace with the address of your syslog server. ❸ `flush_limit`: set to 1 to push log to the syslog server immediately. Next, update the log format for all `syslog` instances with the new variable by configuring the [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) : curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/syslog" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "log_format": { "host": "$host", "client_ip": "$remote_addr", "labels": "$a6_route_labels" } }' ❶ `$host` and `$remote_addr`: [NGINX variables](https://docs.api7.ai/hub/serverless-functions/#nginx-variables) . ❷ `$a6_route_labels`: custom variable. Finally, create a route in the service: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "id":"route_custom_var", "uri":"/get", "service_id": "srv_custom_var", "labels": { "key": "test_a6_route_labels" }}' ❶ `service_id`: correspond to the previously created service. ❷ `labels`: route information to be logged with the custom variable. To verify the variable registration, send a request to the route: curl "http://127.0.0.1:9080/get" You should see a log entry in your syslog server similar to the following: { "host":"127.0.0.1", "route_id":"route_custom_var", "client_ip":"172.19.0.1", "labels":{ "key":"test_a6_route_labels" }, "service_id":"srv_custom_var"} This verifies the custom variable was registered and it logs the `labels` information in a route successfully. ### Modify a Specific Field in Response Body[​](https://docs.api7.ai/hub/serverless-functions/#modify-a-specific-field-in-response-body "Direct link to Modify a Specific Field in Response Body") The example below demonstrates how you can use the serverless plugins to remove a specific field from a JSON response body. Before proceeding with the removal, first configure a route as follows to see the unmodified response: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "id":"serverless-remove-body-info", "uri": "/get", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Send a request to the route: curl "http://127.0.0.1:9080/get" You should see a response similar to the following with your host and proxy's IP information: { "args": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.4.0", "X-Amzn-Trace-Id": "Root=1-663db30f-51448a1b635f2f4338a4fcfc", "X-Forwarded-Host": "127.0.0.1" }, "origin": "172.19.0.1, 123.456.122.90", "url": "http://127.0.0.1/get"} To remove the `origin` field from the response, update the route with serverless plugins: curl "http://127.0.0.1:9180/apisix/admin/routes/serverless-remove-body-info" -X PATCH \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "plugins": { "serverless-pre-function": { "phase": "header_filter", "functions" : [ "return function(conf, ctx) local core = require(\"apisix.core\") core.response.clear_header_as_body_modified() end" ] }, "serverless-post-function": { "phase": "body_filter", "functions" : [ "return function(conf, ctx) local cjson = require(\"cjson\") local core = require(\"apisix.core\") local body = core.response.hold_body_chunk(ctx) if not body then return end body = cjson.decode(body) body.origin = nil body = cjson.encode(body) ngx.arg[1] = body end" ] } } }' ❶ Execute a pre-function in the `header_filter` [phase](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) . ❷ Use `clear_header_as_body_modified` method to clear body-related response headers such as `Content-Length` to help with response modification. ❸ Execute a post-function in the `body_filter` [phase](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) . ❹ Use `hold_body_chunk` method to collect the response body. ❺ Decode the JSON response body. ❻ Set the `origin` field to `nil` to remove the field. Send another request to the route: curl "http://127.0.0.1:9080/get" You should see a response without the `origin` information: { "url":"http://127.0.0.1/get", "args":{}, "headers":{ "X-Forwarded-Host":"127.0.0.1", "Host":"127.0.0.1", "Accept":"*/*", "User-Agent":"curl/8.4.0", "X-Amzn-Trace-Id":"Root=1-663db276-1c15276864294d963c6e1755" }} For simpler response modifications, such as modifying HTTP status codes, request headers, or the entire response body, please use the [`response-rewrite`](https://docs.api7.ai/hub/response-rewrite) plugin. * [Tips for Writing Functions](https://docs.api7.ai/hub/serverless-functions/#tips-for-writing-functions) * [Examples](https://docs.api7.ai/hub/serverless-functions/#examples) * [Log Information before and after a Phase](https://docs.api7.ai/hub/serverless-functions/#log-information-before-and-after-a-phase) * [Register Custom Variables](https://docs.api7.ai/hub/serverless-functions/#register-custom-variables) * [Modify a Specific Field in Response Body](https://docs.api7.ai/hub/serverless-functions/#modify-a-specific-field-in-response-body) --- # Graphql Limit Count | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/graphql-limit-count/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `graphql-limit-count` plugin uses a fixed window algorithm to limit the rate of GraphQL requests based on the depth of the GraphQL [queries](https://graphql.org/learn/queries/) or [mutations](https://graphql.org/learn/queries#mutations) . In GraphQL, the depth refers to the number of nesting levels in a query or mutation. The following is an example query with a depth of 3: { a { b { c } }} The `graphql-limit-count` plugin rate limits by a quota of depth within a given time interval. For example, if the quota of count is set to 4 within a 30-second interval, requests with a depth of 3 will be allowed. The remaining quota within the same 30-second is 1. If a request of depth 2 is sent within the same 30-second interval, it will be rejected. Local vs Redis Rate Limiting[​](https://docs.api7.ai/hub/graphql-limit-count/#local-vs-redis-rate-limiting "Direct link to Local vs Redis Rate Limiting") ---------------------------------------------------------------------------------------------------------------------------------------------------------- The `graphql-limit-count` plugin supports two modes of rate limiting: * **Local rate limiting**: Limits are enforced independently on each gateway instance. Each instance maintains its own counters, so the effective limit is roughly (limit × number of instances) when traffic is spread across instances. This is the default when no `policy` is set or when `policy` is `local`. * **Redis-based rate limiting**: Limits are shared across all gateway instances through Redis. All instances share the same quota, so the configured limit applies to all gateway instances. Examples[​](https://docs.api7.ai/hub/graphql-limit-count/#examples "Direct link to Examples") ---------------------------------------------------------------------------------------------- The examples below use [GitHub GraphQL API](https://docs.github.com/en/graphql) endpoint as an upstream and demonstrate how you can configure `graphql-limit-count` for different scenarios. To follow along, create a GitHub [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with the appropriate scopes for the resources you want to interact with. ### Apply Rate Limiting by Remote Address[​](https://docs.api7.ai/hub/graphql-limit-count/#apply-rate-limiting-by-remote-address "Direct link to Apply Rate Limiting by Remote Address") The following example demonstrates the rate limiting of GraphQL requests by a single variable, `remote_addr`. Create a route with `graphql-limit-count` plugin that allows for a quota of depth 2 within a 30-second window per remote address: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-limit-count-route", "uri": "/graphql", "plugins": { "graphql-limit-count": { "count": 2, "time_window": 30, "rejected_code": 429, "key_type": "var", "key": "remote_addr", "policy": "local" } }, "upstream": { "type": "roundrobin", "pass_host": "node", "scheme": "https", "nodes": { "api.github.com:443": 1 } } }' #### Verify with GraphQL Query[​](https://docs.api7.ai/hub/graphql-limit-count/#verify-with-graphql-query "Direct link to Verify with GraphQL Query") Send a request with a GraphQL query of depth 2 to verify: curl -i "http://127.0.0.1:9080/graphql" -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -d '{"query": "query {viewer{login}}"}' You should see an `HTTP/1.1 200 OK` response with the corresponding response body. The request has consumed all the quota allowed for the time window. If you send the request again within the same 30-second time interval, you should receive an `HTTP/1.1 429 Too Many Requests` response, indicating the request surpasses the quota threshold. #### Verify with GraphQL Mutation[​](https://docs.api7.ai/hub/graphql-limit-count/#verify-with-graphql-mutation "Direct link to Verify with GraphQL Mutation") You can also send a request with a GraphQL mutation of depth 3 to verify: curl -i "http://127.0.0.1:9080/graphql" -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -d '{"query": "mutation AddReactionToIssue {addReaction(input:{subjectId:\"MDU6SXNzdWUyMzEzOTE1NTE=\",content:HOORAY}) {reaction {content} subject {id}}}"}' You should see an `HTTP/1.1 429 Too Many Requests` response at any time, as depth 3 always surpasses the quota of depth 2. ### Apply Rate Limiting by Remote Address and Consumer Name[​](https://docs.api7.ai/hub/graphql-limit-count/#apply-rate-limiting-by-remote-address-and-consumer-name "Direct link to Apply Rate Limiting by Remote Address and Consumer Name") The following example demonstrates the rate limiting of GraphQL requests by a combination of variables, `remote_addr` and `consumer_name`. It allows for a quota of depth 2 within a 30-second window per remote address and for each [consumer](https://docs.api7.ai/apisix/key-concepts/consumers) . Create a consumer `john`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Create a second consumer `jane`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jane" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-key-auth", "plugins": { "key-auth": { "key": "jane-key" } } }' Create a route with `key-auth` and `graphql-limit-count` plugins: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-limit-count-route", "uri": "/graphql", "plugins": { "key-auth": {}, "graphql-limit-count": { "count": 2, "time_window": 30, "rejected_code": 429, "policy": "local", "key_type": "var_combination", "key": "$remote_addr $consumer_name" } }, "upstream": { "type": "roundrobin", "pass_host": "node", "scheme": "https", "nodes": { "api.github.com:443": 1 } } }' ❶ `key-auth`: enable key authentication on the route. ❷ `key_type`: set to `var_combination` to interpret the `key` as a combination of variables. ❸ `key`: set to `$remote_addr $consumer_name` to apply rate limiting quota by remote address and consumer. Send a request with a GraphQL query of depth 2 as the consumer `jane`: curl -i "http://127.0.0.1:9080/graphql" -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -H 'apikey: jane-key' \ -d '{"query": "query {viewer{login}}"}' You should see an `HTTP/1.1 200 OK` response with the corresponding response body. This request has consumed all the quota set for the time window. If you send the same request as the consumer `jane` within the same 30-second time interval, you should receive an `HTTP/1.1 429 Too Many Requests` response, indicating the request surpasses the quota threshold. Send the same request as the consumer `john` within the same 30-second time interval: curl -i "http://127.0.0.1:9080/graphql" -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -H 'apikey: john-key' \ -d '{"query": "query {viewer{login}}"}' You should see an `HTTP/1.1 200 OK` response with the corresponding response body, indicating the request is not rate limited. Send the same request as the consumer `john` again within the same 30-second time interval, you should receive an `HTTP/1.1 429 Too Many Requests` response. This verifies the plugin rate limits by the combination of variables, `remote_addr` and `consumer_name`. ### Share Quota among Routes[​](https://docs.api7.ai/hub/graphql-limit-count/#share-quota-among-routes "Direct link to Share Quota among Routes") The following example demonstrates the sharing of GraphQL rate limiting quota among multiple routes by configuring the `group` of the `graphql-limit-count` plugin. Note that the configurations of the `graphql-limit-count` plugin of the same `group` should be identical. To avoid update anomalies and repetitive configurations, you can create a [service](https://docs.api7.ai/apisix/key-concepts/services) with `graphql-limit-count` plugin and upstream for routes to connect to. Create a service: curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-limit-count-service", "plugins": { "graphql-limit-count": { "count": 2, "time_window": 30, "rejected_code": 429, "policy": "local", "group": "srv1" } }, "upstream": { "type": "roundrobin", "pass_host": "node", "scheme": "https", "nodes": { "api.github.com:443": 1 } } }' Create two routes and configure their `service_id` to be `graphql-limit-count-service`, so that they share the same configurations for the plugin and upstream: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-limit-count-route-1", "service_id": "graphql-limit-count-service", "uri": "/graphql1", "plugins": { "proxy-rewrite": { "uri": "/graphql" } } }' curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-limit-count-route-2", "service_id": "graphql-limit-count-service", "uri": "/graphql2", "plugins": { "proxy-rewrite": { "uri": "/graphql" } } }' note The [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite) plugin is used to rewrite the URI to `/graphql` so that requests are forwarded to the correct endpoint. Send a request with a GraphQL query of depth 2 to route `/graphql1`: curl -i "http://127.0.0.1:9080/graphql1" -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -d '{"query": "query {viewer{login}}"}' You should see an `HTTP/1.1 200 OK` response with the corresponding response body. Send the same query of depth 2 to route `/graphql2` within the same 30-second time interval: curl -i "http://127.0.0.1:9080/graphql2" -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -d '{"query": "query {viewer{login}}"}' You should receive an `HTTP/1.1 429 Too Many Requests` response, which verifies the two routes share the same rate limiting quota. ### Share Quota Among Gateway Nodes with a Redis Server[​](https://docs.api7.ai/hub/graphql-limit-count/#share-quota-among-gateway-nodes-with-a-redis-server "Direct link to Share Quota Among Gateway Nodes with a Redis Server") The following example demonstrates the rate limiting of GraphQL requests across multiple gateway nodes with a Redis server, such that different gateway nodes share the same rate limiting quota. Create a route with the following configurations in the gateway group: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-limit-count-route", "uri": "/graphql", "plugins": { "graphql-limit-count": { "count": 2, "time_window": 30, "rejected_code": 429, "key": "remote_addr", "policy": "redis", "redis_host": "192.168.xxx.xxx", "redis_port": 6379, "redis_password": "p@ssw0rd", "redis_database": 1 } }, "upstream": { "type": "roundrobin", "pass_host": "node", "scheme": "https", "nodes": { "api.github.com:443": 1 } } }' ❶ `policy`: set to `redis` to use a Redis instance for rate limiting. ❷ `redis_host`: set to Redis instance IP address. ❸ `redis_port`: set to Redis instance listening port. ❹ `redis_password`: set to the password of the Redis instance, if any. ❺ `redis_database`: set to the database number in the Redis instance. Send a request with a GraphQL query of depth 2 to a gateway instance: curl -i "http://127.0.0.1:9080/graphql" -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -d '{"query": "query {viewer{login}}"}' You should see an `HTTP/1.1 200 OK` response with the corresponding response body. Send the same request to a different gateway instance within the same 30-second time interval, you should receive an `HTTP/1.1 429 Too Many Requests` response, verifying routes configured in different gateway nodes share the same quota. ### Share Quota Among Gateway Nodes with a Redis Cluster[​](https://docs.api7.ai/hub/graphql-limit-count/#share-quota-among-gateway-nodes-with-a-redis-cluster "Direct link to Share Quota Among Gateway Nodes with a Redis Cluster") You can also use a Redis cluster to apply the same quota across multiple gateway nodes, such that different gateway nodes share the same rate limiting quota. Ensure that your Redis instances are running in [cluster mode](https://redis.io/docs/management/scaling/#create-and-use-a-redis-cluster) . A minimum of two nodes are required for the `graphql-limit-count` plugin configurations. Create a route with the following configurations in the gateway group: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "graphql-limit-count-route", "uri": "/graphql", "plugins": { "graphql-limit-count": { "count": 2, "time_window": 30, "rejected_code": 429, "key": "remote_addr", "policy": "redis-cluster", "redis_cluster_nodes": [ "192.168.xxx.xxx:6379", "192.168.xxx.xxx:16379" ], "redis_password": "p@ssw0rd", "redis_cluster_name": "redis-cluster-1", "redis_cluster_ssl": true } }, "upstream": { "type": "roundrobin", "pass_host": "node", "scheme": "https", "nodes": { "api.github.com:443": 1 } } }' ❶ `policy`: set to `redis-cluster` to use a Redis cluster for rate limiting. ❷ `redis_cluster_nodes`: set to Redis node addresses in the Redis cluster. ❸ `redis_password`: set to the password of the Redis cluster, if any. ❹ `redis_cluster_name`: set to the Redis cluster name. ➎ `redis_cluster_ssl`: enable SSL/TLS communication with Redis cluster. Send a request with a GraphQL query of depth 2 to a gateway instance: curl -i "http://127.0.0.1:9080/graphql" -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${GH_ACCESS_TOKEN}" \ -d '{"query": "query {viewer{login}}"}' You should see an `HTTP/1.1 200 OK` response with the corresponding response body. Send the same request to a different gateway instance within the same 30-second time interval, you should receive an `HTTP/1.1 429 Too Many Requests` response, verifying routes configured in different gateway nodes share the same quota. * [Local vs Redis Rate Limiting](https://docs.api7.ai/hub/graphql-limit-count/#local-vs-redis-rate-limiting) * [Examples](https://docs.api7.ai/hub/graphql-limit-count/#examples) * [Apply Rate Limiting by Remote Address](https://docs.api7.ai/hub/graphql-limit-count/#apply-rate-limiting-by-remote-address) * [Apply Rate Limiting by Remote Address and Consumer Name](https://docs.api7.ai/hub/graphql-limit-count/#apply-rate-limiting-by-remote-address-and-consumer-name) * [Share Quota among Routes](https://docs.api7.ai/hub/graphql-limit-count/#share-quota-among-routes) * [Share Quota Among Gateway Nodes with a Redis Server](https://docs.api7.ai/hub/graphql-limit-count/#share-quota-among-gateway-nodes-with-a-redis-server) * [Share Quota Among Gateway Nodes with a Redis Cluster](https://docs.api7.ai/hub/graphql-limit-count/#share-quota-among-gateway-nodes-with-a-redis-cluster) --- # Proxy Rewrite | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/proxy-rewrite/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `proxy-rewrite` plugin offers options to rewrite requests that APISIX forwards to upstream services. With the plugin, you can modify the HTTP methods, request destination upstream addresses, request headers, and more. Examples[​](https://docs.api7.ai/hub/proxy-rewrite/#examples "Direct link to Examples") ---------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `proxy-rewrite` on a route in different scenarios. ### Rewrite Host Header[​](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-host-header "Direct link to Rewrite Host Header") The following example demonstrates how you can modify the `Host` header in a request. Note that you should not use `headers.set` to set the `Host` header. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-rewrite-route", "methods": ["GET"], "uri": "/headers", "plugins": { "proxy-rewrite": { "host": "myapisix.demo" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to `/headers` to check all the request headers sent to upstream: curl "http://127.0.0.1:9080/headers" You should see a response similar to the following: { "headers": { "Accept": "*/*", "Host": "myapisix.demo", "User-Agent": "curl/8.2.1", "X-Amzn-Trace-Id": "Root=1-64fef198-29da0970383150175bd2d76d", "X-Forwarded-Host": "127.0.0.1" }} ### Rewrite URI And Set Headers[​](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-uri-and-set-headers "Direct link to Rewrite URI And Set Headers") The following example demonstrates how you can rewrite the request upstream URI and set additional header values. If the same headers present in the client request, the corresponding header values set in the plugin will overwrite the values present in the client request. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-rewrite-route", "methods": ["GET"], "uri": "/", "plugins": { "proxy-rewrite": { "uri": "/anything", "headers": { "set": { "X-Api-Version": "v1", "X-Api-Engine": "apisix" } } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to verify: curl "http://127.0.0.1:9080/" -H 'X-Api-Version: v2' You should see a response similar to the following: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/8.2.1", "X-Amzn-Trace-Id": "Root=1-64fed73a-59cd3bd640d76ab16c97f1f1", "X-Api-Engine": "apisix", "X-Api-Version": "v1", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "GET", "origin": "::1, 103.248.35.179", "url": "http://localhost/anything"} Note that both headers present and the header value of `X-Api-Version` configured in the plugin overwrites the header value passed in the request. ### Rewrite URI And Append Headers[​](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-uri-and-append-headers "Direct link to Rewrite URI And Append Headers") The following example demonstrates how you can rewrite the request upstream URI and append additional header values. If the same headers present in the client request, their headers values will append to the configured header values in the plugin. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-rewrite-route", "methods": ["GET"], "uri": "/", "plugins": { "proxy-rewrite": { "uri": "/headers", "headers": { "add": { "X-Api-Version": "v1", "X-Api-Engine": "apisix" } } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to verify: curl "http://127.0.0.1:9080/" -H 'X-Api-Version: v2' You should see a response similar to the following: { "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/8.2.1", "X-Amzn-Trace-Id": "Root=1-64fed73a-59cd3bd640d76ab16c97f1f1", "X-Api-Engine": "apisix", "X-Api-Version": "v1,v2", "X-Forwarded-Host": "127.0.0.1" }} Note that both headers present and the header value of `X-Api-Version` configured in the plugin is appended by the header value passed in the request. ### Remove Existing Header[​](https://docs.api7.ai/hub/proxy-rewrite/#remove-existing-header "Direct link to Remove Existing Header") The following example demonstrates how you can remove an existing header `User-Agent`. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-rewrite-route", "methods": ["GET"], "uri": "/headers", "plugins": { "proxy-rewrite": { "headers": { "remove":[ "User-Agent" ] } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to verify if the specified header is removed: curl "http://127.0.0.1:9080/headers" You should see a response similar to the following, where the `User-Agent` header is not present: { "headers": { "Accept": "*/*", "Host": "httpbin.org", "X-Amzn-Trace-Id": "Root=1-64fef302-07f2b13e0eb006ba776ad91d", "X-Forwarded-Host": "127.0.0.1" }} ### Rewrite URI Using RegEx[​](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-uri-using-regex "Direct link to Rewrite URI Using RegEx") The following example demonstrates how you can parse text from the original upstream URI path and use them to compose a new upstream URI path. In this example, APISIX is configured to forward all requests from `/test/user/agent` to `/user-agent`. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-rewrite-route", "uri": "/test/*", "plugins": { "proxy-rewrite": { "regex_uri": ["^/test/(.*)/(.*)", "/$1-$2"] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to `/test/user/agent` to check if it is redirected to `/user-agent`: curl "http://127.0.0.1:9080/test/user/agent" You should see a response similar to the following: { "user-agent": "curl/8.2.1"} ### Add URL Parameters[​](https://docs.api7.ai/hub/proxy-rewrite/#add-url-parameters "Direct link to Add URL Parameters") The following example demonstrates how you can add URL parameters to the request. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-rewrite-route", "methods": ["GET"], "uri": "/get", "plugins": { "proxy-rewrite": { "uri": "/get?arg1=apisix&arg2=plugin" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to verify if the URL parameters are also forwarded to upstream: curl "http://127.0.0.1:9080/get" You should see a response similar to the following: { "args": { "arg1": "apisix", "arg2": "plugin" }, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.2.1", "X-Amzn-Trace-Id": "Root=1-64fef6dc-2b0e09591db7353a275cdae4", "X-Forwarded-Host": "127.0.0.1" }, "origin": "127.0.0.1, 103.248.35.148", "url": "http://127.0.0.1/get?arg1=apisix&arg2=plugin"} ### Rewrite HTTP Method[​](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-http-method "Direct link to Rewrite HTTP Method") The following example demonstrates how you can rewrite a GET request into a POST request. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-rewrite-route", "methods": ["GET"], "uri": "/get", "plugins": { "proxy-rewrite": { "uri": "/anything", "method":"POST" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a GET request to `/get` to verify if it is transformed into a POST request to `/anything`: curl "http://127.0.0.1:9080/get" You should see a response similar to the following: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.2.1", "X-Amzn-Trace-Id": "Root=1-64fef7de-0c63387645353998196317f2", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "POST", "origin": "::1, 103.248.35.179", "url": "http://localhost/anything"} ### Forward Consumer Names to Upstream[​](https://docs.api7.ai/hub/proxy-rewrite/#forward-consumer-names-to-upstream "Direct link to Forward Consumer Names to Upstream") The following example demonstrates how you can forward the name of consumers who authenticates successfully to upstream services. As an example, you will be using [`key-auth`](https://docs.api7.ai/hub/key-auth) as the authentication method. Create a consumer `JohnDoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "JohnDoe" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/JohnDoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Next, create a route with key authentication enabled, and configure `proxy-rewrite` to add consumer name to the header: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "consumer-restricted-route", "uri": "/get", "plugins": { "key-auth": {}, "proxy-rewrite": { "headers": { "set": { "X-Apisix-Consumer": "$consumer_name" }, "remove": [ "Apikey" ] } } }, "upstream" : { "nodes": { "httpbin.org":1 } } }' ❶ Add the consumer name to the header `X-Apisix-Consumer` using the [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables#apisix-variables) . ❷ Remove the authentication key so that it is not visible to the upstream service. Send a request to the route as consumer `JohnDoe`: curl -i "http://127.0.0.1:9080/get" -H 'apikey: john-key' You should receive an `HTTP/1.1 200 OK` response with the following body: { "args": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.4.0", "X-Amzn-Trace-Id": "Root=1-664b01a6-2163c0156ed4bff51d87d877", "X-Apisix-Consumer": "JohnDoe", "X-Forwarded-Host": "127.0.0.1" }, "origin": "172.19.0.1, 203.12.12.12", "url": "http://127.0.0.1/get"} Send another request to the route without the valid credential: curl -i "http://127.0.0.1:9080/get" You should receive an `HTTP/1.1 403 Forbidden` response. ### Dynamically Forward Requests in `radixtree_uri_with_parameter` Router Mode[​](https://docs.api7.ai/hub/proxy-rewrite/#dynamically-forward-requests-in-radixtree_uri_with_parameter-router-mode "Direct link to dynamically-forward-requests-in-radixtree_uri_with_parameter-router-mode") The following example demonstrates how to extract part of the URL path using the `uri_param_*` variable and forward the value to the upstream service in a new header. This example assumes that APISIX is operating in the `radixtree_uri_with_parameter` [router mode](https://docs.api7.ai/apisix/reference/router-options) . Create a route as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "httpbin", "uri": "/anything/user/:user_id/profile", "plugins":{ "proxy-rewrite": { "headers": { "set": { "X-User-ID": "$uri_param_user_id" } } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Match requests to `/anything/user/:user_id/profile` where `user_id` is a parameter. ❷ Assign the `user_id` parameter value to a new header `X-User-ID`. Send a request to the route: curl "http://127.0.0.1:9080/anything/user/123/profile" You should see the following response: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-68873cf5-7248f64d19d607ea50aa9735", "X-Forwarded-Host": "127.0.0.1", "X-User-Id": "123" }, ...} The route parameter can also accept URL-encoded string. For instance, if you send a request as such: curl -i "http://127.0.0.1:9080/anything/user/123%20456/profile" The user ID would be extracted as `123 456`: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-68873d37-7634825b20d05dee3a852cb9", "X-Forwarded-Host": "127.0.0.1", "X-User-Id": "123 456" }, ...} * [Examples](https://docs.api7.ai/hub/proxy-rewrite/#examples) * [Rewrite Host Header](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-host-header) * [Rewrite URI And Set Headers](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-uri-and-set-headers) * [Rewrite URI And Append Headers](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-uri-and-append-headers) * [Remove Existing Header](https://docs.api7.ai/hub/proxy-rewrite/#remove-existing-header) * [Rewrite URI Using RegEx](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-uri-using-regex) * [Add URL Parameters](https://docs.api7.ai/hub/proxy-rewrite/#add-url-parameters) * [Rewrite HTTP Method](https://docs.api7.ai/hub/proxy-rewrite/#rewrite-http-method) * [Forward Consumer Names to Upstream](https://docs.api7.ai/hub/proxy-rewrite/#forward-consumer-names-to-upstream) * [Dynamically Forward Requests in `radixtree_uri_with_parameter` Router Mode](https://docs.api7.ai/hub/proxy-rewrite/#dynamically-forward-requests-in-radixtree_uri_with_parameter-router-mode) --- # Upstreams | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/upstreams/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of an upstream object in APISIX and why you would want to use it. You will be introduced to a few relevant features, including load balancing, service discovery, and upstream health checking. Explore additional resources at the end for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/upstreams/#overview "Direct link to Overview") ---------------------------------------------------------------------------------------------------- An _upstream_ object in APISIX is a logical abstraction of a set containing one or more upstream addresses. It is required in [routes](https://docs.api7.ai/apisix/key-concepts/routes) or [services](https://docs.api7.ai/apisix/key-concepts/services) to specify **where** requests flow to and **how** they are distributed. There are different ways to configure upstream addresses, one of which is to explicitly configure them in every route/service. Here is an example of such a configuration in routes, where the same upstream address is repeated across three different routes: ![Upstreams Diagram showing three routes with different plugins and the same hard-coded upstream address](https://static.api7.ai/uploads/2023/02/24/6gZ6zw2R_upstream-before.svg) This approach, however, leads to data redundancies and poses a risk for update anomalies (i.e. inconsistent data resulting from a partial update). A better approach is to extract the repetitive upstream configurations into a separate upstream object and embed its `upstream_id` into routes/services, such as the following: ![Upstreams Diagram showing three routes with different plugins pointing to the same upstream object with the desired upstream address](https://static.api7.ai/uploads/2023/02/24/vPjoKWdE_upstream-after.svg) As you can probably see, large-scale systems with many routes/services would benefit significantly from configuring identical groups of upstream addresses in upstream objects, reducing redundant information and operational costs. Load Balancing[​](https://docs.api7.ai/apisix/key-concepts/upstreams/#load-balancing "Direct link to Load Balancing") ---------------------------------------------------------------------------------------------------------------------- An important configuration option in upstreams is the [load balancing](https://docs.api7.ai/apisix/getting-started/load-balancing) algorithm. APISIX offers four load-balancing algorithm options to choose from: * `roundrobin` - weighted round robin * `chash` - consistent hashing * `ewma` - exponentially weighted moving average * `least_conn` - least connections You can also build your own load balancing algorithm by adding the source file under `apisix/balancer` and importing it with `require("apisix.balancer.your_balancer_name")`, where needed. To learn more about how you can configure load balancing in APISIX, see the [load balancing tutorial](https://docs.api7.ai/apisix/getting-started/load-balancing) and [Admin API reference](https://docs.api7.ai/apisix/reference/admin-api#tag/Upstream/paths/~1apisix~1admin~1upstreams~1%7Bid%7D/put) . Service Discovery[​](https://docs.api7.ai/apisix/key-concepts/upstreams/#service-discovery "Direct link to Service Discovery") ------------------------------------------------------------------------------------------------------------------------------- While it is straightforward to figure upstream addresses statically, in microservice-based architectures, upstream addresses are often dynamically assigned and therefore changed during autoscaling, failures, and updates. Static configurations are less than ideal in this case. Service discovery comes to the rescue. It describes the process of automatically detecting the available upstream services, keeping their addresses in a database (called a service registry) for others to reference. That way, an API gateway can always fetch the latest list of upstream addresses through the registry, ensuring all requests are forwarded to healthy upstream nodes. APISIX supports integrations with many service registries, such as Consul, Eureka, Nacos, Kubernetes service discovery, and more. For more details about how to integrate with third-party service registries, such as HashiCorp Consul, please see the [how-to guide](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration) . Upstream Health Checking[​](https://docs.api7.ai/apisix/key-concepts/upstreams/#upstream-health-checking "Direct link to Upstream Health Checking") ---------------------------------------------------------------------------------------------------------------------------------------------------- APISIX provides active and passive health checking options to probe if upstream nodes are online (a.k.a. healthy). Unhealthy upstream nodes will be ignored until they recover and are deemed healthy again. Upstream health checking can be configured in the `checks` parameter in an upstream object. For more details about how to configure active and passive upstream health check, please see the [how-to guide](https://docs.api7.ai/apisix/how-to-guide/traffic-management/health-check) . Additional Resources[​](https://docs.api7.ai/apisix/key-concepts/upstreams/#additional-resources "Direct link to Additional Resources") ---------------------------------------------------------------------------------------------------------------------------------------- * Getting Started - [Load Balancing](https://docs.api7.ai/apisix/getting-started/load-balancing) * [Configure Upstream Health Checks](https://docs.api7.ai/apisix/how-to-guide/traffic-management/health-check) * [Integrate with HashiCorp Consul for Service Discovery](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration) * Admin API - [Upstream](https://docs.api7.ai/apisix/reference/admin-api#tag/Upstream) * [Overview](https://docs.api7.ai/apisix/key-concepts/upstreams/#overview) * [Load Balancing](https://docs.api7.ai/apisix/key-concepts/upstreams/#load-balancing) * [Service Discovery](https://docs.api7.ai/apisix/key-concepts/upstreams/#service-discovery) * [Upstream Health Checking](https://docs.api7.ai/apisix/key-concepts/upstreams/#upstream-health-checking) * [Additional Resources](https://docs.api7.ai/apisix/key-concepts/upstreams/#additional-resources) --- # Authz Keycloak | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/authz-keycloak/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `authz-keycloak` plugin supports the integration with [Keycloak](https://www.keycloak.org/) to authenticate and authorize users. See Keycloak's [Authorization Services Guide](https://www.keycloak.org/docs/latest/authorization_services/) for more information about the configuration options available in this plugin. While the plugin was developed for Keycloak, it could theoretically be used with other OAuth/OIDC and UMA-compliant identity providers. Examples[​](https://docs.api7.ai/hub/authz-keycloak/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `authz-keycloak` for different scenarios. To follow along, complete the [preliminary setups](https://docs.api7.ai/hub/authz-keycloak/#set-up-keycloak) for Keycloak. ### Set Up Keycloak[​](https://docs.api7.ai/hub/authz-keycloak/#set-up-keycloak "Direct link to Set Up Keycloak") #### Start Keycloak[​](https://docs.api7.ai/hub/authz-keycloak/#start-keycloak "Direct link to Start Keycloak") Start a Keycloak instance named `apisix-quickstart-keycloak` with the administrator name `quickstart-admin` and password `quickstart-admin-pass` in [development mode](https://www.keycloak.org/server/configuration#_starting_keycloak_in_development_mode) in Docker: docker run -d --name "apisix-quickstart-keycloak" \ -e 'KEYCLOAK_ADMIN=quickstart-admin' \ -e 'KEYCLOAK_ADMIN_PASSWORD=quickstart-admin-pass' \ -p 8080:8080 \ quay.io/keycloak/keycloak:18.0.2 start-dev Save the Keycloak IP to an environment variable to be referenced in future configuration: KEYCLOAK_IP=192.168.42.145 # replace with your host IP Navigate to `http://localhost:8080` in browser and click **Administration Console**: ![admin-console](https://static.api7.ai/uploads/2024/01/12/yEKlaSf5_admin-console.png) Enter the administrator’s username `quickstart-admin` and password `quickstart-admin-pass` to sign in: ![admin-signin](https://static.api7.ai/uploads/2024/01/12/GYIVrPyb_signin.png) #### Create a Realm[​](https://docs.api7.ai/hub/authz-keycloak/#create-a-realm "Direct link to Create a Realm") In the left menu, hover over **Master**, and select **Add realm** in the dropdown: ![create-realm](https://static.api7.ai/uploads/2024/01/12/563XIJPK_add-realm.png) Enter the realm name `quickstart-realm` and click **Create** to create it: ![add-realm](https://static.api7.ai/uploads/2024/01/12/0lD21Z8R_create-realm.png) #### Create a Client[​](https://docs.api7.ai/hub/authz-keycloak/#create-a-client "Direct link to Create a Client") Click **Clients** > **Create** to open the **Add Client** page: ![create-client](https://static.api7.ai/uploads/2024/01/12/nHxgXyd9_create-client.png) Enter **Client ID** as `apisix-quickstart-client`, keep the **Client Protocol** as `openid-connect` and **Save**: ![add-client](https://static.api7.ai/uploads/2024/01/12/7YSCHCnp_add-client.png) The client `apisix-quickstart-client` is created. After redirecting to the detailed page, select `confidential` as the **Access Type**: ![client-access-type-confidential](https://static.api7.ai/uploads/2024/01/12/L7cahPUe_confidential.png) When the user login is successful during the SSO, Keycloak will carry the state and code to redirect the client to the addresses in **Valid Redirect URIs**. For simplicity of demonstration, enter wildcard `*` to accept any redirect URI: ![client-redirect](https://static.api7.ai/uploads/2024/01/12/B3VGbQbW_redirect-uri.png) Enable authorization for the client, which should also enable service accounts with an assigned role `uma_protection` automatically: ![enable-authorization](https://static.api7.ai/uploads/2024/01/05/S4we4KO9_enable-auth.png) Select **Save** to apply custom configurations. #### Save Client ID and Secret[​](https://docs.api7.ai/hub/authz-keycloak/#save-client-id-and-secret "Direct link to Save Client ID and Secret") Click on **Clients** > `apisix-quickstart-client` > **Credentials**, and copy the client secret from **Secret**: ![client-secret](https://static.api7.ai/uploads/2024/01/12/3VqiXdf9_client-secret.png) Save the OIDC client ID and secret to environment variables: OIDC_CLIENT_ID=apisix-quickstart-clientOIDC_CLIENT_SECRET=bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH # replace with your value #### Request Access Token[​](https://docs.api7.ai/hub/authz-keycloak/#request-access-token "Direct link to Request Access Token") Request an access token from Keycloak: curl -i "http://$KEYCLOAK_IP:8080/realms/quickstart-realm/protocol/openid-connect/token" -X POST \ -d 'grant_type=client_credentials' \ -d 'client_id='$OIDC_CLIENT_ID'' \ -d 'client_secret='$OIDC_CLIENT_SECRET'' You should see a response similar to the following: {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoT3ludlBPY2d6Y3VWWnYtTU42bXZKMUczb0dOX2d6MFo3WFl6S2FSa1NBIn0.eyJleHAiOjE3MDM4MjU1NjQsImlhdCI6MTcwMzgyNTI2NCwianRpIjoiMWQ4NWE4N2UtZDFhMC00NThmLThiMTItNGZiYWM2ODA5YmYwIiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguMS44Mzo4MDgwL3JlYWxtcy9xdWlja3N0YXJ0LXJlYWxtIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjE1OGUzOWFlLTk0YjAtNDI3Zi04ZGU3LTU3MTRhYWYwOGYzOSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwaXNpeC1xdWlja3N0YXJ0LWNsaWVudCIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1xdWlja3N0YXJ0LXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJjbGllbnRJZCI6ImFwaXNpeC1xdWlja3N0YXJ0LWNsaWVudCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1hcGlzaXgtcXVpY2tzdGFydC1jbGllbnQiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSJ9.TltzSXqrJuVID7aGrb35jn-oc07U_-jugSn-3jKz4A44LwtAsME_8b3qkmR4boMOIht_5pF6bnnp70MFAlg6JKu4_yIQDxF_GAHjnZXEO8OCKhtIKwXm2w-hnnJVIhIdGkIVkbPP0HfILuar_m0hpa53VpPBGYR-OS4pyh0KTUs8MB22xAEqyz9zjCm6SX9vXCqgeVkSpRW2E8NaGEbAdY25uY-ZC4dI_pON87Ey5e8GdD6HQLXQlGIOdCDi3N7k0HDoD9TZRv2bMRPfy4zVYm1ZlClIuF79A-ZBwr0c-XYuq7t6EY0gPGEXB-s0SaKlrIU5S9JBeVXRzYvqAih41g","expires_in":300,"refresh_expires_in":0,"token_type":"Bearer","not-before-policy":0,"scope":"email profile"} Save the token to an environment variable: # replace with your access tokenACCESS_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoT3ludlBPY2d6Y3VWWnYtTU42bXZKMUczb0dOX2d6MFo3WFl6S2FSa1NBIn0.eyJleHAiOjE3MDM4MjU1NjQsImlhdCI6MTcwMzgyNTI2NCwianRpIjoiMWQ4NWE4N2UtZDFhMC00NThmLThiMTItNGZiYWM2ODA5YmYwIiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguMS44Mzo4MDgwL3JlYWxtcy9xdWlja3N0YXJ0LXJlYWxtIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjE1OGUzOWFlLTk0YjAtNDI3Zi04ZGU3LTU3MTRhYWYwOGYzOSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwaXNpeC1xdWlja3N0YXJ0LWNsaWVudCIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1xdWlja3N0YXJ0LXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJjbGllbnRJZCI6ImFwaXNpeC1xdWlja3N0YXJ0LWNsaWVudCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1hcGlzaXgtcXVpY2tzdGFydC1jbGllbnQiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSJ9.TltzSXqrJuVID7aGrb35jn-oc07U_-jugSn-3jKz4A44LwtAsME_8b3qkmR4boMOIht_5pF6bnnp70MFAlg6JKu4_yIQDxF_GAHjnZXEO8OCKhtIKwXm2w-hnnJVIhIdGkIVkbPP0HfILuar_m0hpa53VpPBGYR-OS4pyh0KTUs8MB22xAEqyz9zjCm6SX9vXCqgeVkSpRW2E8NaGEbAdY25uY-ZC4dI_pON87Ey5e8GdD6HQLXQlGIOdCDi3N7k0HDoD9TZRv2bMRPfy4zVYm1ZlClIuF79A-ZBwr0c-XYuq7t6EY0gPGEXB-s0SaKlrIU5S9JBeVXRzYvqAih41g ### Use Lazy Load Path and Resource Registration Endpoint[​](https://docs.api7.ai/hub/authz-keycloak/#use-lazy-load-path-and-resource-registration-endpoint "Direct link to Use Lazy Load Path and Resource Registration Endpoint") The examples below demonstrate how you can configure the plugin to dynamically resolve the request URI to resource(s) using the resource registration endpoint instead of the static permissions. Create a route with `authz-keycloak-route` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "authz-keycloak-route", "uri": "/anything", "plugins": { "authz-keycloak": { "lazy_load_paths": true, "resource_registration_endpoint": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/authz/protection/resource_set", "discovery": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/.well-known/uma2-configuration", "client_id": "'"$OIDC_CLIENT_ID"'", "client_secret": "'"$OIDC_CLIENT_SECRET"'" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Set `lazy_load_paths` to `true`. ❷ Set `resource_registration_endpoint` to Keycloak's UMA-compliant resource registration endpoint endpoint. Required when `lazy_load_paths` is `true`. ❸ Set `discovery` to the discovery document endpoint of Keycloak authorization services. ❹ Set `client_id` to client ID created previously. ❺ Set `client_secret` to client secret created previously. Required when `lazy_load_paths` is `true`. Send a request to the route: curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN" You should see an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Authorization": "Bearer eyJhbGciOiJSU...", ... }, "json": null, "method": "GET", "origin": "127.0.0.1, 108.180.51.111", "url": "http://127.0.0.1/anything"} ### Use Static Permissions[​](https://docs.api7.ai/hub/authz-keycloak/#use-static-permissions "Direct link to Use Static Permissions") The examples below demonstrate how you can configure Keycloak for scope-based permission associated with a client scope policy, and configure the `authz-keycloak` plugin to use static permissions. #### Create Scope in Keycloak[​](https://docs.api7.ai/hub/authz-keycloak/#create-scope-in-keycloak "Direct link to Create Scope in Keycloak") Go to **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Authorization Scopes**, and click **Create** to open the **Add Scope** page: ![add-scope](https://static.api7.ai/uploads/2024/01/06/bVHhiALe_auth-scope.png) Enter the scope names as `access` and click **Save**: ![create-new-scope](https://static.api7.ai/uploads/2024/01/06/xPorYwK3_save-scope.png) #### Create Resource in Keycloak[​](https://docs.api7.ai/hub/authz-keycloak/#create-resource-in-keycloak "Direct link to Create Resource in Keycloak") Go to **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Resources** and click **Create** to open the **Add Resource** page: ![create-resource](https://static.api7.ai/uploads/2024/01/06/15DJ9HAU_create-resource.png) Enter the resource names `httpbin-anything`, URI `/anything`, scope `access`, and click **Save**: ![save-resource](https://static.api7.ai/uploads/2024/01/06/epuAPgos_save-resource.png) #### Create Client Scope in Keycloak[​](https://docs.api7.ai/hub/authz-keycloak/#create-client-scope-in-keycloak "Direct link to Create Client Scope in Keycloak") Go to **Client Scopes** and click **Create** to open the **Add client scope** page: ![create-client-scope](https://static.api7.ai/uploads/2024/01/11/PyseoG7T_creat-client-scope.png) Enter the scope name `httpbin-access` and click **Save**: ![save-client-scope](https://static.api7.ai/uploads/2024/01/12/5xQl0Xbx_save-client-scope.png) #### Create Policy in Keycloak[​](https://docs.api7.ai/hub/authz-keycloak/#create-policy-in-keycloak "Direct link to Create Policy in Keycloak") Go to **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Policies** > **Create Policies** and select **Client Scope** from the dropdown to open the **Add Client Scope Policy** page: ![create-policy](https://static.api7.ai/uploads/2024/01/06/7UtT3cF6_create-policy.png) Enter the policy name `access-client-scope-policy` for client scope `httpbin-access`, check the **Required** box, and click **Save**: ![save-policy](https://static.api7.ai/uploads/2024/12/12/2DR0K39f_add_client_scope.png) #### Create Permission in Keycloak[​](https://docs.api7.ai/hub/authz-keycloak/#create-permission-in-keycloak "Direct link to Create Permission in Keycloak") Go to **Clients** > **`apisix-quickstart-client`** > **Authorization** > **Permissions** > **Create Permissions** and select **Scope-Based** from the dropdown to open the **Add Scope Permission** page: ![create-permission](https://static.api7.ai/uploads/2024/12/12/0PWsJUti_create_permission.png) Enter the permission name `access-scope-perm`, select the `access` scope, apply the policy `access-client-scope-policy`, and click **Save**: ![add-scope-permission](https://static.api7.ai/uploads/2024/01/12/Y0vlk1Tj_add-scope-permission.png) #### Assign Client Scope[​](https://docs.api7.ai/hub/authz-keycloak/#assign-client-scope "Direct link to Assign Client Scope") Go to **Clients** > **`apisix-quickstart-client`** > **Client Scopes** and add `httpbin-access` to the default client scopes: ![add-client-scope](https://static.api7.ai/uploads/2024/01/06/sJKUMUcP_add-client-scope.png) #### Configure APISIX[​](https://docs.api7.ai/hub/authz-keycloak/#configure-apisix "Direct link to Configure APISIX") Create a route with `authz-keycloak-route` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "authz-keycloak-route", "uri": "/anything", "plugins": { "authz-keycloak": { "lazy_load_paths": false, "discovery": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/.well-known/uma2-configuration", "permissions": ["httpbin-anything#access"], "client_id": "apisix-quickstart-client" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Set `lazy_load_paths` to `false`. ❷ Set `discovery` to the discovery document endpoint of Keycloak authorization services. ❸ Set `permissions` to resource `httpbin-anything` and scope `access`. Send a request to the route: curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN" You should see an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Authorization": "Bearer eyJhbGciOiJSU...", ... }, "json": null, "method": "GET", "origin": "127.0.0.1, 108.180.51.111", "url": "http://127.0.0.1/anything"} If you remove the client scope `httpbin-access` for `apisix-quickstart-client`, you should receive a `401 Unauthorized` response when requesting the resource. ### Generate Token with Password Grant at Custom Token Endpoint[​](https://docs.api7.ai/hub/authz-keycloak/#generate-token-with-password-grant-at-custom-token-endpoint "Direct link to Generate Token with Password Grant at Custom Token Endpoint") The examples below demonstrate how you can generate a token using with the password grant at a custom endpoint. #### Create User in Keycloak[​](https://docs.api7.ai/hub/authz-keycloak/#create-user-in-keycloak "Direct link to Create User in Keycloak") To use the password grant, you should first create a user. Go to **Users** > **Add user** and click on **Add user**: ![add-user](https://static.api7.ai/uploads/2024/01/12/IBCav8aa_add-user.png) Enter the **Username** as `quickstart-user` and select **Save**: ![save-user](https://static.api7.ai/uploads/2024/01/12/3fUQOFWg_save-user.png) Click on **Credentials**, then set the **Password** as `quickstart-user-pass`. Switch **Temporary** to `OFF` to that you do not need to change password the first time you log in: ![set-password](https://static.api7.ai/uploads/2024/01/12/aoabcBbC_set-password.png) #### Configure APISIX[​](https://docs.api7.ai/hub/authz-keycloak/#configure-apisix-1 "Direct link to Configure APISIX") Create a route with `authz-keycloak-route` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "authz-keycloak-route", "uri": "/api/*", "plugins": { "authz-keycloak": { "lazy_load_paths": true, "resource_registration_endpoint": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/authz/protection/resource_set", "client_id": "'"$OIDC_CLIENT_ID"'", "client_secret": "'"$OIDC_CLIENT_SECRET"'", "token_endpoint": "http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/protocol/openid-connect/token", "password_grant_token_generation_incoming_uri": "/api/token" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Set `token_endpoint` to the Keycloak token endpoint. Required when discovery document is not provided. ❷ Set `password_grant_token_generation_incoming_uri` to a custom URI path users can obtain tokens from. Send a request to the configured token endpoint. Note that the request should use the POST method and `application/x-www-form-urlencoded` as the `Content-Type`: OIDC_USER=quickstart-userOIDC_PASSWORD=quickstart-user-passcurl "http://127.0.0.1:9080/api/token" -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "Accept: application/json" \ -d 'username='$OIDC_USER'' \ -d 'password='$OIDC_PASSWORD'' You should see a JSON response with the access token, similar to the following: {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6U3FFaXN6VlpuYi1sRWMzZkp0UHNpU1ZZcGs4RGN3dXI1Mkx5V05aQTR3In0.eyJleHAiOjE2ODAxNjA5NjgsImlhdCI6MTY4MDE2MDY2OCwianRpIjoiMzQ5MTc4YjQtYmExZC00ZWZjLWFlYTUtZGY2MzJiMDJhNWY5IiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguNDIuMTQ1OjgwODAvcmVhbG1zL3F1aWNrc3RhcnQtcmVhbG0iLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTg4MTVjM2EtNmQwNy00YTY2LWJjZjItYWQ5NjdmMmIwMTFmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYXBpc2l4LXF1aWNrc3RhcnQtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6ImIxNmIyNjJlLTEwNTYtNDUxNS1hNDU1LWYyNWUwNzdjY2I3NiIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1xdWlja3N0YXJ0LXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6ImIxNmIyNjJlLTEwNTYtNDUxNS1hNDU1LWYyNWUwNzdjY2I3NiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoicXVpY2tzdGFydC11c2VyIn0.uD_7zfZv5182aLXu9-YBzBDK0nr2mE4FWb_4saTog2JTqFTPZZa99Gm8AIDJx2ZUcZ_ElkATqNUZ4OpWmL2Se5NecMw3slJReewjD6xgpZ3-WvQuTGpoHdW5wN9-Rjy8ungilrnAsnDA3tzctsxm2w6i9KISxvZrzn5Rbk-GN6fxH01VC5eekkPUQJcJgwuJiEiu70SjGnm21xDN4VGkNRC6jrURoclv3j6AeOqDDIV95kA_MTfBswDFMCr2PQlj5U0RTndZqgSoxwFklpjGV09Azp_jnU7L32_Sq-8coZd0nj5mSdbkJLJ8ZDQDV_PP3HjCP7EHdy4P6TyZ7oGvjw","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0YjFiNTQ3Yi0zZmZjLTQ5YzQtYjE2Ni03YjdhNzIxMjk1ODcifQ.eyJleHAiOjE2ODAxNjI0NjgsImlhdCI6MTY4MDE2MDY2OCwianRpIjoiYzRjNjNlMTEtZTdlZS00ZmEzLWJlNGYtNDMyZWQ4ZmY5OTQwIiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguNDIuMTQ1OjgwODAvcmVhbG1zL3F1aWNrc3RhcnQtcmVhbG0iLCJhdWQiOiJodHRwOi8vMTkyLjE2OC40Mi4xNDU6ODA4MC9yZWFsbXMvcXVpY2tzdGFydC1yZWFsbSIsInN1YiI6IjE4ODE1YzNhLTZkMDctNGE2Ni1iY2YyLWFkOTY3ZjJiMDExZiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJhcGlzaXgtcXVpY2tzdGFydC1jbGllbnQiLCJzZXNzaW9uX3N0YXRlIjoiYjE2YjI2MmUtMTA1Ni00NTE1LWE0NTUtZjI1ZTA3N2NjYjc2Iiwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiYjE2YjI2MmUtMTA1Ni00NTE1LWE0NTUtZjI1ZTA3N2NjYjc2In0.8xYP4bhDg1U9B5cTaEVD7B4oxNp8wwAYEynUne_Jm78","token_type":"Bearer","not-before-policy":0,"session_state":"b16b262e-1056-4515-a455-f25e077ccb76","scope":"profile email"} * [Examples](https://docs.api7.ai/hub/authz-keycloak/#examples) * [Set Up Keycloak](https://docs.api7.ai/hub/authz-keycloak/#set-up-keycloak) * [Use Lazy Load Path and Resource Registration Endpoint](https://docs.api7.ai/hub/authz-keycloak/#use-lazy-load-path-and-resource-registration-endpoint) * [Use Static Permissions](https://docs.api7.ai/hub/authz-keycloak/#use-static-permissions) * [Generate Token with Password Grant at Custom Token Endpoint](https://docs.api7.ai/hub/authz-keycloak/#generate-token-with-password-grant-at-custom-token-endpoint) --- # OpenTelemetry | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/opentelemetry/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `opentelemetry` plugin instruments APISIX and sends traces to OpenTelemetry collector based on the [OpenTelemetry specification](https://opentelemetry.io/docs/reference/specification/) , in binary-encoded [OTLP over HTTP](https://opentelemetry.io/docs/reference/specification/protocol/otlp/#otlphttp) . Examples[​](https://docs.api7.ai/hub/opentelemetry/#examples "Direct link to Examples") ---------------------------------------------------------------------------------------- The examples below demonstrate how you can work with the `opentelemetry` plugin for different scenarios. ### Enable `opentelemetry` Plugin[​](https://docs.api7.ai/hub/opentelemetry/#enable-opentelemetry-plugin "Direct link to enable-opentelemetry-plugin") If you are using API7 Enterprise, you may skip this section as there is no need to manually enable the plugin. By default, the `opentelemetry` plugin is disabled in APISIX. To enable, add the plugin to your [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) as such: config.yaml plugins: - ... - opentelemetry [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. ### Send Traces to OpenTelemetry[​](https://docs.api7.ai/hub/opentelemetry/#send-traces-to-opentelemetry "Direct link to Send Traces to OpenTelemetry") The following example demonstrates how to trace requests to a route and send traces to OpenTelemetry. Start an OpenTelemetry collector instance in Docker: docker run -d --name otel-collector -p 4318:4318 otel/opentelemetry-collector-contrib The collector should start listening on `127.0.0.1:4318`. If you would like to update the collector, you can configure the plugin metadata as such: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/opentelemetry" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "collector": { "address": "127.0.0.1:4318" } }' Create a route with `opentelemetry` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "otel-tracing-route", "uri": "/anything", "plugins": { "opentelemetry": { "sampler": { "name": "always_on" } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Send a request to the route: curl "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. In OpenTelemetry collector's log, you should see information similar to the following: 2024-02-18T17:14:03.825Z info ResourceSpans #0Resource SchemaURL: Resource attributes: -> telemetry.sdk.language: Str(lua) -> telemetry.sdk.name: Str(opentelemetry-lua) -> telemetry.sdk.version: Str(0.1.1) -> hostname: Str(e34673e24631) -> service.name: Str(APISIX)ScopeSpans #0ScopeSpans SchemaURL: InstrumentationScope opentelemetry-lua Span #0 Trace ID : fbd0a38d4ea4a128ff1a688197bc58b0 Parent ID : ID : af3dc7642104748a Name : GET /anything Kind : Server Start time : 2024-02-18 17:14:03.763244032 +0000 UTC End time : 2024-02-18 17:14:03.920229888 +0000 UTC Status code : Unset Status message : Attributes: -> net.host.name: Str(127.0.0.1) -> http.method: Str(GET) -> http.scheme: Str(http) -> http.target: Str(/anything) -> http.user_agent: Str(curl/7.64.1) -> apisix.route_id: Str(otel-tracing-route) -> apisix.route_name: Empty() -> http.route: Str(/anything) -> http.status_code: Int(200){"kind": "exporter", "data_type": "traces", "name": "debug"} To visualize these traces, you can export your telemetry to backend services, such as Zipkin and Prometheus. See [exporters](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter) for more details. ### Using Trace Variables in Logging[​](https://docs.api7.ai/hub/opentelemetry/#using-trace-variables-in-logging "Direct link to Using Trace Variables in Logging") The following example demonstrates how to configure the `opentelemetry` plugin to set the following built-in variables, which can be used in logger plugins or access logs: * `opentelemetry_context_traceparent`: [trace parent](https://www.w3.org/TR/trace-context/#trace-context-http-headers-format) ID * `opentelemetry_trace_id`: trace ID of the current span * `opentelemetry_span_id`: span ID of the current span Configure the plugin metadata to set `set_ngx_var` as true: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/opentelemetry" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "set_ngx_var": true }' Update the access log format in [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) to use the `opentelemetry` plugin variables as such: conf/config.yaml nginx_config: http: enable_access_log: true access_log_format: '{"time": "$time_iso8601","opentelemetry_context_traceparent": "$opentelemetry_context_traceparent","opentelemetry_trace_id": "$opentelemetry_trace_id","opentelemetry_span_id": "$opentelemetry_span_id","remote_addr": "$remote_addr"}' access_log_format_escape: json [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for configuration changes to take effect. You should see access log entries similar to the following when you generate requests: {"time": "18/Feb/2024:15:09:00 +0000","opentelemetry_context_traceparent": "00-fbd0a38d4ea4a128ff1a688197bc58b0-8f4b9d9970a02629-01","opentelemetry_trace_id": "fbd0a38d4ea4a128ff1a688197bc58b0","opentelemetry_span_id": "af3dc7642104748a","remote_addr": "172.10.0.1"} * [Examples](https://docs.api7.ai/hub/opentelemetry/#examples) * [Enable `opentelemetry` Plugin](https://docs.api7.ai/hub/opentelemetry/#enable-opentelemetry-plugin) * [Send Traces to OpenTelemetry](https://docs.api7.ai/hub/opentelemetry/#send-traces-to-opentelemetry) * [Using Trace Variables in Logging](https://docs.api7.ai/hub/opentelemetry/#using-trace-variables-in-logging) --- # SkyWalking | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/skywalking/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `skywalking` plugin supports the integrating with [Apache SkyWalking](https://skywalking.apache.org/) for request tracing. SkyWalking uses its native Nginx Lua tracer to provide tracing, topology analysis, and metrics from both service and URI perspectives. APISIX supports HTTP protocol to interact with the SkyWalking server. Example[​](https://docs.api7.ai/hub/skywalking/#example "Direct link to Example") ---------------------------------------------------------------------------------- To follow along the example, start a storage, OAP and Booster UI with Docker Compose, following [Skywalking's documentation](https://skywalking.apache.org/docs/main/next/en/setup/backend/backend-docker/) . Once set up, the OAP server should be listening on `12800` and you should be able to access the UI at [http://localhost:8080](http://localhost:8080/) . Update APISIX [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) to enable the `skywalking` plugin, which is disabled by default, and update the endpoint address: config.yaml plugins: - skywalking - ...plugin_attr: skywalking: report_interval: 3 service_name: APISIX service_instance_name: APISIX Instance endpoint_addr: http://192.168.2.103:12800 [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for configuration changes to take effect. ### Trace All Requests[​](https://docs.api7.ai/hub/skywalking/#trace-all-requests "Direct link to Trace All Requests") The following example demonstrates how you can trace all requests passing through a route. Create a route with `skywalking` and configure the sampling ratio to be 1 to trace all requests: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "skywalking-route", "uri": "/anything", "plugins": { "skywalking": { "sample_ratio": 1 } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Send a few requests to the route: curl -i "http://127.0.0.1:9080/anything" You should receive `HTTP/1.1 200 OK` responses. In [Skywalking UI](http://localhost:8080/) , navigate to **General Service** > **Services**. You should see a service called `APISIX` with traces corresponding to your requests: ![SkyWalking APISIX traces](https://static.api7.ai/uploads/2025/01/15/UdwiO8NJ_skywalking-traces.png) ### Associate Traces with Logs[​](https://docs.api7.ai/hub/skywalking/#associate-traces-with-logs "Direct link to Associate Traces with Logs") The following example demonstrates how you can configure the `skywalking-logger` plugin on a route to log information of requests hitting the route. Create a route with the `skywalking-logger` plugin and configure the plugin with your OAP server URI: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "skywalking-logger-route", "uri": "/anything", "plugins": { "skywalking": { "sample_ratio": 1 }, "skywalking-logger": { "endpoint_addr": "http://192.168.2.103:12800" } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Generate a few requests to the route: curl -i "http://127.0.0.1:9080/anything" You should receive `HTTP/1.1 200 OK` responses. In [Skywalking UI](http://localhost:8080/) , navigate to **General Service** > **Services**. You should see a service called `APISIX` with a trace corresponding to your request, where you can view the associated logs: ![trace context](https://static.api7.ai/uploads/2025/01/16/soUpXm6b_trace-view-logs.png) ![associated log](https://static.api7.ai/uploads/2025/01/16/XD934LvU_associated-logs.png) * [Example](https://docs.api7.ai/hub/skywalking/#example) * [Trace All Requests](https://docs.api7.ai/hub/skywalking/#trace-all-requests) * [Associate Traces with Logs](https://docs.api7.ai/hub/skywalking/#associate-traces-with-logs) --- # SAML Auth | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/saml-auth/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `saml-auth` plugin enables API7 to act as the service provider (SP) and authenticate users via [SAML 2.0](https://en.wikipedia.org/wiki/SAML_2.0) by interacting with identity providers (IdP). Example[​](https://docs.api7.ai/hub/saml-auth/#example "Direct link to Example") --------------------------------------------------------------------------------- ### Integrate with Keycloak[​](https://docs.api7.ai/hub/saml-auth/#integrate-with-keycloak "Direct link to Integrate with Keycloak") The following example assumes you have installed [API7 Enterprise](https://docs.api7.ai/enterprise/getting-started/install-api7-ee#install-api7-enterprise) locally and demonstrates how you can set up SAML single sign-on (SSO) with Keycloak. #### Start a Keycloak Server[​](https://docs.api7.ai/hub/saml-auth/#start-a-keycloak-server "Direct link to Start a Keycloak Server") Start a Keycloak instance with admin username `admin` and admin password `admin-pass`: docker run -d --name keycloak \ -e 'KEYCLOAK_ADMIN=admin' \ -e 'KEYCLOAK_ADMIN_PASSWORD=admin-pass' \ -p 8080:8080 \ quay.io/keycloak/keycloak:25.0.4 start-dev Once started, visit [`http://localhost:8080`](http://localhost:8080/) in browser to access the Keycloak admin console. Log in with the admin username and password. #### Create a Client[​](https://docs.api7.ai/hub/saml-auth/#create-a-client "Direct link to Create a Client") Create a new client in Keycloak and configure the following: * Configure the **Client type** as `SAML`. * Enter the service provider's (SP) name as the **Client ID**, for example, `api7`. * Note that this value should be consistent with the `sp_issuer` parameter value, which you will configure later in the plugin. * Add `http://127.0.0.1:9080/anything/login_callback` to **Valid redirect URIs**. * Add `http://127.0.0.1:9080/anything/logout_callback` to **Valid post logout redirect URIs**. * Set **Force POST binding** option to `Off`. * Make sure the **Sign documents** option is `On`. Otherwise, `SigAlg` and `Signature` will be missing from the SAML response. #### Find the Realm's SAML Metadata[​](https://docs.api7.ai/hub/saml-auth/#find-the-realms-saml-metadata "Direct link to Find the Realm's SAML Metadata") In this step, you will find the `idp_cert` and `idp_uri` from the realm's SAML metadata file. Select **Realm Settings** and under the **General** tab, you should find **SAML 2.0 Identity Provider Metadata** in **Endpoints**. The metadata file should look similar to the following: wDDsXcgLGAZwZgpSb_jlBRf5MF8FoTcOYs0DgZ30Xcc MIICmzCCAYMCBgGRl7njKjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjQwODI4MDY0MjA3WhcNMzQwODI4MDY0MzQ3WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdYPYSFoX2MADSIgfLYQ5oZcLNE+qB+qsO8sNpiebMQE3RmI5+MmZC/aozRzkzxcY+AoM50qfHrM1yM99A9ZxZt6fW/MuIv6IP5zWLDl0XWGVeOH0HIH4/xBxQetBxm1HdOYpCQg5Wm9hmYfebmN7NfW8HjnORjfUuUGgs5eCiHVqfiCfphLF5w+DcIcnjIwyF+xVH/7fRWgo5inBSeIavZh/LEv7LzBeRleGgoZ/+q7cVQiL2e0b8rsslqUOZJmwdPU3VSS0vW1bmXsZsfaZD0bgakFvSj0ARzwIbxc74eEQYKflHGS0zkrpm+TsO5KUn59SCPOhGNgGYpKKv6cY1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBABN21PoEiTaZ20qQUdKD03m+bySlF4jRX2AeZqCedBaW+nHrbefaJdEnE9AcXBENCWVr6ntdeREaL9dW6KpV1hT4BmnXO2aiFotZe4Vc2W6cv7nDpjil6Q5/isbT5sriYhcU9oXBAaLf9dlg7K/X1l1+zcy9Pd1uKUfrC+5ds/Zv+xHiiK4h55o8shcmBmQ7bsanzNmjIQNnyF+lNRciGRvgJp59TR7AWpiBQDTNW1KK3XjO9lmN8nCEPbpdNGi77TDX0OZVrbbPy3vL4n8Gi3oQptHhmV7xou4fTEn9TCrdW82OLOduBCMk9t0tFFNB8Hlxq5XsLVLYW7O9GGcjDmI= urn:oasis:names:tc:SAML:2.0:nameid-format:persistent urn:oasis:names:tc:SAML:2.0:nameid-format:transient urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress Note down the certificate content in `X509Certificate` for plugin configuration `idp_cert`. The `SingleSignOnService.Location` URL will also be used in the `idp_uri` plugin configuration later, where the `localhost` should be replace with your private IP address. For example, the `idp_uri` would look similar to `http://192.168.2.101:8080/realms/master/protocol/saml`. #### Create Service Provider (SP) Certificate and Key[​](https://docs.api7.ai/hub/saml-auth/#create-service-provider-sp-certificate-and-key "Direct link to Create Service Provider (SP) Certificate and Key") There are two approaches to create the service provider's certificate and key, and configure the certificate in Keycloak: 1. Generate the certificate and private key locally using openssl, and import the certificate to Keycloak client; or 2. Generate the certificate and private key in Keycloak, which automatically configures the certificate in Keycloak. You will save the certificate and private key for plugin configuration later in API7. With the first approach, generate the certificate and private key using the openssl utility: # Generate Private Keyopenssl genrsa -out sp_private_key.pem 2048# Generate Certificate Signing Request (CSR)openssl req -new -key sp_private_key.pem -out sp_csr.pem -subj "/CN=API7"# Generate Self-Signed Certificateopenssl x509 -req -days 365 -in sp_csr.pem -signkey sp_private_key.pem -out sp_cert.pem In Keycloak, go to the client, and under **Keys** tab where you see the **Client signature required** option is set on **On**, you should see an option to **Import Key** for the **Certificate**. Choose **Certificate PEM** as the **Archive format** and import `sp_cert.pem`. Alternatively if you wish to use the second approach, to generate the certificate and key in Keycloak, you can click **Regenerate** under **Certificate**. This will update the certificate configured in the client and download the private key to your host. #### Create a Route with `saml-auth` Plugin[​](https://docs.api7.ai/hub/saml-auth/#create-a-route-with-saml-auth-plugin "Direct link to create-a-route-with-saml-auth-plugin") In API7 Dashboard, create a service called `httpbin` pointing to `httpbin.org` and add a route `/anything` allowing all HTTP methods. If you are not sure how to do so, see [create service and route](https://docs.api7.ai/enterprise/getting-started/launch-your-first-api#create-service-and-route) . Next, add the `saml-auth` plugin to the route and use the following configuration. { "sp_issuer": "api7", "idp_uri": "http://192.168.2.101:8080/realms/master/protocol/saml", "login_callback_uri": "/anything/login_callback", "logout_callback_uri": "/anything/logout_callback", "logout_uri": "/anything/logout", "logout_redirect_uri": "/anything/logout_ok", "idp_cert": "-----BEGIN CERTIFICATE-----\nMIICmzCCAYMCBgGRl7njKjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0\nZXIwHhcNMjQwODI4MDY0MjA3WhcNMzQwODI4MDY0MzQ3WjARMQ8wDQYDVQQDDAZt\nYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdYPYSFoX2MADS\nIgfLYQ5oZcLNE+qB+qsO8sNpiebMQE3RmI5+MmZC/aozRzkzxcY+AoM50qfHrM1y\nM99A9ZxZt6fW/MuIv6IP5zWLDl0XWGVeOH0HIH4/xBxQetBxm1HdOYpCQg5Wm9hm\nYfebmN7NfW8HjnORjfUuUGgs5eCiHVqfiCfphLF5w+DcIcnjIwyF+xVH/7fRWgo5\ninBSeIavZh/LEv7LzBeRleGgoZ/+q7cVQiL2e0b8rsslqUOZJmwdPU3VSS0vW1bm\nXsZsfaZD0bgakFvSj0ARzwIbxc74eEQYKflHGS0zkrpm+TsO5KUn59SCPOhGNgGY\npKKv6cY1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBABN21PoEiTaZ20qQUdKD03m+\nbySlF4jRX2AeZqCedBaW+nHrbefaJdEnE9AcXBENCWVr6ntdeREaL9dW6KpV1hT4\nBmnXO2aiFotZe4Vc2W6cv7nDpjil6Q5/isbT5sriYhcU9oXBAaLf9dlg7K/X1l1+\nzcy9Pd1uKUfrC+5ds/Zv+xHiiK4h55o8shcmBmQ7bsanzNmjIQNnyF+lNRciGRvg\nJp59TR7AWpiBQDTNW1KK3XjO9lmN8nCEPbpdNGi77TDX0OZVrbbPy3vL4n8Gi3oQ\nptHhmV7xou4fTEn9TCrdW82OLOduBCMk9t0tFFNB8Hlxq5XsLVLYW7O9GGcjDmI=\n-----END CERTIFICATE-----", "sp_cert": "-----BEGIN CERTIFICATE-----\nMIIC0TCCAbmgAwIBAgIUAT7h3zLAul/3S1F9Ms9w7JjpoJ0wDQYJKoZIhvcNAQEL\nBQAwETEPMA0GA1UEAwwGQVBJU0lYMB4XDTI0MDgyNzA5MDk1NloXDTI1MDgyNzA5\nMDk1NlowETEPMA0GA1UEAwwGQVBJU0lYMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\nMIIBCgKCAQEAvJBsuNfgvxe+xrBPr9+OCwD4dk3M9ua+14l9tlQHFgtqGXEq7nYc\n0ic9wqim+kdxpJWfiwG0mClklO0nELNsgBVrC06FqrcSe2CGEh91UBkGEOzvOgm7\nEBJOB/5Nc4tE/3NXM0ocfRgFXNEvGMkH9M+odGk7ZQraI/hfazwYgjOty1LrvSMp\nKhCfx0DpKlLuX0w2P9CfLuSgZ0ZTdN3Yr4icEuEs0i3ptCd/bip2fccKkRWEguIe\nywoDl/2fjubJFc5sFhl7Rtf+CeFKgqeByNPX2+UCix136L1r+VIlA+3ClInPWZUY\nWCbs/envBO6omUsnqPPCU2zVdYW0Qb+rrQIDAQABoyEwHzAdBgNVHQ4EFgQUvGIj\nuvPoHC74lhKSlOJAwrdq4WwwDQYJKoZIhvcNAQELBQADggEBAJU0+aKCUSYvN6oe\n7PHYD0ZvE13wItzKq/7DQQe1zA/kDoCvSyC8+gB+FZmdHmkGGNdNqXsQgHEnP7Y0\nx7gDqA3s0blXEkECfmmRcVxcS3rb8CVVFqiKdyRO91opdir5J9vbmiF7RK1ajFTy\nyemhK0xxFpPM+gTdetEj7AoVMrlRoOLC+L62GaSi/gpQmKPR91FLyj33vCfVrDCo\nQXYMPQmSbBCwlHrHWa/Px7F7aQ3fuwmY6jgObxewl3HUSCfV1TT4/uYV9GsrVx4p\np9LcyuVBuJroIlCJrk5Q/ozGhuiRoKApaTeUSjy5opziBRC2bF+TIxbO9Mkibtbh\nxvXZ4WE=\n-----END CERTIFICATE-----", "sp_private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8kGy41+C/F77G\nsE+v344LAPh2Tcz25r7XiX22VAcWC2oZcSrudhzSJz3CqKb6R3GklZ+LAbSYKWSU\n7ScQs2yAFWsLToWqtxJ7YIYSH3VQGQYQ7O86CbsQEk4H/k1zi0T/c1czShx9GAVc\n0S8YyQf0z6h0aTtlCtoj+F9rPBiCM63LUuu9IykqEJ/HQOkqUu5fTDY/0J8u5KBn\nRlN03diviJwS4SzSLem0J39uKnZ9xwqRFYSC4h7LCgOX/Z+O5skVzmwWGXtG1/4J\n4UqCp4HI09fb5QKLHXfovWv5UiUD7cKUic9ZlRhYJuz96e8E7qiZSyeo88JTbNV1\nhbRBv6utAgMBAAECggEAFPBeulnykZXD8BFVD/0dq1gkvxJdn884wvt4E76Z+Nc0\npXWdJFTGV4nXAF41CJbVZkbdLBT45mq2ShlZnK+n7UMzm1JRYocozL2Htcx7fPUC\naO++ku3QsXSu6JFTLXD6LPm0ZbQlnLiFo+xws+pi8Ur79E1ZNJuzZIooomJOgGqm\nz/0aTCw1JbMXAI7x0ygCYarfhqX4/M6qokV0Nt64hHxHxtrIWzVac+1QdR4WLaFL\nbdrb6QQeeCw5rWUrZfqmF6+NwCCeP5k/HMeVSwXsI+WrEVCjQBB2qpFqgiDNyfz2\n2i7UYXBP0PUmHEPsctWCYlWwqskBxLZnJdDKTmBCKwKBgQDpXpUpNgaI1LOrhxEQ\n5v1iXDSJweV8Kcdth+e6IGFLtxBgvhDNCijBhwKaFe90SFRldGQgZrz4tBKcxdEw\nslGbbNSSmVZ7nSMpZQoV74Uyrk2i7vxq6A9+ZCMWFpFIwoFBz4SUpnwEe+TEe/l3\nAMOz8BdFa3J0XzUhL7k5X+KaewKBgQDO2Yvi84JhwcmgRzlhv5o6gD40C5x1dv5w\nRqnXxnZGigVwtSBS6CoayBtL8MYNdTB5oM6qoF/FiVHYxbnwgD3d4net/BbUYz9H\nkONxwuEM0a35uSf2FHCaRn7BDPjmbdNmrkWr0bHyjlNAd1CQiwmeLxnaTwjf3C+M\nTdI+p08t9wKBgDms84Zk4MaOcv0we2pG/FaD3UQylInUNYJ/dSjN+d3hl32hW7uh\nCCOUP3NfenetrJYKZviPC6MXtgXi6el0GLEl+39jwDj6xAbl/tEfCjdVVsCu+dle\nEv40t2stFqj50UI3jFfEsZ/WEtrwnN3pZXSiIM46WOYj5ZiXF9rzNKjjAoGBAJcv\nwKPf8fM7rgA9Lr64SaTqqQxnVDMzByPPMkKpJzfFl9ZaPMb8NBIhInpuAIRDnGu5\n0nQ6BeYeyTjUxGP5h76O0YTUVWdlJxJK30L9+nnhI/T7lS6yn97TGcBGmAHsUfCh\n/gBoo1SzHDxpOPR8+0moCZBb5hOhHwvAsaPjq+bfAoGBALV/t+smBLogJOBXpfKU\n9LCXnnIqG804vyobSNCVoJm832gBTM7fVcTZa5I+0O+l1emEETIgKU+5ioP/qwou\nU3a/7jXX4hewCpmPVhvvlHgjs+UOBS6hXQMnq52h6mPhiikGOQ6YnqHtxyFORIlo\ntUlwMjanVlxRKyGJlBYQtADk\n-----END PRIVATE KEY-----"} tip Replace the `idp_uri` IP address, `idp_cert`, `sp_cert`, and `sp_private_key` with your own values. Click **Save** to save the configuration. #### Verify[​](https://docs.api7.ai/hub/saml-auth/#verify "Direct link to Verify") Navigate to [`http://127.0.0.1:9080/anything/saml-test`](http://127.0.0.1:9080/anything/saml-test) in your browser and log in with your Keycloak credentials. If successful, you should be redirected and see a response similar to the following in your browser: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-CA,en-US;q=0.9,en;q=0.8", "Cookie": "saml_session=90f84a61-cb03-4f8c-8202-5e7b5267bda6", "Host": "127.0.0.1", "Sec-Fetch-Dest": "document", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-Site": "none", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15", "X-Amzn-Trace-Id": "Root=1-66cf4d36-18bbacc80af8987b77b1f5c4", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "GET", "origin": "192.168.65.1, 203.91.85.123", "url": "http://127.0.0.1/anything/saml-test"} * [Example](https://docs.api7.ai/hub/saml-auth/#example) * [Integrate with Keycloak](https://docs.api7.ai/hub/saml-auth/#integrate-with-keycloak) --- # Zipkin | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/zipkin/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page [Zipkin](https://github.com/openzipkin/zipkin) is an open-source distributed tracing system. The `zipkin` plugin instruments APISIX and sends traces to Zipkin based on the [Zipkin API specification](https://zipkin.io/pages/instrumenting.html) . The plugin can also sends traces to other compatible collectors, such as [Jaeger](https://www.jaegertracing.io/docs/1.51/getting-started/#migrating-from-zipkin) and [Apache SkyWalking](https://skywalking.apache.org/docs/main/latest/en/setup/backend/zipkin-trace/#zipkin-receiver) , both of which support Zipkin [v1](https://zipkin.io/zipkin-api/zipkin-api.yaml) and [v2](https://zipkin.io/zipkin-api/zipkin2-api.yaml) APIs. Examples[​](https://docs.api7.ai/hub/zipkin/#examples "Direct link to Examples") --------------------------------------------------------------------------------- The examples below shows different use cases for using the `zipkin` plugin. ### Send Traces to Zipkin[​](https://docs.api7.ai/hub/zipkin/#send-traces-to-zipkin "Direct link to Send Traces to Zipkin") The following example demonstrates how to trace requests to a route and send traces to Zipkin using [Zipkin API v2](https://zipkin.io/zipkin-api/zipkin2-api.yaml) . You will also understand the differences between span version 2 and span version 1. Start a Zipkin instance in Docker: docker run -d --name zipkin -p 9411:9411 openzipkin/zipkin Create a route with `zipkin` and use the default span version 2: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "zipkin-tracing-route", "uri": "/anything", "plugins": { "zipkin": { "endpoint": "http://127.0.0.1:9411/api/v2/spans", "sample_ratio": 1, "span_version": 2 } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Adjust the IP address as needed for Zipkin HTTP endpoint. ❷ Configure the sample ratio to 1 to trace every request. ❸ Set span version to 2. Send a request to the route: curl "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/7.64.1", "X-Amzn-Trace-Id": "Root=1-65af2926-497590027bcdb09e34752b78", "X-B3-Parentspanid": "347dddedf73ec176", "X-B3-Sampled": "1", "X-B3-Spanid": "429afa01d0b0067c", "X-B3-Traceid": "aea58f4b490766eccb08275acd52a13a", "X-Forwarded-Host": "127.0.0.1" }, ...} Navigate to the Zipkin web UI at [http://127.0.0.1:9411/zipkin](http://127.0.0.1:9411/zipkin) and click **Run Query**, you should see a trace corresponding to the request: ![trace-from-request](https://static.api7.ai/uploads/2024/01/23/MaXhacYO_zipkin-run-query.png) Click **Show** to see more tracing details: ![v2-trace-spans](https://static.api7.ai/uploads/2024/01/23/3SmfFq9f_trace-details.png) Note that with span version 2, every traced request creates the following spans: request├── proxy└── response where `proxy` represents the time from the beginning of the request to the beginning of `header_filter`, and `response` represents the time from the beginning of `header_filter` to the beginning of `log`. Now, update the plugin on the route to use span version 1: curl "http://127.0.0.1:9180/apisix/admin/routes/zipkin-tracing-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "zipkin": { "span_version": 1 } } }' Send another request to the route: curl "http://127.0.0.1:9080/anything" In the Zipkin web UI, you should a new trace with details similar to the following: ![v1-trace-spans](https://static.api7.ai/uploads/2024/01/23/OPw2sTPa_v1-trace-spans.png) Note that with the older span version 1, every traced request creates the following spans: request├── rewrite├── access└── proxy └── body_filter ### Send Traces to Jaeger[​](https://docs.api7.ai/hub/zipkin/#send-traces-to-jaeger "Direct link to Send Traces to Jaeger") The following example demonstrates how to trace requests to a route and send traces to Jaeger. Start a Jaeger instance in Docker: docker run -d --name jaeger \ -e COLLECTOR_ZIPKIN_HOST_PORT=9411 \ -p 16686:16686 \ -p 9411:9411 \ jaegertracing/all-in-one Create a route with `zipkin`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "zipkin-tracing-route", "uri": "/anything", "plugins": { "zipkin": { "endpoint": "http://127.0.0.1:9411/api/v2/spans", "sample_ratio": 1 } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Adjust the IP address as needed for Zipkin HTTP endpoint. ❷ Configure the sample ratio to 1 to trace every request. Send a request to the route: curl "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Navigate to the Jaeger web UI at [http://127.0.0.1:16686](http://127.0.0.1:16686/) , select APISIX as the service, and click **Find Traces**, you should see a trace corresponding to the request: ![jaeger-traces](https://static.api7.ai/uploads/2024/01/23/X6QdLN3l_jaeger.png) Similarly, you should find more span details once you click into a trace: ![jaeger-details](https://static.api7.ai/uploads/2024/01/23/iP9fXI2A_jaeger-details.png) ### Using Trace Variables in Logging[​](https://docs.api7.ai/hub/zipkin/#using-trace-variables-in-logging "Direct link to Using Trace Variables in Logging") The following example demonstrates how to configure the `zipkin` plugin to set the following built-in variables, which can be used in logger plugins or access logs: * `zipkin_context_traceparent`: [trace parent](https://www.w3.org/TR/trace-context/#trace-context-http-headers-format) ID * `zipkin_trace_id`: trace ID of the current span * `zipkin_span_id`: span ID of the current span Update the [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) as such: conf/config.yaml nginx_config: http: enable_access_log: true access_log_format: '{"time": "$time_iso8601","zipkin_context_traceparent": "$zipkin_context_traceparent","zipkin_trace_id": "$zipkin_trace_id","zipkin_span_id": "$zipkin_span_id","remote_addr": "$remote_addr"}' access_log_format_escape: jsonplugin_attr: zipkin: set_ngx_var: true ❶ `access_log_format`: customize the access log format to use the `zipkin` plugin variables. ❷ `set_ngx_var`: set `zipkin` variables. [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for configuration changes to take effect. You should see access log entries similar to the following when you generate requests: {"time": "23/Jan/2024:06:28:00 +0000","zipkin_context_traceparent": "00-61bce33055c56f5b9bec75227befd142-13ff3c7370b29925-01","zipkin_trace_id": "61bce33055c56f5b9bec75227befd142","zipkin_span_id": "13ff3c7370b29925","remote_addr": "172.28.0.1"} * [Examples](https://docs.api7.ai/hub/zipkin/#examples) * [Send Traces to Zipkin](https://docs.api7.ai/hub/zipkin/#send-traces-to-zipkin) * [Send Traces to Jaeger](https://docs.api7.ai/hub/zipkin/#send-traces-to-jaeger) * [Using Trace Variables in Logging](https://docs.api7.ai/hub/zipkin/#using-trace-variables-in-logging) --- # OpenAPI to MCP | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/openapi-to-mcp/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `openapi-to-mcp` plugin enables the gateway to act as a bridge between OpenAPI specifications and MCP (Model Context Protocol) servers. With this plugin, you can expose your existing OpenAPI-based services through an MCP interface, making them accessible to AI models and clients. The plugin works by converting your OpenAPI specification into the MCP format and serving it through an MCP server interface. Requests from AI clients are then proxied to your upstream services, with support for custom headers and two transport methods for streaming responses: streamable HTTP and Server-Sent Events (SSE), allowing flexible and reliable real-time communication. The following diagram illustrates the interaction between the MCP client, API7 gateway, and an upstream OpenAPI service. The path and data are example values for demonstration. Demo[​](https://docs.api7.ai/hub/openapi-to-mcp/#demo "Direct link to Demo") ----------------------------------------------------------------------------- The following example demonstrates how to [enable MCP access to Petstore APIs](https://docs.api7.ai/hub/openapi-to-mcp/#enable-mcp-access-to-petstore-apis) , allowing AI models and clients to interact with the Petstore service. When configured correctly, the AI client should immediately see available Petstore tools; if tools aren't appearing, verify the OpenAPI specification URL is accessible and the gateway address is reachable from your AI client environment. Examples[​](https://docs.api7.ai/hub/openapi-to-mcp/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `openapi-to-mcp` plugin for different scenarios. ### Enable MCP Access to Petstore APIs[​](https://docs.api7.ai/hub/openapi-to-mcp/#enable-mcp-access-to-petstore-apis "Direct link to Enable MCP Access to Petstore APIs") The following example demonstrates how to expose the Petstore APIs through the MCP protocol, allowing AI models and clients to interact with the Petstore service. * Admin API * ADC * Ingress Controller Create a route with the `openapi-to-mcp` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "openapi-to-mcp-route", "uri": "/mcp", "methods": ["GET", "POST"], "plugins": { "openapi-to-mcp": { "transport": "streamable_http", "base_url": "https://petstore3.swagger.io/api/v3", "headers": { "Authorization": "special-key" }, "openapi_url": "https://petstore3.swagger.io/api/v3/openapi.json" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Configure the route to allow GET and POST methods. The GET method enables the tool discovery and response streaming (SSE), while the POST method enables the execution and action capabilities (messages). ❷ Configure the transport method to be `streamable_http` (recommended for production). ❸ Configure the Petstore API address. ❹ Configure the Petstore API credential. ❺ Configure the Petstore OpenAPI document URL. ❻ Configure any value for the upstream node. The upstream address will not be used for actual request forwarding. Create a route with the `openapi-to-mcp` plugin configured as such: adc.yaml services: - name: openapi-to-mcp-service routes: - name: openapi-to-mcp-route uris: - /mcp methods: - GET - POST plugins: openapi-to-mcp: transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Configure the route to allow GET and POST methods. The GET method enables the tool discovery and response streaming (SSE), while the POST method enables the execution and action capabilities (messages). ❷ Configure the transport method to be `streamable_http` (recommended for production). ❸ Configure the Petstore API address. ❹ Configure the Petstore API credential. ❺ Configure the Petstore OpenAPI document URL. ❻ Configure any value for the upstream node. The upstream address will not be used for actual request forwarding. * Gateway API * APISIX CRD Create a route with the `openapi-to-mcp` plugin configured as such: openapi-to-mcp-ic.yaml apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: openapi-to-mcp-plugin-configspec: plugins: - name: openapi-to-mcp config: transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json"---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: openapi-to-mcp-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /mcp method: GET - path: type: Exact value: /mcp method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: openapi-to-mcp-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f openapi-to-mcp-ic.yaml ❶ Configure the transport method to be `streamable_http` (recommended for production). ❷ Configure the Petstore API address. ❸ Configure the Petstore API credential. ❹ Configure the Petstore OpenAPI document URL. ❺ Configure the route to allow GET and POST methods. The GET method enables the tool discovery and response streaming (SSE), while the POST method enables the execution and action capabilities (messages). Create a route with the `openapi-to-mcp` plugin configured as such: openapi-to-mcp-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: openapi-to-mcp-routespec: ingressClassName: apisix http: - name: openapi-to-mcp-route match: paths: - /mcp methods: - GET - POST upstreams: - name: httpbin-external-domain plugins: - name: openapi-to-mcp enable: true config: transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" Apply the configuration to your cluster: kubectl apply -f openapi-to-mcp-ic.yaml ❶ Configure the route to allow GET and POST methods. The GET method enables the tool discovery and response streaming (SSE), while the POST method enables the execution and action capabilities (messages). ❷ Configure the transport method to be `streamable_http` (recommended for production). ❸ Configure the Petstore API address. ❹ Configure the Petstore API credential. ❺ Configure the Petstore OpenAPI document URL. In your AI client, such as Cursor, update the MCP settings with your API7 Gateway address and append the previously created route path. For instance: mcp.json { "mcpServers": { "api7-petstore-mcp": { "url": "http://123.123.123.123:9080/mcp" } }} If the configuration is successful, you should see the available tools (external functions or services exposed to AI clients through MCP). You can now interact with API7 Enterprise directly from the chat window of your AI client. For example, try asking: “How many pets are there in the petstore?” ![AI client interaction with Petstore](https://static.api7.ai/uploads/2025/09/22/6TE6DgXy_oet.png) ### Configure Authentication for MCP Routes[​](https://docs.api7.ai/hub/openapi-to-mcp/#configure-authentication-for-mcp-routes "Direct link to Configure Authentication for MCP Routes") The following example demonstrates how to expose Petstore APIs through the MCP protocol when the route is protected by an authentication method such as `key-auth`. * Admin API * ADC * Ingress Controller Create a consumer `johndoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "johndoe" }' Configure the `key-auth` credential for `johndoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Create a route with the `openapi-to-mcp` and `key-auth` plugins: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "openapi-to-mcp-route", "uri": "/mcp", "methods": ["GET", "POST"], "plugins": { "openapi-to-mcp": { "transport": "streamable_http", "base_url": "https://petstore3.swagger.io/api/v3", "headers": { "Authorization": "special-key" }, "openapi_url": "https://petstore3.swagger.io/api/v3/openapi.json" }, "key-auth": { "header": "apikey" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Create a consumer and a route with the `openapi-to-mcp` and `key-auth` plugins configured as such: adc.yaml consumers: - name: johndoe plugins: key-auth: key: john-keyservices: - name: openapi-to-mcp-service routes: - name: openapi-to-mcp-route uris: - /mcp methods: - GET - POST plugins: key-auth: header: apikey openapi-to-mcp: transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Create a consumer and a route with the `openapi-to-mcp` and `key-auth` plugins configured as such: openapi-to-mcp-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: johndoespec: gatewayRef: name: apisix credentials: - type: key-auth name: primary-key config: key: john-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: openapi-to-mcp-plugin-configspec: plugins: - name: key-auth config: header: apikey - name: openapi-to-mcp config: transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json"---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: openapi-to-mcp-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /mcp method: GET - path: type: Exact value: /mcp method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: openapi-to-mcp-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f openapi-to-mcp-ic.yaml Create an ApisixConsumer and a route with the `openapi-to-mcp` and `key-auth` plugins configured as such: openapi-to-mcp-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: name: johndoespec: authParameter: keyAuth: value: key: john-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: openapi-to-mcp-routespec: ingressClassName: apisix http: - name: openapi-to-mcp-route match: paths: - /mcp methods: - GET - POST upstreams: - name: httpbin-external-domain plugins: - name: key-auth enable: true config: header: apikey - name: openapi-to-mcp enable: true config: transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" Apply the configuration to your cluster: kubectl apply -f openapi-to-mcp-ic.yaml When an MCP server requires authentication, you can specify headers in the `mcp.json` configuration. Refer to the documentation of your AI client to verify whether headers are supported. #### If Headers Are Supported[​](https://docs.api7.ai/hub/openapi-to-mcp/#if-headers-are-supported "Direct link to If Headers Are Supported") For example, in Cursor you can update the MCP settings with your API7 Gateway address, append the previously created route path, and include the header required for `key-auth`: mcp.json { "mcpServers": { "api7-petstore-mcp": { "url": "http://123.123.123.123:9080/mcp", "headers": { "apikey": "john-key" } } }} The configured headers will be added to both GET and POST requests. If the configuration is successful, you should see the available tools (external functions or services exposed to AI clients through MCP). You can then interact with Petstore directly from the chat window of your AI client. If the authentication header is not configured in `mcp.json`, the AI client will be unable to load tools from the MCP server. #### If Headers Are Not Supported[​](https://docs.api7.ai/hub/openapi-to-mcp/#if-headers-are-not-supported "Direct link to If Headers Are Not Supported") If your AI client does not support configuring headers in `mcp.json`, you can include the authentication credential in the MCP URL query, since `key-auth` supports obtaining credential from the URL query. Update the `key-auth` configuration on the route as such: * Admin API * ADC * Ingress Controller curl "http://127.0.0.1:9180/apisix/admin/routes/openapi-to-mcp-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "key-auth": { "_meta": { "filter": [ [ "request_method", "==", "GET" ] ] }, "query": "apikey" } } }' ❶ Only apply the `key-auth` on GET requests. This is because the `apikey` configured in the query parameter is only sent with the GET request to the SSE endpoint. It is not included in the subsequent POST message requests. As a result, the message requests will be blocked by the `key-auth` plugin if the filter is not applied. ❷ Configure the plugin to obtain the authentication key from the query. adc.yaml # other config# ...services: - name: openapi-to-mcp-service routes: - name: openapi-to-mcp-route uris: - /mcp methods: - GET - POST plugins: key-auth: _meta: filter: - - request_method - "==" - GET query: apikey openapi-to-mcp: transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Only apply the `key-auth` on GET requests. This is because the `apikey` configured in the query parameter is only sent with the GET request to the SSE endpoint. It is not included in the subsequent POST message requests. As a result, the message requests will be blocked by the `key-auth` plugin if the filter is not applied. ❷ Configure the plugin to obtain the authentication key from the query. * Gateway API * APISIX CRD Update the PluginConfig: openapi-to-mcp-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: openapi-to-mcp-plugin-configspec: plugins: - name: key-auth config: _meta: filter: - - request_method - "==" - GET query: apikey - name: openapi-to-mcp config: transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" Apply the updated configuration to your cluster: kubectl apply -f openapi-to-mcp-ic.yaml ❶ Only apply the `key-auth` on GET requests. This is because the `apikey` configured in the query parameter is only sent with the GET request to the SSE endpoint. It is not included in the subsequent POST message requests. As a result, the message requests will be blocked by the `key-auth` plugin if the filter is not applied. ❷ Configure the plugin to obtain the authentication key from the query. Update the ApisixRoute: openapi-to-mcp-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: openapi-to-mcp-routespec: ingressClassName: apisix http: - name: openapi-to-mcp-route match: paths: - /mcp methods: - GET - POST plugins: - name: key-auth enable: true config: _meta: filter: - - request_method - "==" - GET query: apikey - name: openapi-to-mcp enable: true config: transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" Apply the updated configuration to your cluster: kubectl apply -f openapi-to-mcp-ic.yaml ❶ Only apply the `key-auth` on GET requests. This is because the `apikey` configured in the query parameter is only sent with the GET request to the SSE endpoint. It is not included in the subsequent POST message requests. As a result, the message requests will be blocked by the `key-auth` plugin if the filter is not applied. ❷ Configure the plugin to obtain the authentication key from the query. In your AI client, include the credential in the API7 Gateway address query parameter: mcp.json { "mcpServers": { "api7-petstore-mcp": { "url": "http://123.123.123.123:9080/mcp?apikey=john-key" } }} If the configuration is successful, you should see the available tools (external functions or services exposed to AI clients through MCP). You can then interact with Petstore directly from the chat window of your AI client. If the authentication credential is not configured in the MCP server URL query, the AI client will be unable to load tools from the MCP server. ### Flatten Tool Schema Parameters[​](https://docs.api7.ai/hub/openapi-to-mcp/#flatten-tool-schema-parameters "Direct link to Flatten Tool Schema Parameters") The following example demonstrates how `flatten_parameters` affects the structure of query and path parameters in the generated MCP tool input schema. Complete the [previous example](https://docs.api7.ai/hub/openapi-to-mcp/#enable-mcp-access-to-petstore-apis) to set up MCP access to the Petstore APIs. Although the configuration does not explicitly set `flatten_parameters`, the parameter defaults to `false`. In your AI client, such as Cursor, inspect the tool input schema. You should see parameters nested under `pathParameters` and `queryParameters`: { "operations": { ..., "getPetById": { "method": "GET", "path": "/pet/{petId}", "pathParameters": { "type": "object", "required": ["petId"], "properties": { "petId": { "type": "integer", "description": "ID of pet to return" } }, "additionalProperties": false } }, "findPetsByStatus": { "method": "GET", "path": "/pet/findByStatus", "queryParameters": { "type": "object", "properties": { "status": { "type": "string", "enum": ["available", "pending", "sold"], "description": "Status values that need to be considered for filter", "default": "available" } }, "additionalProperties": false } } }} Update the plugin to flatten query and path parameters: * Admin API * ADC * Ingress Controller curl "http://127.0.0.1:9180/apisix/admin/routes/openapi-to-mcp-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "openapi-to-mcp": { "flatten_parameters": true } } }' adc.yaml services: - name: openapi-to-mcp-service routes: - name: openapi-to-mcp-route uris: - /mcp methods: - GET - POST plugins: openapi-to-mcp: flatten_parameters: true transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Update the PluginConfig: openapi-to-mcp-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: openapi-to-mcp-plugin-configspec: plugins: - name: openapi-to-mcp config: flatten_parameters: true transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" Apply the updated configuration to your cluster: kubectl apply -f openapi-to-mcp-ic.yaml Update the ApisixRoute: openapi-to-mcp-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: openapi-to-mcp-routespec: ingressClassName: apisix http: - name: openapi-to-mcp-route match: paths: - /mcp methods: - GET - POST plugins: - name: openapi-to-mcp enable: true config: flatten_parameters: true transport: streamable_http base_url: "https://petstore3.swagger.io/api/v3" headers: Authorization: "special-key" openapi_url: "https://petstore3.swagger.io/api/v3/openapi.json" Apply the updated configuration to your cluster: kubectl apply -f openapi-to-mcp-ic.yaml In your AI client, such as Cursor, inspect the tool input schema. You should see that parameters like `status` are no longer nested under `pathParameters` or `queryParameters`: { "operations": { ..., "getPetById": { "parameters": { "type": "object", "required": ["petId"], "properties": { "petId": { "type": "integer", "description": "ID of pet to return" } }, "additionalProperties": false } }, "findPetsByStatus": { "parameters": { "type": "object", "properties": { "status": { "type": "string", "enum": ["available", "pending", "sold"], "description": "Status values that need to be considered for filter", "default": "available" } }, "additionalProperties": false } } }} Troubleshooting[​](https://docs.api7.ai/hub/openapi-to-mcp/#troubleshooting "Direct link to Troubleshooting") -------------------------------------------------------------------------------------------------------------- To diagnose issues, check the `openapi-to-mcp` error log at `/usr/local/openapi2mcp/error.log` in your gateway container or pod. Note that this log is separate from the gateway’s error log. ### Known Issues[​](https://docs.api7.ai/hub/openapi-to-mcp/#known-issues "Direct link to Known Issues") 1. The error `Cannot use 'in' operator to search for '$ref' in undefined` typically occurs when an OpenAPI v2 document is used in `openapi_url`. The plugin only supports OpenAPI v3 document in `openapi_url`. 2. The plugin has a known parsing issue when handling `oneOf` schemas in OpenAPI v3 document retrieved from `openapi_url`. In this case, the MCP client will be stuck at tool loading. * [Demo](https://docs.api7.ai/hub/openapi-to-mcp/#demo) * [Examples](https://docs.api7.ai/hub/openapi-to-mcp/#examples) * [Enable MCP Access to Petstore APIs](https://docs.api7.ai/hub/openapi-to-mcp/#enable-mcp-access-to-petstore-apis) * [Configure Authentication for MCP Routes](https://docs.api7.ai/hub/openapi-to-mcp/#configure-authentication-for-mcp-routes) * [Flatten Tool Schema Parameters](https://docs.api7.ai/hub/openapi-to-mcp/#flatten-tool-schema-parameters) * [Troubleshooting](https://docs.api7.ai/hub/openapi-to-mcp/#troubleshooting) * [Known Issues](https://docs.api7.ai/hub/openapi-to-mcp/#known-issues) --- # Forward Auth | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/forward-auth/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `forward-auth` plugin supports the integration with an external authorization service for authentication and authorization. If the authentication fails, a customizable error message will be returned to the client. If the authentication succeeds, the request will be forwarded to the upstream service along with the following request headers that APISIX added: * `X-Forwarded-Proto`: scheme * `X-Forwarded-Method`: HTTP method * `X-Forwarded-Host`: host * `X-Forwarded-Uri`: URI * `X-Forwarded-For`: source IP Examples[​](https://docs.api7.ai/hub/forward-auth/#examples "Direct link to Examples") --------------------------------------------------------------------------------------- The examples below demonstrate how you can use `forward-auth` for different scenarios. To follow along the first two examples, please have your external authorization service set up, or create a mock auth service using the [serverless function plugin](https://docs.api7.ai/hub/serverless-functions) as shown below: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -H 'Content-Type: application/json' \ -d '{ "id": "auth-mock", "uri": "/auth", "plugins": { "serverless-pre-function": { "phase": "rewrite", "functions": [ "return function (conf, ctx) local core = require(\"apisix.core\"); local authorization = core.request.header(ctx, \"Authorization\"); if authorization == \"123\" then core.response.exit(200); elseif authorization == \"321\" then core.response.set_header(\"X-User-ID\", \"i-am-user\"); core.response.exit(200); else core.response.set_header(\"X-Forward-Auth\", \"Fail\"); core.response.exit(403); end end" ] } } }' The function above implements the following logics: ❶ If the `Authorization` header has a value of `123`, respond with `200 OK`; ❷ If the `Authorization` header has a value of `321`, set a header `X-User-ID: i-am-user` and respond with `200 OK`; ❸ Otherwise, set a header `X-Forward-Auth: Fail` and respond with `403 Forbidden`. ### Forward Designated Headers to Upstream Resource[​](https://docs.api7.ai/hub/forward-auth/#forward-designated-headers-to-upstream-resource "Direct link to Forward Designated Headers to Upstream Resource") The following example demonstrates how to set up `forward-auth` on a route to regulate client access to the resources upstream based on a value in the request header. It also allows passing a specific header from the authorization service to the upstream resource. Create a route with the `forward-auth` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "forward-auth-route", "uri": "/headers", "plugins": { "forward-auth": { "uri": "http://127.0.0.1:9080/auth", "request_headers": ["Authorization"], "upstream_headers": ["X-User-ID"] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ The URI of the authorization service. ❷ The request header that should be forwarded to the authorization service. ❸ The request header set by the authorization service that should be forwarded to the upstream resource when the authorization succeeds. Send a request to the route with authorization detail in the header: curl "http://127.0.0.1:9080/headers" -H 'Authorization: 123' You should see an `HTTP/1.1 200 OK` response of the following: { "headers": { "Accept": "*/*", "Authorization": "123", ... }} To verify if the `X-User-ID` header set by the authorization service will be forwarded to the upstream service, send a request to the route with the corresponding authorization detail: curl "http://127.0.0.1:9080/headers" -H 'Authorization: 321' You should see an `HTTP/1.1 200 OK` response of the following, showing the header is forwarded to the upstream: { "headers": { "Accept": "*/*", "Authorization": "123", "X-User-ID": "i-am-user", ... }} ### Return Designated Headers to Clients on Authentication Failure[​](https://docs.api7.ai/hub/forward-auth/#return-designated-headers-to-clients-on-authentication-failure "Direct link to Return Designated Headers to Clients on Authentication Failure") The following example demonstrates how you can configure `forward-auth` on a route to regulate client access to the upstream resources. It also passes a specific header returned by the authorization service to the client when the authentication fails. Create a route with the `forward-auth` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "forward-auth-route", "uri": "/headers", "plugins": { "forward-auth": { "uri": "http://127.0.0.1:9080/auth", "request_headers": ["Authorization"], "client_headers": ["X-Forward-Auth"] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ Pass the `X-Forward-Auth` header from the authorization service back to the client when authentication fails. Send a request without any authentication information: curl -i "http://127.0.0.1:9080/headers" You should receive an `HTTP/1.1 403 Forbidden` response: ...X-Forward-Auth: FailServer: APISIX/3.x.x403 Forbidden

403 Forbidden


openresty

Powered by APISIX.

### Authorize Based on POST Body[​](https://docs.api7.ai/hub/forward-auth/#authorize-based-on-post-body "Direct link to Authorize Based on POST Body") This example demonstrates how to configure the `forward-auth` plugin to control access based on POST body data, pass values as headers to the authorization service, and reject the request when authorization failed per the body data. Please have your external authorization service set up, or create a mock auth service using the [serverless function plugin](https://docs.api7.ai/hub/serverless-functions) . The function checks if the `tenant_id` header is `123` and returns `200 OK` if it is, otherwise it returns 403 with an error message. curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -H 'Content-Type: application/json' \ -d '{ "id": "auth-mock", "uri": "/auth", "plugins": { "serverless-pre-function": { "phase": "rewrite", "functions": [ "return function(conf, ctx) local core = require(\"apisix.core\") local tenant_id = core.request.header(ctx, \"tenant_id\") if tenant_id == \"123\" then core.response.exit(200); else core.response.exit(403, \"tenant_id is \"..tenant_id .. \" but expecting 123\"); end end" ] } } }' Create a route with the `forward-auth` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "forward-auth-route", "uri": "/post", "methods": ["POST"], "plugins": { "forward-auth": { "uri": "http://127.0.0.1:9080/auth", "request_method": "GET", "extra_headers": {"tenant_id": "$post_arg.tenant_id"} } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ Set an extra header `tenant_id` using the value from the POST parameter `tenant_id`. Send a POST request with `tenant_id` in the body: curl -i "http://127.0.0.1:9080/post" -X POST -d '{ "tenant_id": "123"}' You should receive an `HTTP/1.1 200 OK` response. Send a POST request with `tenant_id` in the body: curl -i "http://127.0.0.1:9080/post" -X POST -d '{ "tenant_id": "000"}' You should receive an `HTTP/1.1 403 Forbidden` response of the following: tenant_id is 000 but expecting 123 * [Examples](https://docs.api7.ai/hub/forward-auth/#examples) * [Forward Designated Headers to Upstream Resource](https://docs.api7.ai/hub/forward-auth/#forward-designated-headers-to-upstream-resource) * [Return Designated Headers to Clients on Authentication Failure](https://docs.api7.ai/hub/forward-auth/#return-designated-headers-to-clients-on-authentication-failure) * [Authorize Based on POST Body](https://docs.api7.ai/hub/forward-auth/#authorize-based-on-post-body) --- # IP Restriction | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ip-restriction/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ip-restriction` plugin supports restricting access to upstream resources by IP addresses, through either configuring a whitelist or blacklist of IP addresses. Restricting IP to resources helps prevent unauthorized access and harden API security. Examples[​](https://docs.api7.ai/hub/ip-restriction/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------- The examples below demonstrate how you can configure the `ip-restriction` plugin for different scenarios. ### Restrict Access by Whitelisting[​](https://docs.api7.ai/hub/ip-restriction/#restrict-access-by-whitelisting "Direct link to Restrict Access by Whitelisting") The following example demonstrates how you can whitelist a list of IP addresses that should have access to the upstream resource and customize the error message for access denial. Create a route with the `ip-restriction` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ip-restriction-route", "uri": "/anything", "plugins": { "ip-restriction": { "whitelist": [ "192.168.0.1/24" ], "message": "Access denied" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Replace with the IP addresses you would like to whitelist. ❷ Customize the error message for when the access is denied. Send a request to the route: curl -i "http://127.0.0.1:9080/anything" If your IP is allowed, you should receive an `HTTP/1.1 200 OK` response. If not, you should receive an `HTTP/1.1 403 Forbidden` response with the following error message: {"message":"Access denied"} ### Restrict Access Using Modified IP[​](https://docs.api7.ai/hub/ip-restriction/#restrict-access-using-modified-ip "Direct link to Restrict Access Using Modified IP") The following example demonstrates how you can modify the IP used for IP restriction, using the `real-ip` plugin. This is particularly useful if APISIX is behind a reverse proxy and the real client IP is not available to APISIX. Create a route as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ip-restriction-route", "uri": "/anything", "plugins": { "ip-restriction": { "whitelist": [ "192.168.1.241" ] }, "real-ip": { "source": "arg_realip" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Obtain client IP address from the URL parameter `realip` using the [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) . Send a request to the route: curl -i "http://127.0.0.1:9080/anything?realip=192.168.1.241" You should receive an `HTTP/1.1 200 OK` response. Send another request with a different IP address: curl -i "http://127.0.0.1:9080/anything?realip=192.168.10.24" You should receive an `HTTP/1.1 403 Forbidden` response. * [Examples](https://docs.api7.ai/hub/ip-restriction/#examples) * [Restrict Access by Whitelisting](https://docs.api7.ai/hub/ip-restriction/#restrict-access-by-whitelisting) * [Restrict Access Using Modified IP](https://docs.api7.ai/hub/ip-restriction/#restrict-access-using-modified-ip) --- # Chaitin WAF | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/chaitin-waf/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `chaitin-waf` plugin integrates with the Chaitin WAF (SafeLine) service to provide advanced detection and prevention of web-based threats, enhancing application security and protecting sensitive user data. Response Headers[​](https://docs.api7.ai/hub/chaitin-waf/#response-headers "Direct link to Response Headers") -------------------------------------------------------------------------------------------------------------- The plugin can add the following response headers, depending on the configuration of `append_waf_resp_header` and `append_waf_debug_header`: | Header | Description | | --- | --- | | `X-APISIX-CHAITIN-WAF` | Indicates whether APISIX forwarded the request to the WAF server.
• `yes`: Request was forwarded to the WAF server.
• `no`: Request was not forwarded to the WAF server.
• `unhealthy`: Request matches the configured rules, but no WAF service is available.
• `err`: An error occurred during plugin execution. The `X-APISIX-CHAITIN-WAF-ERROR` header is also included with details.
• `waf-err`: Error while interacting with the WAF server. The `X-APISIX-CHAITIN-WAF-ERROR` header is also included with details.
• `timeout`: Request to the WAF server timed out. | | `X-APISIX-CHAITIN-WAF-TIME` | Round-trip time (RTT) in milliseconds for the request to the Chaitin WAF server, including both network latency and WAF server processing. | | `X-APISIX-CHAITIN-WAF-STATUS` | Status code returned to APISIX by the WAF server. | | `X-APISIX-CHAITIN-WAF-ACTION` | Action returned to APISIX by the WAF server.
• `pass`: Request was allowed by the WAF service.
• `reject`: Request was blocked by the WAF service. | | `X-APISIX-CHAITIN-WAF-ERROR` | Debug header. Contains WAF error message. | | `X-APISIX-CHAITIN-WAF-SERVER` | Debug header. Indicates which WAF server was selected. | Examples[​](https://docs.api7.ai/hub/chaitin-waf/#examples "Direct link to Examples") -------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `chaitin-waf` plugin for different scenarios. Before proceeding, make sure you have installed [Chaitin WAF (SafeLine)](https://docs.waf.chaitin.com/en/GetStarted/Deploy) . ### Block Malicious Requests on a Route[​](https://docs.api7.ai/hub/chaitin-waf/#block-malicious-requests-on-a-route "Direct link to Block Malicious Requests on a Route") The following example demonstrates how to integrate with Chaitin WAF to protect traffic on a route, rejecting malicious requests immediately. Configure the Chaitin WAF connection details using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) (update the address accordingly): curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/chaitin-waf" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "nodes": [ { "host": "172.22.222.5", "port": 8000 } ] }' Create a route and enable `chaitin-waf` on the route to block requests identified to be malicious: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "chaitin-waf-route", "uri": "/anything", "plugins": { "chaitin-waf": { "mode": "block", "append_waf_resp_header": true, "append_waf_debug_header": true } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Set `mode` to `block` to block requests identified to be malicious. ❷ Set `append_waf_resp_header` to `true` to include WAF-related standard response headers. ❸ Set `append_waf_debug_header` to `true` to include WAF-related debugging response headers. Send a standard request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Send a request with SQL injection to the route: curl -i "http://127.0.0.1:9080/anything" -d 'a=1 and 1=1' You should see an `HTTP/1.1 403 Forbidden` response similar to the following: ...X-APISIX-CHAITIN-WAF-STATUS: 403X-APISIX-CHAITIN-WAF-ACTION: rejectX-APISIX-CHAITIN-WAF-SERVER: 172.22.222.5X-APISIX-CHAITIN-WAF: yesX-APISIX-CHAITIN-WAF-TIME: 3...{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "276be6457d8447a4bf1f792501dfba6c"} ### Monitor Requests for Malicious Intent[​](https://docs.api7.ai/hub/chaitin-waf/#monitor-requests-for-malicious-intent "Direct link to Monitor Requests for Malicious Intent") This example shows how to integrate with Chaitin WAF to monitor all routes with `chaitin-waf` without rejection, and to reject potentially malicious requests on a specific route. Configure the Chaitin WAF connection details using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) (update the address accordingly) and configure the mode: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/chaitin-waf" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "nodes": [ { "host": "172.22.222.5", "port": 8000 } ], "mode": "monitor" }' ❶ Set `mode` to `monitor` in the plugin metadata. This applies to all `chaitin-waf` plugin instances if `mode` is not specified on a route. Create a route and enable `chaitin-waf` without any configuration on the route: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "chaitin-waf-route", "uri": "/anything", "plugins": { "chaitin-waf": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a standard request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Send a request with SQL injection to the route: curl -i "http://127.0.0.1:9080/anything" -d 'a=1 and 1=1' You should also receive an `HTTP/1.1 200 OK` response as the request is not blocked in the `monitor` mode, but observe the following in the log entry: 2025/09/09 11:44:08 [warn] 115#115: *31683 [lua] chaitin-waf.lua:385: do_access(): chaitin-waf monitor mode: request would have been rejected, event_id: 49bed20603e242f9be5ba6f1744bba4b, client: 172.20.0.1, server: _, request: "POST /anything HTTP/1.1", host: "127.0.0.1:9080" If you explicitly configure the `mode` on a route, it will take precedence over the configuration in the plugin metadata. For instance, if you create a route like this: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "chaitin-waf-route", "uri": "/anything", "plugins": { "chaitin-waf": { "mode": "block" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a standard request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Send a request with SQL injection to the route: curl -i "http://127.0.0.1:9080/anything" -d 'a=1 and 1=1' You should see an `HTTP/1.1 403 Forbidden` response similar to the following: ...X-APISIX-CHAITIN-WAF-STATUS: 403X-APISIX-CHAITIN-WAF-ACTION: rejectX-APISIX-CHAITIN-WAF: yesX-APISIX-CHAITIN-WAF-TIME: 3...{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "c3eb25eaa7ae4c0d82eb8ceebf3600d0"} * [Response Headers](https://docs.api7.ai/hub/chaitin-waf/#response-headers) * [Examples](https://docs.api7.ai/hub/chaitin-waf/#examples) * [Block Malicious Requests on a Route](https://docs.api7.ai/hub/chaitin-waf/#block-malicious-requests-on-a-route) * [Monitor Requests for Malicious Intent](https://docs.api7.ai/hub/chaitin-waf/#monitor-requests-for-malicious-intent) --- # ClickHouse Logger | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/clickhouse-logger/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `clickhouse-logger` plugin pushes request and response logs to ClickHouse database in batches and supports the customization of log formats. Examples[​](https://docs.api7.ai/hub/clickhouse-logger/#examples "Direct link to Examples") -------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `clickhouse-logger` plugin for different scenarios. To follow along the examples, start a sample ClickHouse server with user `default` and empty password: docker run -d -p 8123:8123 -p 9000:9000 -p 9009:9009 --name clickhouse-server clickhouse/clickhouse-server ### Log in the Default Log Formats[​](https://docs.api7.ai/hub/clickhouse-logger/#log-in-the-default-log-formats "Direct link to Log in the Default Log Formats") The following example demonstrates how you can log in the default request body. Create a table named `default_logs` in your ClickHouse database with columns corresponding to your log format: curl "http://127.0.0.1:8123" -X POST -d ' CREATE TABLE default.default_logs ( host String, client_ip String, route_id String, service_id String, start_time String, latency String, upstream_latency String, apisix_latency String, consumer String, request String, response String, server String, PRIMARY KEY(`start_time`) ) ENGINE = MergeTree()' --user default: Create a route with `clickhouse-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "clickhouse-logger-route", "uri": "/get", "plugins": { "clickhouse-logger": { "user": "default", "password": "", "database": "default", "logtable": "default_logs", "endpoint_addrs": ["http://127.0.0.1:8123"] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Send a request to the route to generate a log entry: curl -i "http://127.0.0.1:9080/get" You should see an `HTTP/1.1 200 OK` response. Send a request to ClickHouse to see the log entries: echo 'SELECT * FROM default.default_logs FORMAT Pretty' | curl "http://127.0.0.1:8123/?" -d @- You should see a log entry similar to the following: ┏━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┃ host ┃ client_ip ┃ route_id ┃ service_id ┃ start_time ┃ latency ┃ upstream_latency ┃ apisix_latency ┃ consumer ┃ request ┃ response ┃ server ┃┡━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩│ │ 172.19.0.1 │ clickhouse-logger-route │ │ 1703026935235 │ 481.00018501282 │ 473 │ 8.0001850128174 │ │ {"method":"GET","uri":"/get","headers":{"host":"127.0.0.1:9080","user-agent":"curl/7.29.0","accept":"*/*"},"url":"http://127.0.0.1:9080/get","querystring":{},"size":81} │ {"headers":{"access-control-allow-credentials":"true","access-control-allow-origin":"*","content-type":"application/json","content-length":"299","date":"Tue,19 Dec 2023 23:02:15 GMT","connection":"close","server":"APISIX/3.8.0"},"status":200,"size":526} │ {"hostname":"85cf6f06914e","version":"3.8.0"} │└──────┴────────────┴─────────────────────────┴────────────┴───────────────┴─────────────────┴──────────────────┴─────────────────┴──────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────┘ ### Customize Log Format With Plugin Metadata[​](https://docs.api7.ai/hub/clickhouse-logger/#customize-log-format-with-plugin-metadata "Direct link to Customize Log Format With Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) . Create a table named `custom_logs` in your ClickHouse database with columns corresponding to your customized log format: curl "http://127.0.0.1:8123" -X POST -d ' CREATE TABLE default.custom_logs ( host String, client_ip String, route_id String, service_id String, `@timestamp` String, PRIMARY KEY(`@timestamp`) ) ENGINE = MergeTree()' --user default: Create a route with the `clickhouse-logger` plugin that is used to forward logs in the specified format to ClickHouse: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "clickhouse-logger-route", "uri": "/get", "plugins": { "clickhouse-logger": { "user": "default", "password": "", "database": "default", "logtable": "custom_logs", "endpoint_addrs": ["http://127.0.0.1:8123"] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Configure plugin metadata for `clickhouse-logger`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/clickhouse-logger" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "log_format": { "host": "$host", "client_ip": "$remote_addr", "route_id": "$route_id", "service_id": "$service_id", "@timestamp": "$time_iso8601" } }' Send a request to the route to generate a log entry: curl -i "http://127.0.0.1:9080/get" You should see an `HTTP/1.1 200 OK` response. Send a request to ClickHouse to see the log entries: echo 'SELECT * FROM default.custom_logs FORMAT Pretty' | curl "http://127.0.0.1:8123/?" -d @- You should see a log entry similar to the following: ┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┃ host ┃ client_ip ┃ route_id ┃ service_id ┃ @timestamp ┃┡━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩│ 127.0.0.1 │ 172.19.0.1 │ clickhouse-logger-route │ │ 2023-12-19T23:25:43+00:00 │└───────────┴────────────┴─────────────────────────┴────────────┴───────────────────────────┘ * [Examples](https://docs.api7.ai/hub/clickhouse-logger/#examples) * [Log in the Default Log Formats](https://docs.api7.ai/hub/clickhouse-logger/#log-in-the-default-log-formats) * [Customize Log Format With Plugin Metadata](https://docs.api7.ai/hub/clickhouse-logger/#customize-log-format-with-plugin-metadata) --- # CORS | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/cors/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `cors` plugin allows you to enable [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) . CORS is an HTTP-header based mechanism which allows a server to specify any origins (domain, scheme, or port) other than its own, and instructs browsers to allow the loading of resources from those origins. Examples[​](https://docs.api7.ai/hub/cors/#examples "Direct link to Examples") ------------------------------------------------------------------------------- The examples below demonstrate how you can configure routes using the `cors` plugin for different scenarios. ### Enable CORS for a Route[​](https://docs.api7.ai/hub/cors/#enable-cors-for-a-route "Direct link to Enable CORS for a Route") The following example demonstrates how to enable CORS on a route to allow resource loading from a list of origins. Create a route with the `cors` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cors-route", "uri": "/anything", "plugins": { "cors": { "allow_origins": "http://sub.domain.com,http://sub2.domain.com", "allow_methods": "GET,POST", "allow_headers": "headr1,headr2", "expose_headers": "ex-headr1,ex-headr2", "max_age": 50, "allow_credential": true } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `allow_origins`: configure allowed origins, comma-separated. To allow all origins, set it to `*`. ❷ `max_age`: configure the maximum time the result is cached in seconds. ❸ `allow_credential`: set to `true` to allow credentials (cookies, HTTP authentication, and client-side SSL certificates) to be sent with the request. If you set this to true, you cannot use `*` for other cors attributes. Send a head request to the route with an allowed origin: curl "http://127.0.0.1:9080/anything" -H "Origin: http://sub2.domain.com" -I You should receive an `HTTP/1.1 200 OK` response and observe CORS headers: ...Access-Control-Allow-Origin: http://sub2.domain.comAccess-Control-Allow-Credentials: trueServer: APISIX/3.8.0Vary: OriginAccess-Control-Allow-Methods: GET,POSTAccess-Control-Max-Age: 50Access-Control-Expose-Headers: ex-headr1,ex-headr2Access-Control-Allow-Headers: headr1,headr2 Send a head request to the route with an origin that is not allowed: curl "http://127.0.0.1:9080/anything" -H "Origin: http://sub3.domain.com" -I You should receive an `HTTP/1.1 200 OK` response without any CORS header: ...Access-Control-Allow-Origin: http://sub3.domain.comAccess-Control-Allow-Credentials: trueServer: APISIX/3.8.0Vary: Origin ### Use RegEx to Match Origin[​](https://docs.api7.ai/hub/cors/#use-regex-to-match-origin "Direct link to Use RegEx to Match Origin") The following example demonstrates how to use RegEx to match the origin in `allow_origins` using the `allow_origins_by_regex` field. Create a route with the `cors` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cors-route", "uri": "/anything", "plugins": { "cors": { "allow_methods": "GET,POST", "allow_headers": "headr1,headr2", "expose_headers": "ex-headr1,ex-headr2", "max_age": 50, "allow_origins_by_regex": [ ".*\\.test.com$" ] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `allow_origins_by_regex`: allow origins using RegEx. If used together with `allow_origins`, then `allow_origins` will be ignored. Send a head request to the route with an allowed origin: curl "http://127.0.0.1:9080/anything" -H "Origin: http://a.test.com" -I You should receive an `HTTP/1.1 200 OK` response and observe CORS headers: ...Access-Control-Allow-Origin: http://a.test.comAccess-Control-Allow-Credentials: trueServer: APISIX/3.8.0Access-Control-Allow-Methods: GET,POSTAccess-Control-Max-Age: 50Access-Control-Expose-Headers: ex-headr1,ex-headr2Access-Control-Allow-Headers: headr1,headr2 You can also try to make a request with an invalid origin: curl "http://127.0.0.1:9080/anything" -H "Origin: http://a.test2.com" -I You should receive an `HTTP/1.1 200 OK` response without any CORS header: ...Access-Control-Allow-Origin: http://a.test2.comAccess-Control-Allow-Credentials: trueServer: APISIX/3.8.0Vary: Origin ### Configure Origins in Plugin Metadata[​](https://docs.api7.ai/hub/cors/#configure-origins-in-plugin-metadata "Direct link to Configure Origins in Plugin Metadata") The following example demonstrates how to configure origins in [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) and reference them as the allowed origins in the `cors` plugin. Configure plugin metadata for the `cors` plugin: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/cors" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "allow_origins": { "key_1": "https://domain.com", "key_2": "https://sub.domain.com,https://sub2.domain.com", "key_3": "*" } }' ❶ `allow_origins` : a map of keys and allowed origins. The key will be used to match the origin in the route. Create a route with the `cors` plugin using `allow_origins_by_metadata`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cors-route", "uri": "/anything", "plugins": { "cors": { "allow_methods": "GET,POST", "allow_headers": "headr1,headr2", "expose_headers": "ex-headr1,ex-headr2", "max_age": 50, "allow_origins_by_metadata": ["key_1"] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `allow_origins_by_metadata`: keys in the metadata to match the origin. Send a head request to the route with an allowed origin: curl "http://127.0.0.1:9080/anything" -H "Origin: https://domain.com" -I You should receive an `HTTP/1.1 200 OK` response and observe CORS headers: ...Access-Control-Allow-Origin: https://domain.comAccess-Control-Allow-Credentials: trueServer: APISIX/3.8.0Access-Control-Allow-Methods: GET,POSTAccess-Control-Max-Age: 50Access-Control-Expose-Headers: ex-headr1,ex-headr2Access-Control-Allow-Headers: headr1,headr2 Send another request with an invalid origin: curl "http://127.0.0.1:9080/anything" -H "Origin: http://a.test2.com" -I You should receive an `HTTP/1.1 200 OK` response without any CORS header: ...Access-Control-Allow-Origin: http://a.test2.comAccess-Control-Allow-Credentials: trueServer: APISIX/3.8.0Vary: Origin * [Examples](https://docs.api7.ai/hub/cors/#examples) * [Enable CORS for a Route](https://docs.api7.ai/hub/cors/#enable-cors-for-a-route) * [Use RegEx to Match Origin](https://docs.api7.ai/hub/cors/#use-regex-to-match-origin) * [Configure Origins in Plugin Metadata](https://docs.api7.ai/hub/cors/#configure-origins-in-plugin-metadata) --- # Back Up and Restore etcd | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page etcd clusters are generally fault-tolerant. Still, there might be scenarios where multiple etcd nodes may fail and lose access to the cluster. To recover from such failures, etcd supports backup and restore features for recreating instances without data loss. Setting up backups can help store the state of an APISIX instance. This is also useful while upgrading APISIX to ensure your configuration is saved. This document shows how to back up and restore from etcd with both APISIX and etcd running in Docker. Most steps work similarly for other deployments. Prerequisites[​](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#prerequisites "Direct link to Prerequisites") ------------------------------------------------------------------------------------------------------------------------------------ * Complete the [Get APISIX](https://docs.api7.ai/apisix/getting-started/) step to install APISIX. * Complete the [Configure Routes](https://docs.api7.ai/apisix/getting-started/configure-routes) guide to create routes. Back Up through a Temporary Container[​](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#back-up-through-a-temporary-container "Direct link to Back Up through a Temporary Container") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ etcd comes packaged with [etcdctl](https://etcd.io/docs/v3.5/dev-guide/interacting_v3/) , a CLI for working with the etcd APIs. etcdctl supports the [`etcdctl snapshot`](https://etcd.io/docs/v3.3/op-guide/recovery/) command, which can take a backup of the etcd database. The example below runs this command on a temporary Docker container and creates a backup in the host machine. First, create a folder, `backup`, to store the backup in the host machine. This folder is mounted as a volume to the temporary container. Create the temporary container for backing up etcd in the same network as the APISIX and etcd containers and run the `etcdctl snapshot` command providing the etcd endpoints: export NET_NAME=apisix-quickstart-netexport ETCD_LISTEN_PORT=2379export ETCD_NAME="etcd-quickstart"docker run --rm \ -v ./backup:/backup \ -e ETCDCTL_API=3 \ --network "$NET_NAME" \ bitnami/etcd:3.5.7 \ etcdctl --endpoints="http://$ETCD_NAME:$ETCD_LISTEN_PORT" snapshot save /backup/snapshot.db If successful, you should see a response similar to the following: {"level":"info","ts":"2024-03-26T06:14:17.173Z","caller":"snapshot/v3_snapshot.go:65","msg":"created temporary db file","path":"/backup/snapshot.db.part"}{"level":"info","ts":"2024-03-26T06:14:17.178Z","logger":"client","caller":"v3@v3.5.7/maintenance.go:212","msg":"opened snapshot stream; downloading"}{"level":"info","ts":"2024-03-26T06:14:17.178Z","caller":"snapshot/v3_snapshot.go:73","msg":"fetching snapshot","endpoint":"http://etcd-quickstart:2379"}{"level":"info","ts":"2024-03-26T06:14:17.182Z","logger":"client","caller":"v3@v3.5.7/maintenance.go:220","msg":"completed snapshot read; closing"}{"level":"info","ts":"2024-03-26T06:14:17.182Z","caller":"snapshot/v3_snapshot.go:88","msg":"fetched snapshot","endpoint":"http://etcd-quickstart:2379","size":"25 kB","took":"now"}{"level":"info","ts":"2024-03-26T06:14:17.183Z","caller":"snapshot/v3_snapshot.go:97","msg":"saved","path":"/backup/snapshot.db"}Snapshot saved at /backup/snapshot.db Back Up Directly on the etcd Container[​](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#back-up-directly-on-the-etcd-container "Direct link to Back Up Directly on the etcd Container") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- `etcdctl snapshot` can also be run directly on the etcd container. Note that this might require setting up directory permissions for the Docker container. export ETCD_NAME="etcd-quickstart"docker exec -e ETCD_LISTEN_PORT=2379 -it $ETCD_NAME etcdctl --endpoints="http://127.0.0.1:$ETCD_LISTEN_PORT" snapshot save ./backup/snapshot.db Make sure to create volume mounts to the host machine so that the `/backup/snapshot.db` file can be accessed in the host machine. Back Up from the Host Machine[​](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#back-up-from-the-host-machine "Direct link to Back Up from the Host Machine") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ etcdctl can be run on the host machine if the etcd API endpoints are accessible. etcd endpoints can be [exposed securely](https://etcd.io/docs/v3.5/op-guide/security/) through TLS. Note that etcdctl should be installed on the host machine. Download the binaries for the appropriate operating system or use a package manager to install etcdctl. Run etcdctl on the host machine to save the snapshot: export ETCD_LISTEN_PORT=2379export ETCD_ADDRESS="127.0.0.1"etcdctl --endpoints="http://$ETCD_ADDRESS:$ETCD_LISTEN_PORT" snapshot save ./backup/snapshot.db Restore from Backups[​](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#restore-from-backups "Direct link to Restore from Backups") --------------------------------------------------------------------------------------------------------------------------------------------------------- To restore from a backup, run: etcdctl snapshot restore ./backup/snapshot.db * [Prerequisites](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#prerequisites) * [Back Up through a Temporary Container](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#back-up-through-a-temporary-container) * [Back Up Directly on the etcd Container](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#back-up-directly-on-the-etcd-container) * [Back Up from the Host Machine](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#back-up-from-the-host-machine) * [Restore from Backups](https://docs.api7.ai/apisix/production/recovery/etcd-backup-restore/#restore-from-backups) --- # Credentials | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/credentials/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Credentials are data used to verify the identity of an individual or system. They serve as a way to authenticate and authorize access to protected resources, proving that a user or system is who they claim to be. Credentials can take various forms, including but not limited to: * Usernames and passwords: a unique username paired with a secret password. * API keys or tokens: alphanumeric strings generated for applications or users to access APIs securely. In API7 Enterprise, credentials are associated with consumers and contain authentication data. A consumer can be associated with one or more credentials from a designated list of authentication plugins, including `key-auth`, `basic-auth`, `jwt-auth`, and `hmac-auth`. The decoupling of credentials from consumers facilitates credential reuse and rotation as well as enhanced security. Consider a scenario where API keys have a defined lifespan—such as one month—configuring multiple credentials of the same type allows administrators to assign a new API key while the existing one is still valid. The approach provides a buffer period during which both keys are accepted, enabling administrators to update client applications with the new key while avoiding any disruption to active services. Once the new key is in use, the old key can be securely removed, mitigating risks associated with abrupt credential changes and reducing the chance of application downtime or access issues during credential rotation. ![key rotation transition period diagram](https://static.api7.ai/uploads/2024/12/06/B065grtp_credential-concept.png) Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/credentials/#key-features "Direct link to Key Features") ------------------------------------------------------------------------------------------------------------------------ * Have a one-to-many relationship with consumers. * Support the key management of multiple authentication methods, including key authentication, JWT, HMAC, and basic authentication. * Allow for independent management and updates to consumer authentication details, which promotes better security practices, simplifies maintenance, and prevents unintended changes to consumers. * Forward the `X-Credential-Identifier` header containing the configured credential ID to upstream services, which allows for additional business logic to be implemented. * When examining consumer details, the returned information does not expose the consumer-associated credentials, reducing the attack surface. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/credentials/#use-cases "Direct link to Use Cases") --------------------------------------------------------------------------------------------------------------- ### Mitigate Configuration Drift[​](https://docs.api7.ai/apisix/enterprise-feature/credentials/#mitigate-configuration-drift "Direct link to Mitigate Configuration Drift") When configuring authentication credentials directly on consumers, each credential rotation or reconfiguration could inadvertently alter or overwrite existing consumer configurations, potentially leading to service disruptions. When decoupling the credential configuration, the credentials can be updated or rotated without touching the consumer-specific settings, reducing the likelihood of configuration drift—a scenario where unexpected changes accumulate over time, leading to mismatched or conflicting configurations compared with what was originally planned. ### Support Credential Rotation[​](https://docs.api7.ai/apisix/enterprise-feature/credentials/#support-credential-rotation "Direct link to Support Credential Rotation") The ability to configure multiple credentials of the same type for a consumer allows for smooth credential rotation, which is essential for maintaining secure access while minimizing disruptions. For example, in cases where API keys have a defined lifespan—such as one month—having multiple credentials of the same type allows administrators to assign a new API key while the existing one remains valid. This controlled overlap period facilitates a seamless transition, where both keys remain active temporarily, ensuring uninterrupted access for dependent applications. Once the new API key is confirmed to be fully operational, the outdated credential can be safely removed. ### Enhance Security Compliance[​](https://docs.api7.ai/apisix/enterprise-feature/credentials/#enhance-security-compliance "Direct link to Enhance Security Compliance") Regularly rotating credentials, a process that can be facilitated by the use of credentials in API7 Enterprise, is a critical practice for aligning with security frameworks and standards. For example, PCI-DSS (Payment Card Industry Data Security Standard) requires frequent updates of passwords and other credentials for systems handling cardholder data, reducing the risk of unauthorized access due to outdated or compromised credentials. * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/credentials/#key-features) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/credentials/#use-cases) * [Mitigate Configuration Drift](https://docs.api7.ai/apisix/enterprise-feature/credentials/#mitigate-configuration-drift) * [Support Credential Rotation](https://docs.api7.ai/apisix/enterprise-feature/credentials/#support-credential-rotation) * [Enhance Security Compliance](https://docs.api7.ai/apisix/enterprise-feature/credentials/#enhance-security-compliance) --- # Trace Requests with Zipkin | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Traces are one of the three pillars of observability, along with metrics and logs. A trace tracks the journey of a request as it traverses through various parts of a system. It is an effective mechanism that helps developers and administrators monitor system performance, identify bottlenecks, and improve user experience. This guide will walk you through how to trace requests to APISIX using the `zipkin` plugin and send traces to [Zipkin](https://zipkin.io/) . Prerequisite(s)[​](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#prerequisites "Direct link to Prerequisite(s)") --------------------------------------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker) . * Install [cURL](https://curl.se/) to send requests to the services for validation. * Follow the [Getting Started tutorial](https://docs.api7.ai/apisix/getting-started/) to start a new APISIX instance in Docker or on Kubernetes. Start Zipkin Instance[​](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#start-zipkin-instance "Direct link to Start Zipkin Instance") ----------------------------------------------------------------------------------------------------------------------------------------------------------------- Start a Zipkin instance: docker run -d --name zipkin -p 9411:9411 openzipkin/zipkin Configure APISIX[​](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#configure-apisix "Direct link to Configure APISIX") -------------------------------------------------------------------------------------------------------------------------------------------------- Enable `zipkin` globally and create a sample route to generate traces. Alternatively, you can enable the plugin on a route. * Admin API * ADC Configure `zipkin` to be a global plugin: curl "http://127.0.0.1:9180/apisix/admin/global_rules/zipkin" -X PUT -d '{ "plugins": { "zipkin": { "endpoint": "http://192.168.42.145:9411/api/v2/spans", "sample_ratio": 1 } }}' ❶ Set to the `/spans` [Zipkin endpoint](https://zipkin.io/zipkin-api) . Update with your IP address. ❷ Configure the sample ratio to 1 to trace every request. Create a sample route where requests through the route will be traced: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "zipkin-tracing-route", "uri": "/anything", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } }}' Configure `zipkin` to be a global plugin and create a sample route where requests through the route will be traced: adc-route.yaml global_rules: zipkin: endpoint: "http://192.168.42.145:9411/api/v2/spans" sample_ratio: 1services: - name: httpbin Service routes: - uris: - /anything name: zipkin-tracing-route upstream: type: roundrobin nodes: - host: httpbin.org weight: 1 ❶ Set to the `/spans` [Zipkin endpoint](https://zipkin.io/zipkin-api) . ❷ Configure the sample ratio to 1 to trace every request. Synchronize the configuration to APISIX: adc sync -f adc-route.yaml Trace Requests[​](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#trace-requests "Direct link to Trace Requests") -------------------------------------------------------------------------------------------------------------------------------------------- Send a request to the route: curl "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response similar to the following, with trace information in the headers: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/7.64.1", "X-Amzn-Trace-Id": "Root=1-65b1dd40-339dc8832b4b78d36703cbc0", "X-B3-Parentspanid": "6ca01ad46bdb0198", "X-B3-Sampled": "1", "X-B3-Spanid": "f02ab1a9b2d5c3e4", "X-B3-Traceid": "ef5b16781c7ad00ea2e3efa6f784031a", "X-Forwarded-Host": "127.0.0.1" }, ...} Navigate to the Zipkin web UI at [http://127.0.0.1:9411/zipkin](http://127.0.0.1:9411/zipkin) and click **Run Query**, you should see a trace corresponding to the request: ![trace-from-request](https://static.api7.ai/uploads/2024/01/23/MaXhacYO_zipkin-run-query.png) Click **Show** to see more tracing details: ![v2-trace-spans](https://static.api7.ai/uploads/2024/01/23/3SmfFq9f_trace-details.png) By default, the plugin uses span version 2, where every traced request has `proxy` and `response` spans. More specifically: * `proxy` represents the time from the beginning of the request to the beginning of `header_filter` * `response` represents the time from the beginning of `header_filter` to the beginning of `log` To understand the differences between different span versions, see the [plugin doc](https://docs.api7.ai/hub/zipkin#send-traces-to-zipkin) . Next Steps[​](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#next-steps "Direct link to Next Steps") -------------------------------------------------------------------------------------------------------------------------------- You have now integrated Zipkin with APISIX for tracing. In addition to sending traces to Zipkin, the `zipkin` plugin can also send traces to [Jaeger](https://www.jaegertracing.io/docs/1.51/getting-started/#migrating-from-zipkin) and other compatible collectors. See the `zipkin` [plugin doc](https://docs.api7.ai/hub/zipkin#send-traces-to-jaeger) for more details. * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#prerequisites) * [Start Zipkin Instance](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#start-zipkin-instance) * [Configure APISIX](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#configure-apisix) * [Trace Requests](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#trace-requests) * [Next Steps](https://docs.api7.ai/apisix/how-to-guide/observability/trace-with-zipkin/#next-steps) --- # AWS Lambda | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/aws-lambda/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `aws-lambda` plugin eases the integration of APISIX with [AWS Lambda](https://aws.amazon.com/lambda/) and [Amazon API gateway](https://aws.amazon.com/api-gateway/) to proxy for other AWS services. The plugin supports authentication and authorization with AWS via IAM user credentials and API gateway's API key. Examples[​](https://docs.api7.ai/hub/aws-lambda/#examples "Direct link to Examples") ------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `aws-lambda` for different scenarios. To follow along the examples, please first log into your AWS console and create a Lambda function with any runtime. You do not need to customize the function and by default, the function should return `Hello from Lambda!` when called. ### Invoke Lambda Function Securely using IAM Access Keys[​](https://docs.api7.ai/hub/aws-lambda/#invoke-lambda-function-securely-using-iam-access-keys "Direct link to Invoke Lambda Function Securely using IAM Access Keys") The following example demonstrates how you can integrate APISIX with the Lambda function and configure IAM access keys for authorization. The `aws-lambda` plugin implements [AWS Signature Version 4](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-signing.html) for IAM access keys. You will be first creating IAM access keys and the Lambda function URL on AWS console. For IAM access keys, go to **AWS Identity and Access Management (IAM)** and click into the user you would like to use for integration. Next, in the **security credentials** tab, select **create access key**: ![create access keys](https://static.api7.ai/uploads/2024/04/23/1K9FiWb4_create-access-key.png) Select **application running outside AWS** as the use case: ![select use case](https://static.api7.ai/uploads/2024/04/23/Fa4jdK5H_iam-user-use-case.png) Continue the credential creation and note down the access key and secret access key: ![save access keys](https://static.api7.ai/uploads/2024/04/23/zGCyqp20_save-access-key.png) To create the Lambda function URL, go to the **Configuration** tab of the Lambda function and under **Function URL**, create a function URL: ![create function URL](https://static.api7.ai/uploads/2024/04/23/3fF90ws2_function-url.png) Finally, create a route in APISIX with your function URL and IAM access keys: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "aws-lambda-route", "uri": "/aws-lambda", "plugins": { "aws-lambda": { "function_uri": "https://jbihqn4bfwewfrz6typjmcvjh40iuamw.lambda-url.us-west-2.on.aws/", "authorization": { "iam": { "accesskey": "AKIARK7HKSJVWCOIIMW6", "secretkey": "7vb0dmaYP4afRpvtQJ9Tloija729s2g4moUdIdr8", "aws_region": "us-west-2", "service": "lambda" } }, "ssl_verify": false } } }' ❶ replace with your Lambda function URL ❷ replace with your IAM access key ❸ replace with your IAM secret access key ❹ replace with the AWS region of your Lambda function ❺ set to `lambda` when integrating with Lambda function Send a request to the route: curl -i "http://127.0.0.1:9080/aws-lambda" You should receive an `HTTP/1.1 200 OK` response with the following message: "Hello from Lambda!" ### Integrate with Amazon API Gateway Securely with API Key[​](https://docs.api7.ai/hub/aws-lambda/#integrate-with-amazon-api-gateway-securely-with-api-key "Direct link to Integrate with Amazon API Gateway Securely with API Key") The following example demonstrates how you can integrate APISIX with Amazon API gateway and configure the gateway to trigger the execution of Lambda function. To configure an API gateway as a Lambda trigger, go to your Lambda function and select **Add trigger**: ![add trigger for lambda function](https://static.api7.ai/uploads/2024/04/25/UjI9bLDQ_add-trigger.png) Next, select **API Gateway** as the trigger and **REST API** as the API type, and finish adding the trigger: ![select REST to be the API type and secure the API with API key](https://static.api7.ai/uploads/2024/04/25/4Bp9r3UP_rest-api-key.png) info The Amazon API gateway supports two types of RESTful APIs: HTTP APIs and REST APIs. Only REST APIs offer API key and IAM as security measures. You should now be redirected back to the Lambda interface. To find the API key and gateway API endpoint, go to the **Configuration** tab of the Lambda function and under **Triggers**, you can find the details of the API gateway: ![API gateway endpoint and API key](https://static.api7.ai/uploads/2024/04/25/6bjpeNIb_api-gateway-info.png) Finally, create a route in APISIX with your gateway endpoint and API key: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "aws-lambda-route", "uri": "/aws-lambda", "plugins": { "aws-lambda": { "function_uri": "https://xwbs1bjiy3.execute-api.us-west-2.amazonaws.com/default/api7-docs", "authorization": { "apikey": "hpr8KdMxlR37p0Sq8aIhi28cq2thilcF5wziOsvJ" }, "ssl_verify":false } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/aws-lambda" You should receive an `HTTP/1.1 200 OK` response with the following message: "Hello from Lambda!" If your API key is invalid, you should receive an `HTTP/1.1 403 Forbidden` response. ### Forward Requests to Amazon API Gateway Sub-Paths[​](https://docs.api7.ai/hub/aws-lambda/#forward-requests-to-amazon-api-gateway-sub-paths "Direct link to Forward Requests to Amazon API Gateway Sub-Paths") The following example demonstrates how you can forward requests to a sub-path of the Amazon API gateway API and configure the API to trigger the execution of Lambda function. Please follow the [previous example](https://docs.api7.ai/hub/aws-lambda/#integrate-with-amazon-api-gateway-securely-with-api-key) to set up an API gateway first. To create a sub-path, go to the **Configuration** tab of the Lambda function and under **Triggers**, click into the API gateway: ![click into the API gateway](https://static.api7.ai/uploads/2024/04/26/5Twffgyr_click-into-adjusted.png) Next, select **Create resource** to create a sub-path: ![create resource](https://static.api7.ai/uploads/2024/04/26/hXlnuVwk_create-resource.png) Enter the sub-path information and complete creation: ![complete resource creation](https://static.api7.ai/uploads/2024/04/26/7t1yiWjl_create-resource-2.png) Once redirected back to the main gateway console, you should see the newly created path. Select **Create method** to configure HTTP methods for the path and the associated action: ![click on create method](https://static.api7.ai/uploads/2024/04/26/3rZZJy3e_create-method.png) Select the allowed HTTP method in the dropdown. For the purpose of demonstration, this example continues to use the same Lambda function as the triggered action when the path is requested: ![create method and lambda function](https://static.api7.ai/uploads/2024/04/26/vni7yS2q_create%20method%202.png) Finish the method creation. Once redirected back to the main gateway console, click on **Deploy API** to deploy the path and method changes: ![deploy changes to API gateway](https://static.api7.ai/uploads/2024/04/26/2vrqnVPB_deploy-api.png) Finally, create a route in APISIX with your gateway endpoint and API key: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "aws-lambda-route", "uri": "/aws-lambda/*", "plugins": { "aws-lambda": { "function_uri": "https://xwbs1bjiy3.execute-api.us-west-2.amazonaws.com/default", "authorization": { "apikey": "hpr8KdMxlR37p0Sq8aIhi28cq2thilcF5wziOsvJ" }, "ssl_verify":false } } }' ❶ match all sub-paths of `/aws-lambda/` ❷ the sub-paths matched by the wildcard `*` will be appended to the end of the `function_uri` Send a request to the route: curl -i "http://127.0.0.1:9080/aws-lambda/api7-docs" APISIX will forward the request to `https://xwbs1bjiy3.execute-api.us-west-2.amazonaws.com/default/api7-docs` and you should receive an `HTTP/1.1 200 OK` response with the following message: "Hello from Lambda!" If your API key is invalid or if the requested path is not associated with any method, you should receive an `HTTP/1.1 403 Forbidden` response. * [Examples](https://docs.api7.ai/hub/aws-lambda/#examples) * [Invoke Lambda Function Securely using IAM Access Keys](https://docs.api7.ai/hub/aws-lambda/#invoke-lambda-function-securely-using-iam-access-keys) * [Integrate with Amazon API Gateway Securely with API Key](https://docs.api7.ai/hub/aws-lambda/#integrate-with-amazon-api-gateway-securely-with-api-key) * [Forward Requests to Amazon API Gateway Sub-Paths](https://docs.api7.ai/hub/aws-lambda/#forward-requests-to-amazon-api-gateway-sub-paths) --- # Public API | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/public-api/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `public-api` plugin exposes an internal API endpoint, making it publicly accessible. One of the primary use cases of this plugin is to expose internal endpoints created by other plugins. Examples[​](https://docs.api7.ai/hub/public-api/#examples "Direct link to Examples") ------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `public-api` in different scenarios. ### Expose Prometheus Metrics at Custom Endpoint[​](https://docs.api7.ai/hub/public-api/#expose-prometheus-metrics-at-custom-endpoint "Direct link to Expose Prometheus Metrics at Custom Endpoint") The following example demonstrates how you can disable the Prometheus export server that, by default, exposes an endpoint on port `9091`, and expose APISIX Prometheus metrics on a new public API endpoint on port `9080`, which APISIX uses to listen to other client requests. You will also configure the route such that the internal endpoint `/apisix/prometheus/metrics` is exposed at a custom endpoint. caution If a large quantity of metrics is being collected, the plugin could take up a significant amount of CPU resources for metric computations and negatively impact the processing of regular requests. To address this issue, APISIX uses [privileged agent](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/process.md#enable_privileged_agent) and offloads the metric computations to a separate process. This optimization applies automatically if you use the metric endpoint configured under `plugin_attr.prometheus.export_addr` in the configuration file. If you expose the metric endpoint with the `public-api` plugin, you will not benefit from this optimization. Disable the Prometheus export server in the configuration file and [reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect: conf/config.yaml plugin_attr: prometheus: enable_export_server: false Next, create a route with `public-api` plugin and expose a public API endpoint for APISIX metrics: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "prometheus-metrics", "uri": "/prometheus_metrics", "plugins": { "public-api": { "uri": "/apisix/prometheus/metrics" } } }' ❶ Set the route `uri` to the custom endpoint path. ❷ Set the plugin `uri` to the internal endpoint to be exposed. Send a request to the custom metrics endpoint: curl "http://127.0.0.1:9080/prometheus_metrics" You should see an output similar to the following: # HELP apisix_http_requests_total The total number of client requests since APISIX started# TYPE apisix_http_requests_total gaugeapisix_http_requests_total 1# HELP apisix_nginx_http_current_connections Number of HTTP connections# TYPE apisix_nginx_http_current_connections gaugeapisix_nginx_http_current_connections{state="accepted"} 1apisix_nginx_http_current_connections{state="active"} 1apisix_nginx_http_current_connections{state="handled"} 1apisix_nginx_http_current_connections{state="reading"} 0apisix_nginx_http_current_connections{state="waiting"} 0apisix_nginx_http_current_connections{state="writing"} 1... ### Expose Batch Requests Endpoint[​](https://docs.api7.ai/hub/public-api/#expose-batch-requests-endpoint "Direct link to Expose Batch Requests Endpoint") The following example demonstrates how you can use the `public-api` plugin to expose an endpoint for the `batch-requests` plugin, which is used for assembling multiple requests into one single request before sending them to the gateway. Create a sample route to httpbin's `/anything` endpoint for verification purpose: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "httpbin-anything", "uri": "/anything", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create a route with `public-api` plugin and set the route `uri` to the internal endpoint to be exposed: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "batch-requests", "uri": "/apisix/batch-requests", "plugins": { "public-api": {} } }' Send a pipelined request consisting of a GET and a POST request to the exposed batch requests endpoint: curl "http://127.0.0.1:9080/apisix/batch-requests" -X POST -d '{ "pipeline": [ { "method": "GET", "path": "/anything" }, { "method": "POST", "path": "/anything", "body": "a post request" } ]}' You should receive responses from both requests, similar to the following: [ { "reason": "OK", "body": "{\n \"args\": {}, \n \"data\": \"\", \n \"files\": {}, \n \"form\": {}, \n \"headers\": {\n \"Accept\": \"*/*\", \n \"Host\": \"127.0.0.1\", \n \"User-Agent\": \"curl/8.6.0\", \n \"X-Amzn-Trace-Id\": \"Root=1-67b6e33b-5a30174f5534287928c54ca9\", \n \"X-Forwarded-Host\": \"127.0.0.1\"\n }, \n \"json\": null, \n \"method\": \"GET\", \n \"origin\": \"192.168.107.1, 43.252.208.84\", \n \"url\": \"http://127.0.0.1/anything\"\n}\n", "headers": { ... }, "status": 200 }, { "reason": "OK", "body": "{\n \"args\": {}, \n \"data\": \"a post request\", \n \"files\": {}, \n \"form\": {}, \n \"headers\": {\n \"Accept\": \"*/*\", \n \"Content-Length\": \"14\", \n \"Host\": \"127.0.0.1\", \n \"User-Agent\": \"curl/8.6.0\", \n \"X-Amzn-Trace-Id\": \"Root=1-67b6e33b-0eddcec07f154dac0d77876f\", \n \"X-Forwarded-Host\": \"127.0.0.1\"\n }, \n \"json\": null, \n \"method\": \"POST\", \n \"origin\": \"192.168.107.1, 43.252.208.84\", \n \"url\": \"http://127.0.0.1/anything\"\n}\n", "headers": { ... }, "status": 200 }] If you would like to expose the batch requests endpoint at a custom endpoint, create a route with `public-api` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "batch-requests", "uri": "/batch-requests", "plugins": { "public-api": { "uri": "/apisix/batch-requests" } } }' ❶ Set the route `uri` to the custom endpoint path. ❷ Set the plugin `uri` to the internal endpoint to be exposed. The batch requests endpoint should now be exposed as `/batch-requests`, instead of `/apisix/batch-requests`. Send a pipelined request consisting of a GET and a POST request to the exposed batch requests endpoint: curl "http://127.0.0.1:9080/batch-requests" -X POST -d '{ "pipeline": [ { "method": "GET", "path": "/anything" }, { "method": "POST", "path": "/anything", "body": "a post request" } ]}' You should receive responses from both requests, similar to the following: [ { "reason": "OK", "body": "{\n \"args\": {}, \n \"data\": \"\", \n \"files\": {}, \n \"form\": {}, \n \"headers\": {\n \"Accept\": \"*/*\", \n \"Host\": \"127.0.0.1\", \n \"User-Agent\": \"curl/8.6.0\", \n \"X-Amzn-Trace-Id\": \"Root=1-67b6e33b-5a30174f5534287928c54ca9\", \n \"X-Forwarded-Host\": \"127.0.0.1\"\n }, \n \"json\": null, \n \"method\": \"GET\", \n \"origin\": \"192.168.107.1, 43.252.208.84\", \n \"url\": \"http://127.0.0.1/anything\"\n}\n", "headers": { ... }, "status": 200 }, { "reason": "OK", "body": "{\n \"args\": {}, \n \"data\": \"a post request\", \n \"files\": {}, \n \"form\": {}, \n \"headers\": {\n \"Accept\": \"*/*\", \n \"Content-Length\": \"14\", \n \"Host\": \"127.0.0.1\", \n \"User-Agent\": \"curl/8.6.0\", \n \"X-Amzn-Trace-Id\": \"Root=1-67b6e33b-0eddcec07f154dac0d77876f\", \n \"X-Forwarded-Host\": \"127.0.0.1\"\n }, \n \"json\": null, \n \"method\": \"POST\", \n \"origin\": \"192.168.107.1, 43.252.208.84\", \n \"url\": \"http://127.0.0.1/anything\"\n}\n", "headers": { ... }, "status": 200 }] * [Examples](https://docs.api7.ai/hub/public-api/#examples) * [Expose Prometheus Metrics at Custom Endpoint](https://docs.api7.ai/hub/public-api/#expose-prometheus-metrics-at-custom-endpoint) * [Expose Batch Requests Endpoint](https://docs.api7.ai/hub/public-api/#expose-batch-requests-endpoint) --- # Error Log Logger | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/error-log-logger/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `error-log-logger` plugin pushes APISIX's error logs (`error.log`) to TCP, Apache SkyWalking, Apache Kafka, or ClickHouse servers, in batches. You can specify the severity level of which the plugin should send the corresponding logs. The plugin is disabled by default. Once enabled, it will automatically start pushing error logs to remote servers. You should configure remote server details in [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) only, instead of on other resources, such as routes. Examples[​](https://docs.api7.ai/hub/error-log-logger/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `error-log-logger` plugin for different scenarios. If you are using API7 Enterprise, the plugin is enabled by default. If you are using APISIX, the `error-log-logger` plugin is disabled by default. To enable the plugin, add the plugin to your [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) as such: config.yaml plugins: - ... - error-log-logger [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. ### Send Logs to TCP Server[​](https://docs.api7.ai/hub/error-log-logger/#send-logs-to-tcp-server "Direct link to Send Logs to TCP Server") The following example demonstrates how you can configure the `error-log-logger` plugin to send error logs to a TCP server. Start a netcat listener on port `19000` as the example TCP server: nc -l 19000 Configure the plugin metadata for `error-log-logger`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/error-log-logger" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "tcp": { "host": "192.168.2.103", "port": 19000 }, "level": "INFO" }' ❶ Replace with your internal IP address. ❷ Configure the port to your TCP server listening port. ❸ Configure the severity level to `INFO` so most logs would be sent, for easier verification. To verify, you can manually generate a log at `warn` level by [reloading APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) . In terminal session where netcat is listening, you should see a log entry similar to the following: 2025/01/26 20:15:29 [warn] 211#211: *35552 [lua] plugin.lua:205: load(): new plugins: {"cas-auth":true,"real-ip":true,"ai":true,"client-control":true,"proxy-control":true,"request-id":true,"zipkin":true,"ext-plugin-pre-req":true,"fault-injection":true,"mocking":true,"serverless-pre-function":true,"cors":true,"ip-restriction":true,"ua-restriction":true,"referer-restriction":true,"csrf":true,"uri-blocker":true,"request-validation":true,"chaitin-waf":true,"multi-auth":true,"openid-connect":true,"authz-casbin":true,"authz-casdoor":true,"wolf-rbac":true,"ldap-auth":true,"hmac-auth":true,"basic-auth":true,"jwt-auth":true,"redirect":true,"key-auth":true,"consumer-restriction":true,"attach-consumer-label":true,"authz-keycloak":true,"proxy-cache":true,"body-transformer":true,"ai-prompt-template":true,"ai-prompt-decorator":true,"proxy-mirror":true,"proxy-rewrite":true,"workflow":true,"api-breaker":true,"ai-proxy":true,"limit-conn":true,"limit-count":true,"limit-req":true,"gzip":true,"server-info":true,"traffic-split":true,"response-rewrite":true,"degraphql":true,"kafka-proxy":true,"grpc-transcode":true,"grpc-web":true,"http-dubbo":true,"public-api":true,"prometheus":true,"datadog":true,"loki-logger":true,"elasticsearch-logger":true,"echo":true,"loggly":true,"http-logger":true,"splunk-hec-logging":true,"skywalking-logger":true,"google-cloud-logging":true,"sls-logger":true,"tcp-logger":true,"kafka-logger":true,"rocketmq-logger":true,"syslog":true,"udp-logger":true,"file-logger":true,"clickhouse-logger":true,"tencent-cloud-cls":true,"inspect":true,"example-plugin":true,"aws-lambda":true,"azure-functions":true,"openwhisk":true,"openfunction":true,"error-log-logger":true,"ext-plugin-post-req":true,"ext-plugin-post-resp":true,"serverless-post-function":true,"opa":true,"forward-auth":true,"jwe-decrypt":true}, context: init_worker_by_lua* ### Send Logs to SkyWalking[​](https://docs.api7.ai/hub/error-log-logger/#send-logs-to-skywalking "Direct link to Send Logs to SkyWalking") The following example demonstrates how you can configure the `error-log-logger` plugin to send error logs to SkyWalking. Start a SkyWalking storage, OAP and Booster UI with Docker Compose, following [Skywalking's documentation](https://skywalking.apache.org/docs/main/next/en/setup/backend/backend-docker/) . Once set up, the OAP server should be listening on `12800` and you should be able to access the UI at [http://localhost:8080](http://localhost:8080/) . Configure the plugin metadata for `error-log-logger`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/error-log-logger" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "skywalking": { "endpoint_addr": "http://192.168.2.103:12800/v3/logs" }, "level": "INFO" }' ❶ Replace with your SkyWalking server address. ❷ Configure the severity level to `INFO` so most logs would be sent, for easier verification. To verify, you can manually generate a log at `warn` level by [reloading APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) . In [Skywalking UI](http://localhost:8080/) , navigate to **General Service** > **Services**. You should see a service called `APISIX` with the following log entry: 2025/01/27 07:40:06 [warn] 211#211: *35552 [lua] plugin.lua:205: load(): new plugins: {"cas-auth":true,"real-ip":true,"ai":true,"client-control":true,"proxy-control":true,"request-id":true,"zipkin":true,"ext-plugin-pre-req":true,"fault-injection":true,"mocking":true,"serverless-pre-function":true,"cors":true,"ip-restriction":true,"ua-restriction":true,"referer-restriction":true,"csrf":true,"uri-blocker":true,"request-validation":true,"chaitin-waf":true,"multi-auth":true,"openid-connect":true,"authz-casbin":true,"authz-casdoor":true,"wolf-rbac":true,"ldap-auth":true,"hmac-auth":true,"basic-auth":true,"jwt-auth":true,"redirect":true,"key-auth":true,"consumer-restriction":true,"attach-consumer-label":true,"authz-keycloak":true,"proxy-cache":true,"body-transformer":true,"ai-prompt-template":true,"ai-prompt-decorator":true,"proxy-mirror":true,"proxy-rewrite":true,"workflow":true,"api-breaker":true,"ai-proxy":true,"limit-conn":true,"limit-count":true,"limit-req":true,"gzip":true,"server-info":true,"traffic-split":true,"response-rewrite":true,"degraphql":true,"kafka-proxy":true,"grpc-transcode":true,"grpc-web":true,"http-dubbo":true,"public-api":true,"prometheus":true,"datadog":true,"loki-logger":true,"elasticsearch-logger":true,"echo":true,"loggly":true,"http-logger":true,"splunk-hec-logging":true,"skywalking-logger":true,"google-cloud-logging":true,"sls-logger":true,"tcp-logger":true,"kafka-logger":true,"rocketmq-logger":true,"syslog":true,"udp-logger":true,"file-logger":true,"clickhouse-logger":true,"tencent-cloud-cls":true,"inspect":true,"example-plugin":true,"aws-lambda":true,"azure-functions":true,"openwhisk":true,"openfunction":true,"error-log-logger":true,"ext-plugin-post-req":true,"ext-plugin-post-resp":true,"serverless-post-function":true,"opa":true,"forward-auth":true,"jwe-decrypt":true}, context: init_worker_by_lua* You should also observe logs at other severity levels, such as `error`, `emerg`, and `info`, when they are generated. ### Send Logs to ClickHouse[​](https://docs.api7.ai/hub/error-log-logger/#send-logs-to-clickhouse "Direct link to Send Logs to ClickHouse") The following example demonstrates how you can configure the `error-log-logger` plugin to send error logs to ClickHouse. Start a sample ClickHouse server with user `default` and empty password: docker run -d -p 8123:8123 -p 9000:9000 -p 9009:9009 --name clickhouse-server clickhouse/clickhouse-server In ClickHouse database `default`, create a table named `default_logs` with a `data` column. Note that the `data` column is expected by the plugin to push logs to. curl "http://127.0.0.1:8123" -X POST -d ' CREATE TABLE default.default_logs ( data String, PRIMARY KEY(`data`) ) ENGINE = MergeTree()' --user default: Configure the plugin metadata for `error-log-logger`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/error-log-logger" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "clickhouse": { "endpoint_addr": "http://192.168.2.103:8123", "user": "default", "password": "", "database": "default", "logtable": "default_logs" }, "level": "INFO" }' ❶ Replace with your ClickHouse server address. ❷ Set the username to `default`. ❸ Set the password to empty. ❹ Set the database to `default`. ❺ Set the database table to `default_logs`. ❻ Configure the severity level to `INFO` so most logs would be sent, for easier verification. To verify, you can manually generate a log at `warn` level by [reloading APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) . Send a request to ClickHouse to see the log entries: echo 'SELECT * FROM default.default_logs FORMAT Pretty' | curl "http://127.0.0.1:8123/?" -d @- You should see a log entry similar to the following: 2025/01/27 08:21:13 [warn] 353#353: *106572 [lua] plugin.lua:205: load(): new plugins: {"client-control":true,"proxy-control":true,"request-id":true,"zipkin":true,"ext-plugin-pre-req":true,"fault-injection":true,"mocking":true,"serverless-pre-function":true,"cors":true,"ip-restriction":true,"ua-restriction":true,"referer-restriction":true,"csrf":true,"uri-blocker":true,"request-validation":true,"chaitin-waf":true,"multi-auth":true,"openid-connect":true,"authz-casbin":true,"authz-casdoor":true,"wolf-rbac":true,"ldap-auth":true,"hmac-auth":true,"basic-auth":true,"jwt-auth":true,"jwe-decrypt":true,"key-auth":true,"consumer-restriction":true,"attach-consumer-label":true,"forward-auth":true,"opa":true,"authz-keycloak":true,"proxy-cache":true,"body-transformer":true,"ai-prompt-template":true,"ai-prompt-decorator":true,"proxy-mirror":true,"proxy-rewrite":true,"workflow":true,"api-breaker":true,"ai-proxy":true,"limit-conn":true,"limit-count":true,"limit-req":true,"gzip":true,"server-info":true,"traffic-split":true,"response-rewrite":true,"degraphql":true,"kafka-proxy":true,"grpc-transcode":true,"grpc-web":true,"http-dubbo":true,"public-api":true,"error-log-logger":true,"google-cloud-logging":true,"sls-logger":true,"tcp-logger":true,"kafka-logger":true,"rocketmq-logger":true,"syslog":true,"udp-logger":true,"file-logger":true,"clickhouse-logger":true,"tencent-cloud-cls":true,"inspect":true,"example-plugin":true,"aws-lambda":true,"azure-functions":true,"openwhisk":true,"openfunction":true,"serverless-post-function":true,"ext-plugin-post-req":true,"ext-plugin-post-resp":true,"redirect":true,"skywalking-logger":true,"splunk-hec-logging":true,"http-logger":true,"loggly":true,"echo":true,"elasticsearch-logger":true,"cas-auth":true,"prometheus":true,"datadog":true,"loki-logger":true,"real-ip":true,"ai":true}, context: init_worker_by_lua* │ * [Examples](https://docs.api7.ai/hub/error-log-logger/#examples) * [Send Logs to TCP Server](https://docs.api7.ai/hub/error-log-logger/#send-logs-to-tcp-server) * [Send Logs to SkyWalking](https://docs.api7.ai/hub/error-log-logger/#send-logs-to-skywalking) * [Send Logs to ClickHouse](https://docs.api7.ai/hub/error-log-logger/#send-logs-to-clickhouse) --- # Traffic Split | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/traffic-split/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `traffic-split` plugin directs traffic to various upstream services based on conditions and/or weights. It provides a dynamic and flexible approach to implement release strategies and manage traffic. Examples[​](https://docs.api7.ai/hub/traffic-split/#examples "Direct link to Examples") ---------------------------------------------------------------------------------------- The examples below shows different use cases for using the `traffic-split` plugin. ### Implement Canary Release[​](https://docs.api7.ai/hub/traffic-split/#implement-canary-release "Direct link to Implement Canary Release") The following example demonstrates how to implement canary release with this plugin. Canary release is a gradual deployment in which an increasing percentage of traffic is directed to a new release, allowing for a controlled and monitored rollout. This method ensures that any potential issues or bugs in the new release can be identified and addressed early on, before fully redirecting all traffic. Create a route and configure `traffic-split` plugin with the following rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/headers", "id": "traffic-split-route", "plugins": { "traffic-split": { "rules": [ { "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } }, "weight": 3 }, { "weight": 2 } ] } ] } }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } } }' The proportion of traffic to each upstream is determined by the weight of the upstream relative to the total weight of all upstreams. Here, the total weight is calculated as: 3 + 2 = 5. Therefore: ❶ 60% of the traffic are expected to be forwarded to `httpbin.org`. ❷ 40% of the traffic are expected to be forwarded to `mock.api7.ai`. Send 10 consecutive requests to the route to verify: resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers" -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 You should see a response similar to the following: httpbin.org: 6, mock.api7.ai: 4 Adjust the upstream weights accordingly to complete the canary release. ### Implement Blue-Green Deployment[​](https://docs.api7.ai/hub/traffic-split/#implement-blue-green-deployment "Direct link to Implement Blue-Green Deployment") The following example demonstrates how to implement blue-green deployment with this plugin. Blue-green deployment is a deployment strategy that involves maintaining two identical environments: the _blue_ and the _green_. The blue environment refers to the current production deployment and the green environment refers to the new deployment. Once the green environment is tested to be ready for production, traffic will be routed to the green environment, making it the new production deployment. Create a route and configure `traffic-split` plugin with the following rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/headers", "id": "traffic-split-route", "plugins": { "traffic-split": { "rules": [ { "match": [ { "vars": [ ["http_release","==","new_release"] ] } ], "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } } } ] } ] } }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } } }' ❶ Execute the plugin to redirect traffic only when the request contains a header `release: new_release`. Send a request to the route with the `release` header: curl "http://127.0.0.1:9080/headers" -H 'release: new_release' You should see a response similar to the following: { "headers": { "Accept": "*/*", "Host": "httpbin.org", ... }} Send a request to the route without any additional header: curl "http://127.0.0.1:9080/headers" You should see a response similar to the following: { "headers": { "accept": "*/*", "host": "mock.api7.ai", ... }} ### Define Matching Condition for POST Request With APISIX Expressions[​](https://docs.api7.ai/hub/traffic-split/#define-matching-condition-for-post-request-with-apisix-expressions "Direct link to Define Matching Condition for POST Request With APISIX Expressions") The following example demonstrates how to use [APISIX expressions](https://docs.api7.ai/apisix/reference/apisix-expressions) in rules to conditionally execute the plugin when certain condition of a POST request is satisfied. Create a route and configure `traffic-split` plugin with the following rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/post", "methods": ["POST"], "id": "traffic-split-route", "plugins": { "traffic-split": { "rules": [ { "match": [ { "vars": [ ["post_arg_id", "==", "1"] ] } ], "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } } } ] } ] } }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } } }' Send a POST request with body `id=1`: curl "http://127.0.0.1:9080/post" -X POST \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'id=1' ❶ You can specify charset in the `Content-Type` as well, such as `Content-Type: application/x-www-form-urlencoded;charset=UTF-8`. You should see a response similar to the following: { "args": {}, "data": "", "files": {}, "form": { "id": "1" }, "headers": { "Accept": "*/*", "Content-Length": "4", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", ... }, ...} Send a POST request without `id=1` in the body: curl "http://127.0.0.1:9080/post" -X POST \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'random=string' You should see that the request was forwarded to `mock.api7.ai`. ### Define AND Matching Conditions With APISIX Expressions[​](https://docs.api7.ai/hub/traffic-split/#define-and-matching-conditions-with-apisix-expressions "Direct link to Define AND Matching Conditions With APISIX Expressions") The following example demonstrates how to use [APISIX expressions](https://docs.api7.ai/apisix/reference/apisix-expressions) in rules to conditionally execute the plugin when multiple conditions are satisfied. Create a route and configure `traffic-split` plugin with the following matching rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/headers", "id": "traffic-split-route", "plugins": { "traffic-split": { "rules": [ { "match": [ { "vars": [ ["arg_name","==","jack"], ["http_user-id",">","23"], ["http_apisix-key","~~","[a-z]+"] ] } ], "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } }, "weight": 3 }, { "weight": 2 } ] } ] } }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } } }' ❶ Execute the plugin to redirect traffic only when all three conditions are satisfied. If conditions are satisfied, 60% of the traffic should be directed to `httpbin.org` and the other 40% should be directed to `mock.api7.ai`. If conditions are not satisfied, all traffic should be directed to `mock.api7.ai`. Send 10 consecutive requests that satisfy all conditions to verify: resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers?name=jack" -H 'user-id: 30' -H 'apisix-key: helloapisix' -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 You should see a response similar to the following: httpbin.org: 6, mock.api7.ai: 4 Send 10 consecutive requests that do not satisfy the conditions to verify: resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers?name=random" -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 You should see a response similar to the following: httpbin.org: 0, mock.api7.ai: 10 ### Define OR Matching Conditions With APISIX Expressions[​](https://docs.api7.ai/hub/traffic-split/#define-or-matching-conditions-with-apisix-expressions "Direct link to Define OR Matching Conditions With APISIX Expressions") The following example demonstrates how to use [APISIX expressions](https://docs.api7.ai/apisix/reference/apisix-expressions) in rules to conditionally execute the plugin when either set of the condition is satisfied. Create a route and configure `traffic-split` plugin with the following matching rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/headers", "id": "traffic-split-route", "plugins": { "traffic-split": { "rules": [ { "match": [ { "vars": [ ["arg_name","==","jack"], ["http_user-id",">","23"], ["http_apisix-key","~~","[a-z]+"] ] }, { "vars": [ ["arg_name2","==","rose"], ["http_user-id2","!",">","33"], ["http_apisix-key2","~~","[a-z]+"] ] } ], "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } }, "weight": 3 }, { "weight": 2 } ] } ] } }, "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } } }' ❶ and ❷: Execute the plugin to redirect traffic when either set of the conditions are satisfied. Alternatively, you can also use the OR operator in the [APISIX expressions](https://docs.api7.ai/apisix/reference/apisix-expressions#logical-operators) for these conditions. If conditions are satisfied, 60% of the traffic should be directed to `httpbin.org` and the other 40% should be directed to `mock.api7.ai`. If conditions are not satisfied, all traffic should be directed to `mock.api7.ai`. Send 10 consecutive requests that satisfy the second set of conditions to verify: resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers?name2=rose" -H 'user-id:30' -H 'apisix-key2: helloapisix' -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 You should see a response similar to the following: httpbin.org: 6, mock.api7.ai: 4 Send 10 consecutive requests that do not satisfy any set of conditions to verify: resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers?name=random" -sL) && \ count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \ count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \ echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7 You should see a response similar to the following: httpbin.org: 0, mock.api7.ai: 10 ### Configure Different Rules for Different Upstreams[​](https://docs.api7.ai/hub/traffic-split/#configure-different-rules-for-different-upstreams "Direct link to Configure Different Rules for Different Upstreams") The following example demonstrates how to set one-to-one mapping between rule sets and upstreams. Create a route and configure `traffic-split` plugin with the following matching rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/headers", "id": "traffic-split-route", "plugins": { "traffic-split": { "rules": [ { "match": [ { "vars": [ ["http_x-api-id","==","1"] ] } ], "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "httpbin.org:443":1 } }, "weight": 1 } ] }, { "match": [ { "vars": [ ["http_x-api-id","==","2"] ] } ], "weighted_upstreams": [ { "upstream": { "type": "roundrobin", "scheme": "https", "pass_host": "node", "nodes": { "mock.api7.ai:443":1 } }, "weight": 1 } ] } ] } }, "upstream": { "type": "roundrobin", "nodes": { "postman-echo.com:443": 1 }, "scheme": "https", "pass_host": "node" } }' ❶ Execute the plugin to redirect traffic only when the request contains a header `x-api-id: 1`. ❷ Execute the plugin to redirect traffic only when the request contains a header `x-api-id: 2`. Send a request with header `x-api-id: 1`: curl "http://127.0.0.1:9080/headers" -H 'x-api-id: 1' You should see an `HTTP/1.1 200 OK` response similar to the following: { "headers": { "Accept": "*/*", "Host": "httpbin.org", ... }} Send a request with header `x-api-id: 2`: curl "http://127.0.0.1:9080/headers" -H 'x-api-id: 2' You should see an `HTTP/1.1 200 OK` response similar to the following: { "headers": { "accept": "*/*", "host": "mock.api7.ai", ... }} Send a request without any additional header: curl "http://127.0.0.1:9080/headers" You should see a response similar to the following: { "headers": { "accept": "*/*", "host": "postman-echo.com", ... }} * [Examples](https://docs.api7.ai/hub/traffic-split/#examples) * [Implement Canary Release](https://docs.api7.ai/hub/traffic-split/#implement-canary-release) * [Implement Blue-Green Deployment](https://docs.api7.ai/hub/traffic-split/#implement-blue-green-deployment) * [Define Matching Condition for POST Request With APISIX Expressions](https://docs.api7.ai/hub/traffic-split/#define-matching-condition-for-post-request-with-apisix-expressions) * [Define AND Matching Conditions With APISIX Expressions](https://docs.api7.ai/hub/traffic-split/#define-and-matching-conditions-with-apisix-expressions) * [Define OR Matching Conditions With APISIX Expressions](https://docs.api7.ai/hub/traffic-split/#define-or-matching-conditions-with-apisix-expressions) * [Configure Different Rules for Different Upstreams](https://docs.api7.ai/hub/traffic-split/#configure-different-rules-for-different-upstreams) --- # Error Page | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/error-page/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `error-page` plugin allows customizing the error page served when gateways throw 404, 500, 502, and 503 exceptions. Note that it does not allow customizing the error page if the exceptions are thrown by upstream services. Example[​](https://docs.api7.ai/hub/error-page/#example "Direct link to Example") ---------------------------------------------------------------------------------- ### Customize Error Page[​](https://docs.api7.ai/hub/error-page/#customize-error-page "Direct link to Customize Error Page") The example demonstrates how you can customize the error page by configuring the customized content on [plugin metadata](https://docs.api7.ai/apisix/reference/control-api#tag/Plugin-Metadata) and serve the error page when a 404 error is encountered. Configure the [plugin metadata](https://docs.api7.ai/apisix/reference/control-api#tag/Plugin-Metadata) for customized error page: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/error_page" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "enable": true, "error_404": { "body": "\n \n 404\n \n \n
\n

404 not found

\n
\n
\n
API7 Entreprise Edition
\n \n", "content-type": "text/html" } }' To demonstrate the function of the plugin, create a route with the [`serverless-post-function`](https://docs.api7.ai/hub/serverless-functions) plugin, which returns a 404 error code from gateways for all requests to the route: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/*", "id": "error-page-route", "plugins": { "serverless-post-function": { "functions": [ "return function (conf, ctx) local core = require(\"apisix.core\") core.response.exit(404) end" ] }, "error_page": {} }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ return a 404 status code for all requests to the route. ❷ enable `error_page` to return the customized error page. Send a request to the route: curl -i "http://127.0.0.1:9080/get" You should see an `HTTP/1.1 404 Not Found` response with the following response body: 404

404 not found


API7 Entreprise Edition
* [Example](https://docs.api7.ai/hub/error-page/#example) * [Customize Error Page](https://docs.api7.ai/hub/error-page/#customize-error-page) --- # MQTT Proxy | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/mqtt-proxy/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `mqtt-proxy` plugin is an L4 plugin that supports proxying and load balancing MQTT requests to MQTT servers. It supports MQTT versions 3.1.x and 5.0. The plugin must be configured on a [stream route](https://docs.api7.ai/apisix/key-concepts/stream-routes) , and APISIX should enable L4 traffic proxying. Examples[​](https://docs.api7.ai/hub/mqtt-proxy/#examples "Direct link to Examples") ------------------------------------------------------------------------------------- By default, APISIX only proxies L7 traffic. Before proceeding to examples, first ensure that you enable L4 traffic proxying in APISIX. Update the [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) as follows to enable L4 traffic proxying: conf/config.yaml apisix: proxy_mode: http&stream # Enable both L4 & L7 proxies stream_proxy: # Configure L4 proxy tcp: - 9100 # Set TCP proxy listening port [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. APISIX should now start listening L4 traffic on port `9100`. The examples below uses a MQTT client from the Mosquitto project to publish and subscribe messages. You can download it [here](https://mosquitto.org/download/) or use any other MQTT client of your choice. ### Proxy to a MQTT Broker[​](https://docs.api7.ai/hub/mqtt-proxy/#proxy-to-a-mqtt-broker "Direct link to Proxy to a MQTT Broker") The following example demonstrates how you can configure a stream route to proxy traffic to a hosted MQTT server and verify the APISIX can proxy MQTT messages successfully. Create a stream route to the MQTT server and configure the `mqtt-proxy` plugin: curl "http://127.0.0.1:9180/apisix/admin/stream_routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "mqtt-route", "plugins": { "mqtt-proxy": { "protocol_name": "MQTT", "protocol_level": 4 } }, "upstream": { "type": "roundrobin", "nodes": { "test.mosquitto.org:1883": 1 } } }' Open two terminal sessions. In the first one, subscribe to the test topic: mosquitto_sub -h test.mosquitto.org -p 1883 -t "test/apisix" In the other one, publish a sample message to the created route: mosquitto_pub -h 127.0.0.1 -p 9100 -t "test/apisix" -m "Hello APISIX" You should see the message `Hello APISIX` in the first terminal. ### Load Balance MQTT Traffic[​](https://docs.api7.ai/hub/mqtt-proxy/#load-balance-mqtt-traffic "Direct link to Load Balance MQTT Traffic") The following example demonstrates how you can configure a stream route to load balance MQTT traffic to different MQTT servers. When the plugin is enabled, it registers a variable `mqtt_client_id` which can be used for load balancing. MQTT connections with different client ID will be forwarded to different upstream nodes based on the consistent hash algorithm. If the client ID is missing, client IP will be used instead. Create a stream route to two MQTT servers and configure the `mqtt-proxy` plugin: curl "http://127.0.0.1:9180/apisix/admin/stream_routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "mqtt-route", "plugins": { "mqtt-proxy": { "protocol_name": "MQTT", "protocol_level": 4 } }, "upstream": { "type": "roundrobin", "key": "mqtt_client_id", "nodes": [ { "host": "test.mosquitto.org", "port": 1883, "weight": 1 }, { "host": "broker.mqtt.cool", "port": 1883, "weight": 1 } ] } }' Open three terminal sessions. In the first one, subscribe to the test topic in the first MQTT broker: mosquitto_sub -h test.mosquitto.org -p 1883 -t "test/apisix" In the second terminal, subscribe to the same topic in the second MQTT broker: mosquitto_sub -h broker.mqtt.cool -p 1883 -t "test/apisix" In the third terminal, run the following commands a few times to send sample messages to the route: mosquitto_pub -h 127.0.0.1 -p 9100 -t "test/apisix" -m "Hello APISIX" You should see the message `Hello APISIX` in both terminals, verifying the traffic was load balanced. * [Examples](https://docs.api7.ai/hub/mqtt-proxy/#examples) * [Proxy to a MQTT Broker](https://docs.api7.ai/hub/mqtt-proxy/#proxy-to-a-mqtt-broker) * [Load Balance MQTT Traffic](https://docs.api7.ai/hub/mqtt-proxy/#load-balance-mqtt-traffic) --- # JWE Decrypt | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/jwe-decrypt/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `jwe-decrypt` plugin decrypts [JWE](https://datatracker.ietf.org/doc/html/rfc7516) authorization headers in requests sent to APISIX [routes](https://docs.api7.ai/apisix/key-concepts/routes) or [services](https://docs.api7.ai/apisix/key-concepts/services) . Examples[​](https://docs.api7.ai/hub/jwe-decrypt/#examples "Direct link to Examples") -------------------------------------------------------------------------------------- The examples below demonstrate how you can work with the `jwe-decrypt` plugin for different scenarios. ### Expose JWE Encryption Endpoint and Generate JWE Token[​](https://docs.api7.ai/hub/jwe-decrypt/#expose-jwe-encryption-endpoint-and-generate-jwe-token "Direct link to Expose JWE Encryption Endpoint and Generate JWE Token") The following example demonstrates how to expose the JWE encryption endpoint and generate a JWE token. The plugin `jwe-decrypt` plugin creates an internal endpoint at `/apisix/plugin/jwe/encrypt` to encrypt JWE. Expose the endpoint with the `public-api` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes/jwe-encrypt-api" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "/apisix/plugin/jwe/encrypt", "plugins": { "public-api": {} } }' Create a consumer with `jwe-decrypt` and configure the decryption key: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack", "plugins": { "jwe-decrypt": { "key": "jack-key", "secret": "key-length-should-be-32-chars123" } } }' Send a request to the encryption endpoint with consumer key to encrypt some sample data in the payload: curl "http://127.0.0.1:9080/apisix/plugin/jwe/encrypt?key=jack-key" \ -d 'payload={"uid":10000,"uname":"test"}' -G You should see a response similar to the following, with the JWE encrypted data in the response body: eyJraWQiOiJqYWNrLWtleSIsImFsZyI6ImRpciIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.IUFW_q4igO_wvf63i-3VwV0MEetPL9C20tlgcQ.fveViMUi0ijJlQ19D7kDrg ### Decrypt Data with JWE[​](https://docs.api7.ai/hub/jwe-decrypt/#decrypt-data-with-jwe "Direct link to Decrypt Data with JWE") The following example demonstrates how to decrypt the previously generated JWE token. Create a route with `jwe-decrypt` to decrypt the authorization header: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "jwe-decrypt-route", "uri": "/anything/jwe", "plugins": { "jwe-decrypt": { "header": "Authorization", "forward_header": "Authorization" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to the route with the JWE encrypted data in the `Authorization` header: curl "http://127.0.0.1:9080/anything/jwe" -H 'Authorization: eyJraWQiOiJqYWNrLWtleSIsImFsZyI6ImRpciIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.IUFW_q4igO_wvf63i-3VwV0MEetPL9C20tlgcQ.fveViMUi0ijJlQ19D7kDrg' You should see a response similar to the following, where the `Authorization` header shows the plaintext of the payload: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Authorization": "{\"uid\":10000,\"uname\":\"test\"}", "Host": "127.0.0.1", "User-Agent": "curl/8.1.2", "X-Amzn-Trace-Id": "Root=1-6510f2c3-1586ec011a22b5094dbe1896", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "GET", "origin": "127.0.0.1, 119.143.79.94", "url": "http://127.0.0.1/anything/jwe"} * [Examples](https://docs.api7.ai/hub/jwe-decrypt/#examples) * [Expose JWE Encryption Endpoint and Generate JWE Token](https://docs.api7.ai/hub/jwe-decrypt/#expose-jwe-encryption-endpoint-and-generate-jwe-token) * [Decrypt Data with JWE](https://docs.api7.ai/hub/jwe-decrypt/#decrypt-data-with-jwe) --- # Prometheus | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/prometheus/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `prometheus` plugin provides the capability to integrate APISIX with [Prometheus](https://prometheus.io/) . After enabling the plugin, APISIX will start collecting relevant metrics, such as API requests and latencies, and exporting them in a [text-based exposition format](https://prometheus.io/docs/instrumenting/exposition_formats/#exposition-formats) to Prometheus. You can then create event monitoring and alerting in Prometheus to monitor the health of your API gateway and APIs. Metrics[​](https://docs.api7.ai/hub/prometheus/#metrics "Direct link to Metrics") ---------------------------------------------------------------------------------- There are different types of metrics in Prometheus. To understand their differences, see [metrics types](https://prometheus.io/docs/concepts/metric_types/) . The following metrics are exported by the `prometheus` plugin by default. See [get APISIX metrics](https://docs.api7.ai/hub/prometheus/#get-apisix-metrics) for an example. Note that some metrics, such as `apisix_batch_process_entries`, are not readily visible if there are no data. | Name | Type | Description | | --- | --- | --- | | apisix\_bandwidth | counter | Total amount of traffic flowing through APISIX in bytes. | | apisix\_etcd\_modify\_indexes | gauge | Number of changes to etcd by APISIX keys. | | apisix\_batch\_process\_entries | gauge | Number of remaining entries in a batch when sending data in batches, such as with `http logger`, and other logging plugins. | | apisix\_etcd\_reachable | gauge | Whether APISIX can reach etcd. A value of `1` represents reachable and `0` represents unreachable. | | apisix\_http\_status | counter | HTTP status codes returned from upstream services. | | apisix\_http\_requests\_total | gauge | Number of HTTP requests from clients. | | apisix\_nginx\_http\_current\_connections | gauge | Number of current connections with clients. | | apisix\_nginx\_metric\_errors\_total | counter | Total number of `nginx-lua-prometheus` errors. | | apisix\_http\_latency | histogram | HTTP request latency in milliseconds. | | apisix\_node\_info | gauge | Information about the APISIX node, such as the host name and APISIX version. | | apisix\_shared\_dict\_capacity\_bytes | gauge | The total capacity of an [NGINX shared dictionary](https://github.com/openresty/lua-nginx-module#ngxshareddict)
. | | apisix\_shared\_dict\_free\_space\_bytes | gauge | The remaining space in an [NGINX shared dictionary](https://github.com/openresty/lua-nginx-module#ngxshareddict)
. | | apisix\_upstream\_status | gauge | Health check status of upstream nodes, available if health checks are configured on the upstream. A value of `1` represents healthy and `0` represents unhealthy. | | apisix\_stream\_connection\_total | counter | Total number of connections handled per stream route. | | apisix\_llm\_prompt\_tokens | counter | Number of prompt tokens. | | apisix\_llm\_completion\_tokens | counter | Number of completion tokens. | | apisix\_llm\_latency | histogram | Duration from request sending to the first token received from the LLM service, in milliseconds. | | apisix\_llm\_active\_connections | gauge | Number of active connections with LLM services. | Labels[​](https://docs.api7.ai/hub/prometheus/#labels "Direct link to Labels") ------------------------------------------------------------------------------- [Labels](https://prometheus.io/docs/practices/naming/#labels) are attributes of metrics that are used to differentiate metrics. For example, the `apisix_http_status` metric can be labeled with `route` information to identify which route the HTTP status originates from. The following are labels for a non-exhaustive list of APISIX metrics and their descriptions. ### Labels for `apisix_http_status`[​](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_http_status "Direct link to labels-for-apisix_http_status") The following labels are used to differentiate `apisix_http_status` metrics. | Name | Description | | --- | --- | | code | HTTP response code returned by the upstream node. | | route | ID of the route that the HTTP status originates from when `prefer_name` is `false` (default), and name of the route when `prefer_name` to `true`. Default to an empty string if a request does not match any route. | | route\_id | Available only in Enterprise. ID of the route that the HTTP status originates from regardless of the `prefer_name` setting. | | matched\_uri | URI of the route that matches the request. Default to an empty string if a request does not match any route. | | matched\_host | Host of the route that matches the request. Default to an empty string if a request does not match any route, or host is not configured on the route. | | service | ID of the service that the HTTP status originates from when `prefer_name` is `false` (default), and name of the service when `prefer_name` to `true`. Default to the configured value of host on the route if the matched route does not belong to any service. | | service\_id | Available only in Enterprise. ID of the service that the HTTP status originates from regardless of the `prefer_name` setting. | | consumer | Name of the consumer associated with a request. Default to an empty string if no consumer is associated with the request. | | node | IP address of the upstream node. | | gateway\_group\_id | Available only in Enterprise. ID of the gateway group that the HTTP status originates from. | | instance\_id | Available only in Enterprise. ID of the gateway instance that the HTTP status originates from. | | api\_product\_id | Available only in Enterprise. Product ID that the HTTP status originates from. | | request\_type | Available only in Enterprise. Request type that the HTTP status originates from. | | llm\_model | Available only in Enterprise. LLM model that the HTTP status originates from. | ### Labels for `apisix_bandwidth`[​](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_bandwidth "Direct link to labels-for-apisix_bandwidth") The following labels are used to differentiate `apisix_bandwidth` metrics. | Name | Description | | --- | --- | | type | Type of traffic, `egress` or `ingress`. | | route | ID of the route that bandwidth corresponds to when `prefer_name` is `false` (default), and name of the route when `prefer_name` to `true`. Default to an empty string if a request does not match any route. | | route\_id | Available only in Enterprise. ID of the route that bandwidth corresponds to regardless of the `prefer_name` setting. | | service | ID of the service that bandwidth corresponds to when `prefer_name` is `false` (default), and name of the service when `prefer_name` to `true`. Default to the configured value of host on the route if the matched route does not belong to any service. | | service\_id | Available only in Enterprise. ID of the service that bandwidth corresponds to regardless of the `prefer_name` setting. | | consumer | Name of the consumer associated with a request. Default to an empty string if no consumer is associated with the request. | | node | IP address of the upstream node. | | gateway\_group\_id | Available only in Enterprise. ID of the gateway group that bandwidth corresponds to. | | instance\_id | Available only in Enterprise. ID of the gateway instance that bandwidth corresponds to. | | api\_product\_id | Available only in Enterprise. Product ID that bandwidth corresponds to. | | request\_type | Available only in Enterprise. Request type that bandwidth corresponds to. | | llm\_model | Available only in Enterprise. LLM model that bandwidth corresponds to. | ### Labels for `apisix_http_latency`[​](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_http_latency "Direct link to labels-for-apisix_http_latency") The following labels are used to differentiate `apisix_http_latency` metrics. | Name | Description | | --- | --- | | type | Type of latencies. See [latency types](https://docs.api7.ai/hub/prometheus/#latency-types)
for details. | | route | ID of the route that latencies correspond to when `prefer_name` is `false` (default), and name of the route when `prefer_name` to `true`. Default to an empty string if a request does not match any route. | | route\_id | Available only in Enterprise. ID of the route that latencies correspond to regardless of the `prefer_name` setting. | | service | ID of the service that latencies correspond to when `prefer_name` is `false` (default), and name of the service when `prefer_name` to `true`. Default to the configured value of host on the route if the matched route does not belong to any service. | | service\_id | Available only in Enterprise. ID of the service that latencies correspond to regardless of the `prefer_name` setting. | | consumer | Name of the consumer associated with latencies. Default to an empty string if no consumer is associated with the request. | | node | IP address of the upstream node associated with latencies. | | gateway\_group\_id | Available only in Enterprise. ID of the gateway group that latencies correspond to. | | instance\_id | Available only in Enterprise. ID of the gateway instance that latencies correspond to. | | api\_product\_id | Available only in Enterprise. Product ID that latencies correspond to. | | request\_type | Available only in Enterprise. Request type that latencies correspond to. | | llm\_model | Available only in Enterprise. LLM model that latencies correspond to. | #### Latency Types[​](https://docs.api7.ai/hub/prometheus/#latency-types "Direct link to Latency Types") `apisix_http_latency` can be labeled with one of the three types: * `request` represents the time elapsed between the first byte was read from the client and the log write after the last byte was sent to the client. * `upstream` represents the time elapsed waiting on responses from the upstream service. * `apisix` represents the difference between the `request` latency and `upstream` latency. In other words, the APISIX latency is not only attributed to the Lua processing. It should be understood as follows: APISIX latency = downstream request time - upstream response time = downstream traffic latency + NGINX latency ### Labels for `apisix_upstream_status`[​](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_upstream_status "Direct link to labels-for-apisix_upstream_status") The following labels are used to differentiate `apisix_upstream_status` metrics. | Name | Description | | --- | --- | | name | Resource ID corresponding to the upstream configured with health checks, such as `/apisix/routes/1` and `/apisix/upstreams/1`. | | ip | IP address of the upstream node. | | port | Port number of the node. | ### Labels for `apisix_llm_latency`[​](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_llm_latency "Direct link to labels-for-apisix_llm_latency") The following labels are used to differentiate `apisix_llm_latency` metrics. | Name | Description | | --- | --- | | route | ID of the route that the HTTP status originates from when `prefer_name` is `false` (default), and name of the route when `prefer_name` to `true`. Default to an empty string if a request does not match any route. | | route\_id | ID of the route that the HTTP status originates from regardless of the `prefer_name` setting. | | service | ID of the service that the HTTP status originates from when `prefer_name` is `false` (default), and name of the service when `prefer_name` to `true`. Default to the configured value of host on the route if the matched route does not belong to any service. | | service\_id | ID of the service that the HTTP status originates from regardless of the `prefer_name` setting. | | consumer | Name of the consumer associated with a request. Default to an empty string if no consumer is associated with the request. | | node | IP address of the upstream node. | | gateway\_group\_id | ID of the gateway group that the HTTP status originates from. | | instance\_id | ID of the gateway instance that the HTTP status originates from. | | api\_product\_id | Product ID that the HTTP status originates from. | | request\_type | Request type that the HTTP status originates from. | | llm\_model | LLM model that the HTTP status originates from. | ### Labels for Other LLM Metrics[​](https://docs.api7.ai/hub/prometheus/#labels-for-other-llm-metrics "Direct link to Labels for Other LLM Metrics") The following labels are used to differentiate `apisix_llm_prompt_tokens`, `apisix_llm_completion_tokens`, and `apisix_llm_active_connections` metrics. | Name | Description | | --- | --- | | route | ID of the route that the HTTP status originates from when `prefer_name` is `false` (default), and name of the route when `prefer_name` to `true`. Default to an empty string if a request does not match any route. | | route\_id | ID of the route that the HTTP status originates from regardless of the `prefer_name` setting. | | matched\_uri | URI of the route that matches the request. Default to an empty string if a request does not match any route. | | matched\_host | Host of the route that matches the request. Default to an empty string if a request does not match any route, or host is not configured on the route. | | service | ID of the service that the HTTP status originates from when `prefer_name` is `false` (default), and name of the service when `prefer_name` to `true`. Default to the configured value of host on the route if the matched route does not belong to any service. | | service\_id | ID of the service that the HTTP status originates from regardless of the `prefer_name` setting. | | consumer | Name of the consumer associated with a request. Default to an empty string if no consumer is associated with the request. | | node | IP address of the upstream node. | | gateway\_group\_id | ID of the gateway group that the HTTP status originates from. | | instance\_id | ID of the gateway instance that the HTTP status originates from. | | api\_product\_id | Product ID that the HTTP status originates from. | | request\_type | Request type that the HTTP status originates from. | | llm\_model | LLM model that the HTTP status originates from. | Examples[​](https://docs.api7.ai/hub/prometheus/#examples "Direct link to Examples") ------------------------------------------------------------------------------------- The examples below demonstrate how you can work with the `prometheus` plugin for different scenarios. ### Get APISIX Metrics[​](https://docs.api7.ai/hub/prometheus/#get-apisix-metrics "Direct link to Get APISIX Metrics") The following example demonstrates how you can get metrics from APISIX. The default Prometheus metrics endpoint and other Prometheus related configurations can be found in the [static configuration](https://docs.api7.ai/hub/prometheus/configuration#static-configurations) . If you would like to customize these configurations, see [configuration files](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) . If you deploy APISIX In a containerized environment and would like to access the Prometheus metrics endpoint externally, update the configuration file as follows and [reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) : conf/config.yaml plugin_attr: prometheus: export_addr: ip: 0.0.0.0 Send a request to the APISIX Prometheus metrics endpoint: curl "http://127.0.0.1:9091/apisix/prometheus/metrics" You should see an output similar to the following: # HELP apisix_bandwidth Total bandwidth in bytes consumed per service in Apisix# TYPE apisix_bandwidth counterapisix_bandwidth{type="egress",route="",service="",consumer="",node=""} 8417apisix_bandwidth{type="egress",route="1",service="",consumer="",node="127.0.0.1"} 1420apisix_bandwidth{type="egress",route="2",service="",consumer="",node="127.0.0.1"} 1420apisix_bandwidth{type="ingress",route="",service="",consumer="",node=""} 189apisix_bandwidth{type="ingress",route="1",service="",consumer="",node="127.0.0.1"} 332apisix_bandwidth{type="ingress",route="2",service="",consumer="",node="127.0.0.1"} 332# HELP apisix_etcd_modify_indexes Etcd modify index for APISIX keys# TYPE apisix_etcd_modify_indexes gaugeapisix_etcd_modify_indexes{key="consumers"} 0apisix_etcd_modify_indexes{key="global_rules"} 0... ### Expose APISIX Metrics on Public API Endpoint[​](https://docs.api7.ai/hub/prometheus/#expose-apisix-metrics-on-public-api-endpoint "Direct link to Expose APISIX Metrics on Public API Endpoint") The following example demonstrates how you can disable the Prometheus export server that, by default, exposes an endpoint on port `9091`, and expose APISIX Prometheus metrics on a new public API endpoint on port `9080`, which APISIX uses to listen to other client requests. caution If a large quantity of metrics are being collected, the plugin could take up a significant amount of CPU resources for metric computations and negatively impact the processing of regular requests. To address this issue, APISIX uses [privileged agent](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/process.md#enable_privileged_agent) and offloads the metric computations to a separate process. This optimization applies automatically if you use the metric endpoint configured in the configuration files, as demonstrated [above](https://docs.api7.ai/hub/prometheus/#get-apisix-metrics) . If you expose the metric endpoint with the `public-api` plugin, you will not benefit from this optimization. Disable the Prometheus export server in the configuration file and [reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect: conf/config.yaml plugin_attr: prometheus: enable_export_server: false Next, create a route with [`public-api`](https://docs.api7.ai/hub/public-api) plugin and expose a public API endpoint for APISIX metrics: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "prometheus-metrics", "uri": "/apisix/prometheus/metrics", "plugins": { "public-api": {} } }' Send a request to the new metrics endpoint to verify: curl "http://127.0.0.1:9080/apisix/prometheus/metrics" You should see an output similar to the following: # HELP apisix_http_requests_total The total number of client requests since APISIX started# TYPE apisix_http_requests_total gaugeapisix_http_requests_total 1# HELP apisix_nginx_http_current_connections Number of HTTP connections# TYPE apisix_nginx_http_current_connections gaugeapisix_nginx_http_current_connections{state="accepted"} 1apisix_nginx_http_current_connections{state="active"} 1apisix_nginx_http_current_connections{state="handled"} 1apisix_nginx_http_current_connections{state="reading"} 0apisix_nginx_http_current_connections{state="waiting"} 0apisix_nginx_http_current_connections{state="writing"} 1... ### Integrate APISIX with Prometheus and Grafana[​](https://docs.api7.ai/hub/prometheus/#integrate-apisix-with-prometheus-and-grafana "Direct link to Integrate APISIX with Prometheus and Grafana") To learn about how to collect APISIX metrics with Prometheus and visualize them in Grafana, see [how-to guide](https://docs.api7.ai/apisix/how-to-guide/observability/monitor-apisix-with-prometheus) . ### Monitor Upstream Health Statuses[​](https://docs.api7.ai/hub/prometheus/#monitor-upstream-health-statuses "Direct link to Monitor Upstream Health Statuses") The following example demonstrates how to monitor the health status of upstream nodes. Create a route with the `prometheus` plugin and configure upstream active health checks: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "prometheus-route", "uri": "/get", "plugins": { "prometheus": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1, "127.0.0.1:20001": 1 }, "checks": { "active": { "timeout": 5, "http_path": "/status", "healthy": { "interval": 2, "successes": 1 }, "unhealthy": { "interval": 1, "http_failures": 2 } }, "passive": { "healthy": { "http_statuses": [200, 201], "successes": 3 }, "unhealthy": { "http_statuses": [500], "http_failures": 3, "tcp_failures": 3 } } } } }' Send a request to the APISIX Prometheus metrics endpoint: curl "http://127.0.0.1:9091/apisix/prometheus/metrics" You should see an output similar to the following: # HELP apisix_upstream_status upstream status from health check# TYPE apisix_upstream_status gaugeapisix_upstream_status{name="/apisix/routes/1",ip="54.237.103.220",port="80"} 1apisix_upstream_status{name="/apisix/routes/1",ip="127.0.0.1",port="20001"} 0 This shows that the upstream node `httpbin.org:80` is healthy and the upstream node `127.0.0.1:20001` is unhealthy. To learn more about how to configure active and passive health checks, see [health checks](https://docs.api7.ai/apisix/how-to-guide/traffic-management/health-check) . ### Add Extra Labels for Metrics[​](https://docs.api7.ai/hub/prometheus/#add-extra-labels-for-metrics "Direct link to Add Extra Labels for Metrics") The following example demonstrates how to add additional labels to metrics and use [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) in label values. Currently, only the following metrics support extra labels: * apisix\_http\_status * apisix\_http\_latency * apisix\_bandwidth Include the following configurations in the [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) to add labels for metrics and [reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect: conf/config.yaml plugin_attr: prometheus: # Plugin: prometheus metrics: # Create extra labels from built-in variables. http_status: extra_labels: # Set the extra labels for http_status metrics. - upstream_addr: $upstream_addr # Add an extra upstream_addr label with value being the NGINX variable $upstream_addr. - route_name: $route_name # Add an extra route_name label with value being the APISIX variable $route_name. Note that if you define a variable in the label value but it does not correspond to any existing [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) , the label value will default to an empty string. Create a route with the `prometheus` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "prometheus-route", "uri": "/get", "name": "extra-label", "plugins": { "prometheus": {} }, "upstream": { "nodes": { "httpbin.org:80": 1 } } }' Send a request to the route to verify: curl -i "http://127.0.0.1:9080/get" You should see an `HTTP/1.1 200 OK` response. Send a request to the APISIX Prometheus metrics endpoint: curl "http://127.0.0.1:9091/apisix/prometheus/metrics" You should see an output similar to the following: # HELP apisix_http_status HTTP status codes per service in APISIX# TYPE apisix_http_status counterapisix_http_status{code="200",route="1",matched_uri="/get",matched_host="",service="",consumer="",node="54.237.103.220",upstream_addr="54.237.103.220:80",route_name="extra-label"} 1 ### Monitor TCP/UDP Traffic with Prometheus[​](https://docs.api7.ai/hub/prometheus/#monitor-tcpudp-traffic-with-prometheus "Direct link to Monitor TCP/UDP Traffic with Prometheus") The following example demonstrates how to collect TCP/UDP traffic metrics in APISIX. Include the following configurations in the [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) to enable stream proxy and enable `prometheus` plugin for stream proxy. [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect: conf/config.yaml apisix: proxy_mode: http&stream # Enable both L4 & L7 proxies stream_proxy: # Configure L4 proxy tcp: - 9100 # Set TCP proxy listening port udp: - 9200 # Set UDP proxy listening portstream_plugins: - prometheus # Enable prometheus for stream proxy Create a [stream route](https://docs.api7.ai/apisix/key-concepts/stream-routes) with the `prometheus` plugin: curl "http://127.0.0.1:9180/apisix/admin/stream_routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "prometheus-route", "plugins": { "prometheus":{} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to the stream route to verify: curl -i "http://127.0.0.1:9100" You should see an `HTTP/1.1 200 OK` response. Send a request to the APISIX Prometheus metrics endpoint: curl "http://127.0.0.1:9091/apisix/prometheus/metrics" You should see an output similar to the following: # HELP apisix_stream_connection_total Total number of connections handled per stream route in APISIX# TYPE apisix_stream_connection_total counterapisix_stream_connection_total{route="1"} 1 * [Metrics](https://docs.api7.ai/hub/prometheus/#metrics) * [Labels](https://docs.api7.ai/hub/prometheus/#labels) * [Labels for `apisix_http_status`](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_http_status) * [Labels for `apisix_bandwidth`](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_bandwidth) * [Labels for `apisix_http_latency`](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_http_latency) * [Labels for `apisix_upstream_status`](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_upstream_status) * [Labels for `apisix_llm_latency`](https://docs.api7.ai/hub/prometheus/#labels-for-apisix_llm_latency) * [Labels for Other LLM Metrics](https://docs.api7.ai/hub/prometheus/#labels-for-other-llm-metrics) * [Examples](https://docs.api7.ai/hub/prometheus/#examples) * [Get APISIX Metrics](https://docs.api7.ai/hub/prometheus/#get-apisix-metrics) * [Expose APISIX Metrics on Public API Endpoint](https://docs.api7.ai/hub/prometheus/#expose-apisix-metrics-on-public-api-endpoint) * [Integrate APISIX with Prometheus and Grafana](https://docs.api7.ai/hub/prometheus/#integrate-apisix-with-prometheus-and-grafana) * [Monitor Upstream Health Statuses](https://docs.api7.ai/hub/prometheus/#monitor-upstream-health-statuses) * [Add Extra Labels for Metrics](https://docs.api7.ai/hub/prometheus/#add-extra-labels-for-metrics) * [Monitor TCP/UDP Traffic with Prometheus](https://docs.api7.ai/hub/prometheus/#monitor-tcpudp-traffic-with-prometheus) --- # APISIX Model Context Protocol (APISIX-MCP) | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/apisix-mcp/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page APISIX-MCP is a Model Context Protocol (MCP) server designed to bridge large language models (LLMs) with the APISIX Admin API. This integration enables natural language interactions for managing and viewing resources within APISIX through MCP-compatible AI clients, regardless of how APISIX is deployed. By leveraging APISIX-MCP, users can perform operations such as creating, retrieving, updating, deleting resources, as well as sending requests. This approach simplifies API management by allowing conversational commands to handle tasks that traditionally required manual configurations. APISIX-MCP is open-sourced and available on [npm](https://www.npmjs.com/package/apisix-mcp) and [GitHub](https://github.com/api7/apisix-mcp) . It can be configured via any MCP-compatible AI client, such as Claude Desktop, Cursor, or the Cline extension in VS Code. Install and Configure APISIX-MCP[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#install-and-configure-apisix-mcp "Direct link to Install and Configure APISIX-MCP") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The following are different ways of installation. ### npm[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#npm "Direct link to npm") If you are installing from npm, configure the MCP server with the following details and update the APISIX server address, port, Admin API port, prefix, and authentication key per your environment in the AI client: { "mcpServers": { "apisix-mcp": { "command": "npx", "args": ["-y","apisix-mcp"], "env": { "APISIX_SERVER_HOST": "http://127.0.0.1", "APISIX_SERVER_PORT": "9080", "APISIX_ADMIN_API_PORT": "9180", "APISIX_ADMIN_API_PREFIX": "/apisix/admin", "APISIX_ADMIN_KEY": "edd1c9f034335f136f87ad84b625c8f1" } } }} ### Smithery[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#smithery "Direct link to Smithery") To install APISIX-MCP for Claude Desktop, run: npx -y @smithery/cli install @api7/apisix-mcp --client claude ### Source Code[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#source-code "Direct link to Source Code") To install from source code, first clone the `apisix-mcp` repository: git clone https://github.com/api7/apisix-mcp.gitcd apisix-mcp Install the dependencies and build the project: pnpm installpnpm build Finally, configure the MCP server with the following details and update the APISIX server address, port, Admin API port, prefix, and authentication key per your environment in the AI client: { "mcpServers": { "apisix-mcp": { "command": "npx", "args": [ "your-apisix-mcp-path/dist/index.js" ], "env": { "APISIX_SERVER_HOST": "http://127.0.0.1", "APISIX_SERVER_PORT": "9080", "APISIX_ADMIN_API_PORT": "9180", "APISIX_ADMIN_API_PREFIX": "/apisix/admin", "APISIX_ADMIN_KEY": "edd1c9f034335f136f87ad84b625c8f1" } } }} tip The `APISIX_SERVER_HOST`, `APISIX_SERVER_PORT`, `APISIX_ADMIN_API_PORT`, `APISIX_ADMIN_API_PREFIX`, and `APISIX_ADMIN_KEY` above are configured to their default values. If your APISIX installation uses these default values, you can optionally omit the `env` configurations. Once the configurations are saved, you should see the MCP server is successfully installed in your AI client. Supported Operations[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#supported-operations "Direct link to Supported Operations") -------------------------------------------------------------------------------------------------------------------------------------- APISIX-MCP supports the following operations. When you use the AI client with APISIX-MCP, your natural language inputs will be translated into these operations. ### Common Operations[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#common-operations "Direct link to Common Operations") * `get_resource`: Retrieve resources by type, such as routes, services, and upstreams. * `delete_resource`: Delete resources by ID. * `send_request_to_gateway`: Send requests to the gateway. ### API Resources Operations[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#api-resources-operations "Direct link to API Resources Operations") * `create_route` / `update_route` / `delete_route`: Manage routes. * `create_service` / `update_service` / `delete_service`: Manage services. * `create_upstream` / `update_upstream` / `delete_upstream`: Manage upstreams. * `create_or_update_proto`: Manage protobuf definitions. * `create_or_update_stream_route`: Manage stream routes. ### Plugin Operations[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#plugin-operations "Direct link to Plugin Operations") * `get_all_plugin_names`: Get all available plugin names. * `get_plugin_info` / `get_plugins_by_type` / `get_plugin_schema`: Retrieve plugin configurations. * `create_plugin_config` / `update_plugin_config`: Manage plugin configurations. * `create_global_rule` / `update_global_rule`: Manage global plugins. * `get_plugin_metadata` / `create_or_update_plugin_metadata` / `delete_plugin_metadata`: Manage plugin metadata. ### Security Configuration[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#security-configuration "Direct link to Security Configuration") * `get_secret_by_id` / `create_secret` / `update_secret`: Manage secrets. * `create_or_update_consumer` / `delete_consumer`: Manage consumers. * `get_credential` / `create_or_update_credential` / `delete_credential`: Manage consumer credentials. * `create_consumer_group` / `delete_consumer_group`: Manage consumer groups. * `create_ssl` / `update_ssl` / `delete_ssl`: Manage SSL certificates. Example[​](https://docs.api7.ai/apisix/reference/apisix-mcp/#example "Direct link to Example") ----------------------------------------------------------------------------------------------- To create a route, you can enter this prompt in your AI client: Create a route named httpbin-get-route with route URI /get for accessing https://httpbin.org upstream, rate limiting the requests to 2 requests every 30 seconds. Print the route details after configuration. You should see the route is successfully created by your AI client: Successfully created route "httpbin-get-route" with the following configuration:URI: /getUpstream: https://httpbin.org (roundrobin load balancing)Rate limiting: 2 requests per 30 seconds per IP addressRoute ID: httpbin-get-routeStatus: Active (1)The route is now ready to accept requests at the /get path, which will be forwarded to httpbin.org with rate limiting applied. You can also send a request to the route from your AI client for verification: Send a request to this route. You should see a response similar to the following in your AI client: Successfully tested the route "httpbin-get-route":Received HTTP 200 response from httpbin.orgRate limiting headers show:Limit: 2 requests per 30 seconds (x-ratelimit-limit: 2)Remaining: 1 request available (x-ratelimit-remaining: 1)Reset in: 30 seconds (x-ratelimit-reset: 30)The route is properly configured and forwarding requests to httpbin.orgThe rate limiting plugin is working as expected For more examples, please follow the [getting started tutorials](https://docs.api7.ai/apisix/getting-started/configure-routes) . * [Install and Configure APISIX-MCP](https://docs.api7.ai/apisix/reference/apisix-mcp/#install-and-configure-apisix-mcp) * [npm](https://docs.api7.ai/apisix/reference/apisix-mcp/#npm) * [Smithery](https://docs.api7.ai/apisix/reference/apisix-mcp/#smithery) * [Source Code](https://docs.api7.ai/apisix/reference/apisix-mcp/#source-code) * [Supported Operations](https://docs.api7.ai/apisix/reference/apisix-mcp/#supported-operations) * [Common Operations](https://docs.api7.ai/apisix/reference/apisix-mcp/#common-operations) * [API Resources Operations](https://docs.api7.ai/apisix/reference/apisix-mcp/#api-resources-operations) * [Plugin Operations](https://docs.api7.ai/apisix/reference/apisix-mcp/#plugin-operations) * [Security Configuration](https://docs.api7.ai/apisix/reference/apisix-mcp/#security-configuration) * [Example](https://docs.api7.ai/apisix/reference/apisix-mcp/#example) --- # Limit Req | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/limit-req/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `limit-req` plugin uses the [leaky bucket](https://en.wikipedia.org/wiki/Leaky_bucket) algorithm to rate limit the number of the requests and allow for throttling. Local vs Redis Rate Limiting[​](https://docs.api7.ai/hub/limit-req/#local-vs-redis-rate-limiting "Direct link to Local vs Redis Rate Limiting") ------------------------------------------------------------------------------------------------------------------------------------------------ The `limit-req` plugin supports two modes of rate limiting: * **Local rate limiting**: Limits are enforced independently on each gateway instance. Each instance maintains its own counters, so the effective limit is roughly (limit × number of instances) when traffic is spread across instances. This is the default when no `policy` is set or when `policy` is `local`. * **Redis-based rate limiting**: Limits are shared across all gateway instances through Redis. All instances share the same quota, so the configured limit applies to all gateway instances. Examples[​](https://docs.api7.ai/hub/limit-req/#examples "Direct link to Examples") ------------------------------------------------------------------------------------ The examples below demonstrate how you can configure `limit-req` in different scenarios. ### Apply Rate Limiting by Remote Address[​](https://docs.api7.ai/hub/limit-req/#apply-rate-limiting-by-remote-address "Direct link to Apply Rate Limiting by Remote Address") The following example demonstrates the rate limiting of HTTP requests by a single variable, `remote_addr`. Create a route with `limit-req` plugin that allows for 1 QPS per remote address: * Admin API * ADC * Ingress Controller curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d ' { "id": "limit-req-route", "uri": "/get", "plugins": { "limit-req": { "rate": 1, "burst": 0, "key": "remote_addr", "key_type": "var", "rejected_code": 429, "policy": "local", "nodelay": true } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' adc.yaml services: - name: httpbin routes: - uris: - /get name: limit-req-route plugins: limit-req: rate: 1 burst: 0 key: remote_addr key_type: var rejected_code: 429 policy: local nodelay: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD limit-req-ic.yaml apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: limit-req-plugin-configspec: plugins: - name: limit-req config: rate: 1 burst: 0 key: remote_addr key_type: var rejected_code: 429 policy: local nodelay: true---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: limit-req-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /get filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: limit-req-plugin-config backendRefs: - name: httpbin-external-domain port: 80 limit-req-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: limit-req-routespec: ingressClassName: apisix http: - name: limit-req-route match: paths: - /get methods: - GET upstreams: - name: httpbin-external-domain plugins: - name: limit-req config: rate: 1 burst: 0 key: remote_addr key_type: var rejected_code: 429 policy: local nodelay: true Apply the configuration: kubectl apply -f limit-req-ic.yaml ❶ `rate`: limit the QPS to 1. ❷ `key`: set to `remote_addr` to apply rate limiting quota by remote address and consumer. ❸ `key_type`: set to `var` to interpret the `key` as a variable. Send a request to verify: curl -i "http://127.0.0.1:9080/get" You should see an `HTTP/1.1 200 OK` response. The request has consumed all the quota allowed for the time window. If you send the request again within the same second, you should receive an `HTTP/1.1 429 Too Many Requests` response, indicating the request surpasses the quota threshold. ### Implement API Throttling[​](https://docs.api7.ai/hub/limit-req/#implement-api-throttling "Direct link to Implement API Throttling") The following example demonstrates how to configure `burst` to allow overrun of the rate limiting threshold by the configured value and achieve request throttling. You will also see a comparison against when throttling is not implemented. Create a route with `limit-req` plugin that allows for 1 QPS per remote address, with a `burst` of 1: * Admin API * ADC * Ingress Controller curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "limit-req-route", "uri": "/get", "plugins": { "limit-req": { "rate": 1, "burst": 1, "key": "remote_addr", "rejected_code": 429, "policy": "local" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' adc.yaml services: - name: httpbin routes: - uris: - /get name: limit-req-route plugins: limit-req: rate: 1 burst: 1 key: remote_addr rejected_code: 429 policy: local upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD limit-req-ic.yaml apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: limit-req-plugin-configspec: plugins: - name: limit-req config: rate: 1 burst: 1 key: remote_addr rejected_code: 429 policy: local---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: limit-req-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /get filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: limit-req-plugin-config backendRefs: - name: httpbin-external-domain port: 80 limit-req-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: limit-req-routespec: ingressClassName: apisix http: - name: limit-req-route match: paths: - /get methods: - GET upstreams: - name: httpbin-external-domain plugins: - name: limit-req config: rate: 1 burst: 1 key: remote_addr rejected_code: 429 policy: local Apply the configuration: kubectl apply -f limit-req-ic.yaml ❶ `burst`: allow for 1 request exceeding the `rate` to be delayed for processing. Generate three requests to the route: resp=$(seq 3 | xargs -I{} curl -i "http://127.0.0.1:9080/get" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200 responses: $count_200 ; 429 responses: $count_429" You are likely to see that all three requests are successful: 200 responses: 3 ; 429 responses: 0 To see the effect without `burst`, update `burst` to 0 or set `nodelay` to `true` as follows: * Admin API * ADC * Ingress Controller curl "http://127.0.0.1:9180/apisix/admin/routes/limit-req-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "limit-req": { "nodelay": true } } }' Update the ADC YAML with `nodelay: true`: adc.yaml services: - name: httpbin routes: - uris: - /get name: limit-req-route plugins: limit-req: rate: 1 burst: 1 # alternatively, set burst to 0 key: remote_addr rejected_code: 429 policy: local nodelay: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration with updated plugin settings: adc sync -f adc.yaml Update the manifest file as such: * Gateway API * APISIX CRD limit-req-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: limit-req-plugin-configspec: plugins: - name: limit-req config: rate: 1 burst: 1 # alternatively, set burst to 0 key: remote_addr rejected_code: 429 policy: local nodelay: true limit-req-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: limit-req-routespec: ingressClassName: apisix http: - name: limit-req-route match: paths: - /get methods: - GET upstreams: - name: httpbin-external-domain plugins: - name: limit-req config: rate: 1 burst: 1 # alternatively, set burst to 0 key: remote_addr rejected_code: 429 policy: local nodelay: true Apply the updated configuration: kubectl apply -f limit-req-ic.yaml Generate three requests to the route again: resp=$(seq 3 | xargs -I{} curl -i "http://127.0.0.1:9080/get" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200 responses: $count_200 ; 429 responses: $count_429" You should see a response similar to the following, showing requests surpassing the rate have been rejected: 200 responses: 1 ; 429 responses: 2 ### Apply Rate Limiting by Remote Address and Consumer Name[​](https://docs.api7.ai/hub/limit-req/#apply-rate-limiting-by-remote-address-and-consumer-name "Direct link to Apply Rate Limiting by Remote Address and Consumer Name") The following example demonstrates the rate limiting of requests by a combination of variables, `remote_addr` and `consumer_name`. * Admin API * ADC * Ingress Controller Create a consumer `john`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Create a second consumer `jane`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jane" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-key-auth", "plugins": { "key-auth": { "key": "jane-key" } } }' Create a route with `key-auth` and `limit-req` plugins: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "limit-req-route", "uri": "/get", "plugins": { "key-auth": {}, "limit-req": { "rate": 1, "burst": 0, "key": "$remote_addr $consumer_name", "key_type": "var_combination", "rejected_code": 429, "policy": "local" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create two consumers and a route that enables rate limiting by consumers: adc.yaml consumers: - username: john credentials: - name: key-auth type: key-auth config: key: john-key - username: jane credentials: - name: key-auth type: key-auth config: key: jane-keyservices: - name: limit-req-service routes: - name: limit-req-route uris: - /get plugins: key-auth: {} limit-req: rate: 1 burst: 0 key: "$remote_addr $consumer_name" key_type: var_combination rejected_code: 429 policy: local upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Create two consumers and a route that enables rate limiting by consumers: * Gateway API * APISIX CRD limit-req-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: johnspec: gatewayRef: name: apisix credentials: - type: key-auth name: primary-key config: key: john-key---apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: janespec: gatewayRef: name: apisix credentials: - type: key-auth name: primary-key config: key: jane-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: limit-req-plugin-configspec: plugins: - name: key-auth config: _meta: disable: false - name: limit-req config: rate: 1 burst: 0 key: "$remote_addr $consumer_name" key_type: var_combination rejected_code: 429 policy: local---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: limit-req-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /get filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: limit-req-plugin-config backendRefs: - name: httpbin-external-domain port: 80 limit-req-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: johnspec: ingressClassName: apisix authParameter: keyAuth: value: key: john-key---apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: janespec: ingressClassName: apisix authParameter: keyAuth: value: key: jane-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: limit-req-routespec: ingressClassName: apisix http: - name: limit-req-route match: paths: - /get methods: - GET upstreams: - name: httpbin-external-domain plugins: - name: key-auth config: _meta: disable: false - name: limit-req config: rate: 1 burst: 0 key: "$remote_addr $consumer_name" key_type: var_combination rejected_code: 429 policy: local tip The ApisixConsumer CRD does not support configuring plugins on consumers. If you need to apply rate limiting per consumer, consider using [Gateway API Consumer](https://api7.ai/docs/ingress-controller/latest/crd/crd-go-client) which supports plugins on consumers. Apply the configuration: kubectl apply -f limit-req-ic.yaml ❶ `key-auth`: enable key authentication on the route. ❷ `key`: set to `$remote_addr $consumer_name` to apply rate limiting quota by remote address and consumer. ❸ `key_type`: set to `var_combination` to interpret the `key` as a combination of variables. Send two requests simultaneously, each for one consumer: curl -i "http://127.0.0.1:9080/get" -H 'apikey: jane-key' & \curl -i "http://127.0.0.1:9080/get" -H 'apikey: john-key' & You should receive `HTTP/1.1 200 OK` for both requests, indicating the request has not exceeded the threshold for each consumer. If you send more requests as either consumer within the same second, you should receive an `HTTP/1.1 429 Too Many Requests` response. This verifies the plugin rate limits by the combination of variables, `remote_addr` and `consumer_name`. * [Local vs Redis Rate Limiting](https://docs.api7.ai/hub/limit-req/#local-vs-redis-rate-limiting) * [Examples](https://docs.api7.ai/hub/limit-req/#examples) * [Apply Rate Limiting by Remote Address](https://docs.api7.ai/hub/limit-req/#apply-rate-limiting-by-remote-address) * [Implement API Throttling](https://docs.api7.ai/hub/limit-req/#implement-api-throttling) * [Apply Rate Limiting by Remote Address and Consumer Name](https://docs.api7.ai/hub/limit-req/#apply-rate-limiting-by-remote-address-and-consumer-name) --- # Proxy Buffering | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/proxy-buffering/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `proxy-buffering` plugin dynamically disables the NGINX [`proxy_buffering`](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering) directive. You should disable buffering in scenarios when configuring API7 gateways with [server-sent events (SSE)](https://en.wikipedia.org/wiki/Server-sent_events) upstream services, or services that respond with chunked data, such as the etcd watch events. Examples[​](https://docs.api7.ai/hub/proxy-buffering/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------ ### Configure With SSE Upstream[​](https://docs.api7.ai/hub/proxy-buffering/#configure-with-sse-upstream "Direct link to Configure With SSE Upstream") The following example demonstrates how to disable `proxy_buffering` on a route with an SSE upstream service. Start a [sample upstream service](https://hub.docker.com/r/jmalloc/echo-server) for SSE: docker run -d -p 8080:8080 jmalloc/echo-server Create a route to the upstream and configure `proxy-buffering`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-buffering-route", "uri": "/.sse", "plugins": { "proxy-buffering": { "disable_proxy_buffering": true } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:8080": 1 } } }' Send a request to the route: curl "http://127.0.0.1:9080/.sse" -H "Accept: text/event-stream" You should receive a `HTTP/1.1 200 OK` response and see continuous event stream similar to the following: event: serverdata: 162291b28f55id: 1event: requestdata: GET /.sse HTTP/1.1data: data: Host: 127.0.0.1:9080data: Accept: text/event-streamdata: User-Agent: curl/7.74.0data: X-Forwarded-For: 172.19.0.1data: X-Forwarded-Host: 127.0.0.1data: X-Forwarded-Port: 9080data: X-Forwarded-Proto: httpdata: X-Real-Ip: 172.19.0.1data: id: 2event: timedata: 2023-10-19T02:13:53Zid: 3event: timedata: 2023-10-19T02:13:54Zid: 4event: timedata: 2023-10-19T02:13:55Zid: 5... #### (Optional) See Buffering in Effect[​](https://docs.api7.ai/hub/proxy-buffering/#optional-see-buffering-in-effect "Direct link to (Optional) See Buffering in Effect") You will see the proxy buffering effect on SSE when `proxy_buffering` is not turned off in this section. For demonstration, the proxy buffer size will be adjusted to a larger value. Add the following snippet to the [configuration file](https://docs.api7.ai/enterprise/reference/configuration) : config.yaml nginx_config: http_configuration_snippet: | server { listen 9080; location /sse { proxy_buffering on; proxy_buffers 4 2m; } } ❶ Though explicitly configured, `proxy_buffering` is `on` [by default](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering) . ❷ Configure `proxy_buffers` to use 4 buffers, each with a size of 2 MB. Reload the gateway for changes to take effect. Recreate the route without `proxy-buffering`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "proxy-buffering-route", "uri": "/.sse", "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:8080": 1 } } }' Send a request to the route: curl "http://127.0.0.1:9080/.sse" -H "Accept: text/event-stream" You should receive a `HTTP/1.1 200 OK` response and see the same event stream. However, note that events are not received at a regular interval due to the effect of the buffer, which is undesirable when working with an SSE upstream. * [Examples](https://docs.api7.ai/hub/proxy-buffering/#examples) * [Configure With SSE Upstream](https://docs.api7.ai/hub/proxy-buffering/#configure-with-sse-upstream) --- # Real IP | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/real-ip/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `real-ip` plugin allows APISIX to set the client's real IP by IP address passed in the HTTP header or HTTP query string. This is particularly useful when APISIX is behind a reverse proxy, since the proxy could act as the request originating client otherwise. The plugin is functionally similar to NGINX's [ngx\_http\_realip\_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html) but offers more flexibilities. Examples[​](https://docs.api7.ai/hub/real-ip/#examples "Direct link to Examples") ---------------------------------------------------------------------------------- The examples below demonstrate how you can configure `real-ip` in different scenarios. ### Obtain Real Client Address From URI Parameter[​](https://docs.api7.ai/hub/real-ip/#obtain-real-client-address-from-uri-parameter "Direct link to Obtain Real Client Address From URI Parameter") The following example demonstrates how to update client IP address with an URI parameter. Create a route as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "real-ip-route", "uri": "/get", "plugins": { "real-ip": { "source": "arg_realip", "trusted_addresses": ["127.0.0.0/24"] }, "response-rewrite": { "headers": { "remote_addr": "$remote_addr", "remote_port": "$remote_port" } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Configure `source` to obtain value from the URL parameter `realip` using the [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) . ❷ Use the `response-rewrite` plugin to set response headers to verify if the client IP and port were actually updated. Send a request to the route with real IP and port in the URL parameter: curl -i "http://127.0.0.1:9080/get?realip=1.2.3.4:9080" You should see the response includes the following header: remote-addr: 1.2.3.4remote-port: 9080 ### Obtain Real Client Address From Header[​](https://docs.api7.ai/hub/real-ip/#obtain-real-client-address-from-header "Direct link to Obtain Real Client Address From Header") The following example shows how to set the real client IP when APISIX is behind a reverse proxy, such as a load balancer, when the proxy exposes the real client IP in the [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) header. Create a route as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "real-ip-route", "uri": "/get", "plugins": { "real-ip": { "source": "http_x_forwarded_for", "trusted_addresses": ["127.0.0.0/24"] }, "response-rewrite": { "headers": { "remote_addr": "$remote_addr" } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Configure `source` to obtain value from the request header `X-Forwarded-For` using the [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) . ❷ Use the `response-rewrite` plugin to set a response header to verify if the client IP was actually updated. Send a request to the route: curl -i "http://127.0.0.1:9080/get" You should see a response including the following header: remote-addr: 10.26.3.19 The IP address should correspond to the IP address of the request originating client. ### Obtain Real Client Address Behind Multiple Proxies[​](https://docs.api7.ai/hub/real-ip/#obtain-real-client-address-behind-multiple-proxies "Direct link to Obtain Real Client Address Behind Multiple Proxies") The following example shows how to get the real client IP when APISIX is behind multiple proxies, which causes [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) header to include a list of proxy IP addresses. Create a route as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "real-ip-route", "uri": "/get", "plugins": { "real-ip": { "source": "http_x_forwarded_for", "recursive": true, "trusted_addresses": ["192.128.0.0/16", "127.0.0.0/24"] }, "response-rewrite": { "headers": { "remote_addr": "$remote_addr" } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' ❶ Configure `source` to obtain value from the request header `X-Forwarded-For` using the [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) . ❷ Set `recursive` to `true` so that the original client address that matches one of the trusted addresses is replaced by the last non-trusted address sent in the configured `source`. ❸ Use the `response-rewrite` plugin to set a response header to verify if the client IP was actually updated. Send a request to the route: curl -i "http://127.0.0.1:9080/get" \ -H "X-Forwarded-For: 127.0.0.2, 192.128.1.1, 127.0.0.1" You should see a response including the following header: remote-addr: 127.0.0.2 * [Examples](https://docs.api7.ai/hub/real-ip/#examples) * [Obtain Real Client Address From URI Parameter](https://docs.api7.ai/hub/real-ip/#obtain-real-client-address-from-uri-parameter) * [Obtain Real Client Address From Header](https://docs.api7.ai/hub/real-ip/#obtain-real-client-address-from-header) * [Obtain Real Client Address Behind Multiple Proxies](https://docs.api7.ai/hub/real-ip/#obtain-real-client-address-behind-multiple-proxies) --- # Loki Logger | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/loki-logger/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `loki-logger` plugin pushes request and response logs in batches to [Grafana Loki](https://grafana.com/oss/loki/) , via the [Loki HTTP API](https://grafana.com/docs/loki/latest/reference/loki-http-api/#loki-http-api) `/loki/api/v1/push`. When enabled, the plugin will serialize the request context information to [JSON objects](https://grafana.com/docs/loki/latest/api/#push-log-entries-to-loki) and add them to the queue, before they are pushed to Loki. The plugin also supports the customization of log formats. Examples[​](https://docs.api7.ai/hub/loki-logger/#examples "Direct link to Examples") -------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `loki-logger` plugin for different scenarios. To follow along the examples, start a sample Loki instance in Docker: wget https://raw.githubusercontent.com/grafana/loki/v3.0.0/cmd/loki/loki-local-config.yaml -O loki-config.yamldocker run --name loki -d -v $(pwd):/mnt/config -p 3100:3100 grafana/loki:3.2.1 -config.file=/mnt/config/loki-config.yaml Additionally, start a Grafana instance to view and visualize the logs: docker run -d --name=apisix-quickstart-grafana \ -p 3000:3000 \ grafana/grafana-oss To connect Loki and Grafana, visit Grafana at [`http://localhost:3000`](http://localhost:3000/) . Under **Connections > Data sources**, add a new data source and select Loki. Your connection URL should follow the format of `http://{your_ip_address}:3100`. When saving the new data source, Grafana should also test the connection, and you are expected to see Grafana notifying the data source is successfully connected. ### Log Requests and Responses in Default Log Format[​](https://docs.api7.ai/hub/loki-logger/#log-requests-and-responses-in-default-log-format "Direct link to Log Requests and Responses in Default Log Format") The following example demonstrates how you can configure the `loki-logger` plugin on a route to log requests and responses going through the route. Create a route with the `loki-logger` plugin and configure the address of Loki: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "loki-logger-route", "uri": "/anything", "plugins": { "loki-logger": { "endpoint_addrs": ["http://192.168.1.5:3100"] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ Replace with your IP address. Send a few requests to the route to generate log entries: curl "http://127.0.0.1:9080/anything" You should receive `HTTP/1.1 200 OK` responses for all requests. Navigate to the [Grafana explore view](http://localhost:3000/explore) and run a query `job = apisix`. You should see a number of logs corresponding to your requests, such as the following: { "route_id": "loki-logger-route", "response": { "status": 200, "headers": { "date": "Fri, 03 Jan 2025 03:54:26 GMT", "server": "APISIX/3.13.0", "access-control-allow-credentials": "true", "content-length": "391", "access-control-allow-origin": "*", "content-type": "application/json", "connection": "close" }, "size": 619 }, "start_time": 1735876466, "client_ip": "192.168.65.1", "service_id": "", "apisix_latency": 5.0000038146973, "upstream": "34.197.122.172:80", "upstream_latency": 666, "server": { "hostname": "0b9a772e68f8", "version": "3.13.0" }, "request": { "headers": { "user-agent": "curl/8.6.0", "accept": "*/*", "host": "127.0.0.1:9080" }, "size": 85, "method": "GET", "url": "http://127.0.0.1:9080/anything", "querystring": {}, "uri": "/anything" }, "latency": 671.0000038147} This verifies that Loki has been receiving logs from APISIX. You may also create dashboards in Grafana to further visualize and analyze the logs. ### Customize Log Format with Plugin Metadata[​](https://docs.api7.ai/hub/loki-logger/#customize-log-format-with-plugin-metadata "Direct link to Customize Log Format with Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) . Create a route with the `loki-logger` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "loki-logger-route", "uri": "/anything", "plugins": { "loki-logger": { "endpoint_addrs": ["http://192.168.1.5:3100"] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' Configure plugin metadata for `loki-logger`, which will update the log format for all routes of which requests would be logged: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/loki-logger" -X PUT \ -H 'X-API-KEY: ${ADMIN_API_KEY}' \ -d '{ "log_format": { "host": "$host", "client_ip": "$remote_addr", "route_id": "$route_id", "@timestamp": "$time_iso8601" } }' Send a request to the route to generate a new log entry: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Navigate to the [Grafana explore view](http://localhost:3000/explore) and run a query `job = apisix`. You should see a log entry corresponding to your request, similar to the following: { "@timestamp":"2025-01-03T21:11:34+00:00", "client_ip":"192.168.65.1", "route_id":"loki-logger-route", "host":"127.0.0.1"} If the plugin on a route specifies a specific log format, it will take precedence over the log format specified in the plugin metadata. For instance, update the plugin on the previous route as such: curl "http://127.0.0.1:9180/apisix/admin/routes/loki-logger-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "loki-logger": { "log_format": { "route_id": "$route_id", "client_ip": "$remote_addr", "@timestamp": "$time_iso8601" } } } }' Send a request to the route to generate a new log entry: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Navigate to the [Grafana explore view](http://localhost:3000/explore) and re-run the query `job = apisix`. You should see a log entry corresponding to your request, consistent with the format configured on the route, similar to the following: { "client_ip":"192.168.65.1", "route_id":"loki-logger-route", "@timestamp":"2025-01-03T21:19:45+00:00"} ### Log Request Bodies Conditionally[​](https://docs.api7.ai/hub/loki-logger/#log-request-bodies-conditionally "Direct link to Log Request Bodies Conditionally") The following example demonstrates how you can conditionally log request body. Create a route with `loki-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "loki-logger-route", "uri": "/anything", "plugins": { "loki-logger": { "endpoint_addrs": ["http://192.168.1.5:3100"], "include_req_body": true, "include_req_body_expr": [["arg_log_body", "==", "yes"]] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `include_req_body`: set to true to include request body. ❷ `include_req_body_expr`: only include request body if the URL query string `log_body` is `yes`. Send a request to the route with a URL query string satisfying the condition: curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST -d '{"env": "dev"}' Navigate to the [Grafana explore view](http://localhost:3000/explore) and run the query `job = apisix`. You should see a log entry corresponding to your request, where the request body is logged: { "route_id": "loki-logger-route", ..., "request": { "headers": { ... }, "body": "{\"env\": \"dev\"}", "size": 182, "method": "POST", "url": "http://127.0.0.1:9080/anything?log_body=yes", "querystring": { "log_body": "yes" }, "uri": "/anything?log_body=yes" }, "latency": 809.99994277954} Send a request to the route without any URL query string: curl -i "http://127.0.0.1:9080/anything" -X POST -d '{"env": "dev"}' Navigate to the [Grafana explore view](http://localhost:3000/explore) and run the query `job = apisix`. You should see a log entry corresponding to your request, where the request body is not logged: { "route_id": "loki-logger-route", ..., "request": { "headers": { ... }, "size": 169, "method": "POST", "url": "http://127.0.0.1:9080/anything", "querystring": {}, "uri": "/anything" }, "latency": 557.00016021729} info If you have customized the `log_format` in addition to setting `include_req_body` or `include_resp_body` to `true`, the plugin would not include the bodies in the logs. As a workaround, you may be able to use the NGINX variable `$request_body` in the log format, such as: { "kafka-logger": { ..., "log_format": {"body": "$request_body"} }} * [Examples](https://docs.api7.ai/hub/loki-logger/#examples) * [Log Requests and Responses in Default Log Format](https://docs.api7.ai/hub/loki-logger/#log-requests-and-responses-in-default-log-format) * [Customize Log Format with Plugin Metadata](https://docs.api7.ai/hub/loki-logger/#customize-log-format-with-plugin-metadata) * [Log Request Bodies Conditionally](https://docs.api7.ai/hub/loki-logger/#log-request-bodies-conditionally) --- # syslog | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/syslog/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `syslog` plugin pushes request and response logs as JSON objects to syslog servers in batches and supports the customization of log formats. Examples[​](https://docs.api7.ai/hub/syslog/#examples "Direct link to Examples") --------------------------------------------------------------------------------- The examples below demonstrate how you can configure `syslog` plugin for different scenarios. To follow along the examples, you should have your syslog server running, or start an example rsyslog server in Docker: docker run -d -p 514:514 --name example-rsyslog-server rsyslog/syslog_appliance_alpine ### Push Log to Syslog Server[​](https://docs.api7.ai/hub/syslog/#push-log-to-syslog-server "Direct link to Push Log to Syslog Server") The following example demonstrates how you can enable the `syslog` plugin on a route, which logs client requests to the route and pushes logs to syslog server. Create a route with `syslog` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "syslog-route", "uri": "/anything", "plugins": { "syslog": { "host" : "172.0.0.1", "port" : 514, "flush_limit" : 1 } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `host`: replace with the address of your syslog server. ❷ `port`: replace with the port of your syslog server. ❸ `flush_limit`: set to 1 to push log to the syslog server immediately. Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. In the syslog server, you should see a log entry similar to the following: { "response": { "status": 200, "headers": { "access-control-allow-credentials": "true", "connection": "close", "date": "Sat, 02 Mar 2024 00:14:19 GMT", "access-control-allow-origin": "*", "server": "APISIX/3.8.0", "content-type": "application/json", "content-length": "387" }, "size": 614 }, "service_id": "", "client_ip": "172.19.0.1", "server": { "hostname": "eff61bf7be4d", "version": "3.8.0" }, "upstream": "35.171.123.176:80", "apisix_latency": 13.999900817871, "request": { "method": "GET", "url": "http://127.0.0.1:9080/anything", "querystring": {}, "size": 86, "uri": "/anything", "headers": { "host": "127.0.0.1:9080", "accept": "*/*", "user-agent": "curl/7.29.0" } }, "route_id": "syslog-route", "upstream_latency": 165, "latency": 178.99990081787, "start_time": 1709334859598} ### Customize Log Format With Plugin Metadata[​](https://docs.api7.ai/hub/syslog/#customize-log-format-with-plugin-metadata "Direct link to Customize Log Format With Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) . The log format configured in plugin metadata will apply to all syslog plugin instances. Create a route with the `syslog` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "syslog-route", "uri": "/anything", "plugins": { "syslog": { "host" : "172.0.0.1", "port" : 514, "flush_limit" : 1 } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Configure plugin metadata for `syslog`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/syslog" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "route_id": "$route_id", "client_ip": "$remote_addr", "resp_content_type": "$sent_http_Content_Type" } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" In the syslog server, you should see a log entry similar to the following: { "@timestamp": "2024-03-02T00:00:31+00:00", "resp_content_type": "application/json", "host": "127.0.0.1", "route_id": "syslog-route", "client_ip": "172.19.0.1"} ### Log Request Bodies Conditionally[​](https://docs.api7.ai/hub/syslog/#log-request-bodies-conditionally "Direct link to Log Request Bodies Conditionally") The following example demonstrates how you can conditionally log request body. Create a route with the `syslog` plugin as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "syslog-route", "uri": "/anything", "plugins": { "syslog": { "host" : "172.0.0.1", "port" : 514, "flush_limit" : 1, "include_req_body": true, "include_req_body_expr": [["arg_log_body", "==", "yes"]] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `include_req_body`: set to true to include request body. ❷ `include_req_body_expr`: only include request body if the URL query string `log_body` is `yes`. Send a request to the route with a URL query string satisfying the condition: curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST -d '{"env": "dev"}' You should see the request body logged: { "response": { "status": 200, "headers": { "connection": "close", "server": "APISIX/3.8.0", "date": "Sat, 02 Mar 2024 00:46:04 GMT", "access-control-allow-origin": "*", "access-control-allow-credentials": "true", "content-type": "application/json", "content-length": "545" }, "size": 772 }, "service_id": "", "client_ip": "172.19.0.1", "server": { "hostname": "eff61bf7be4d", "version": "3.8.0" }, "upstream": "35.171.123.176:80", "apisix_latency": 0, "request": { "method": "POST", "url": "http://127.0.0.1:9080/anything?log_body=yes", "querystring": { "log_body": "yes" }, "size": 183, "body": "{\"env\": \"dev\"}", "uri": "/anything?log_body=yes", "headers": { "accept": "*/*", "user-agent": "curl/7.29.0", "host": "127.0.0.1:9080", "content-type": "application/x-www-form-urlencoded", "content-length": "14" } }, "route_id": "syslog-route", "upstream_latency": 165, "latency": 164.99996185303, "start_time": 1709340364390} Send a request to the route without any URL query string: curl -i "http://127.0.0.1:9080/post" -X POST -d '{"env": "dev"}' You should not observe the request body in the log. info If you have customized the `log_format` in addition to setting `include_req_body` or `include_resp_body` to `true`, the plugin would not include the bodies in the logs. As a workaround, you may be able to use the NGINX variable `$request_body` in the log format, such as: { "syslog": { ..., "log_format": {"body": "$request_body"} }} * [Examples](https://docs.api7.ai/hub/syslog/#examples) * [Push Log to Syslog Server](https://docs.api7.ai/hub/syslog/#push-log-to-syslog-server) * [Customize Log Format With Plugin Metadata](https://docs.api7.ai/hub/syslog/#customize-log-format-with-plugin-metadata) * [Log Request Bodies Conditionally](https://docs.api7.ai/hub/syslog/#log-request-bodies-conditionally) --- # Splunk HEC Logging | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/splunk-hec-logging/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `splunk-hec-logging` plugin serializes request and response context information to [Splunk Event Data format](https://docs.splunk.com/Documentation/Splunk/latest/Data/FormateventsforHTTPEventCollector#Event_metadata) and push to your [Splunk HTTP Event Collector (HEC)](https://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector) in batches. The plugin also supports the customization of log formats. Examples[​](https://docs.api7.ai/hub/splunk-hec-logging/#examples "Direct link to Examples") --------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `splunk-hec-logging` plugin for different scenarios. To follow along the examples, please complete the following steps to set up Splunk: * Install [Splunk](https://www.splunk.com/en_us/download.html) . Splunk Web should be running at `localhost:8000` by default. * See [set up and use HTTP Event Collector in Splunk Web](https://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector) to set up an HTTP Event Collector. * Navigate to **Settings > Data Inputs** at the upper-right corner of the console. You should see at least one input for the HTTP Event Collector. Note down the token value. * Navigate to **Settings > Data Inputs** at the upper-right corner of the console and select **HTTP Event Collector**. In **Global Settings**, enable all tokens. * In **Global Settings**, you should also find the collector's default port to be `8088`. To verify the setup, execute the following command with your token: curl "http://localhost:8088/services/collector/event" \ -H "Authorization: Splunk " \ -d '{"event": "hello world"}' You should see a `success` response. ### Push Log to Splunk[​](https://docs.api7.ai/hub/splunk-hec-logging/#push-log-to-splunk "Direct link to Push Log to Splunk") The following example demonstrates how you can enable the `splunk-hec-logging` plugin on a route, which logs client requests and pushes logs to Splunk. Create a route as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "splunk-route", "uri": "/anything", "plugins": { "splunk-hec-logging":{ "endpoint":{ "uri":"http://192.168.2.108:8088/services/collector/event", "token":"26b15ddd-31db-455b-ak0c-9b5be3decc4a" } } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ Configure to the Splunk HTTP collector's endpoint. Replace with your IP address. ❷ Replace with your collector's token. Send a few requests to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` responses. Navigate to Splunk Web and select **Search & Reporting** in the left menu. In the search box, enter `source="apache-apisix-splunk-hec-logging"` and search for events from APISIX. You should see events corresponding to your requests, such as the following: { "response_size": 617, "response_headers": { "server": "APISIX/3.10.0", "connection": "close", "content-type": "application/json", "access-control-allow-credentials": "true", "access-control-allow-origin": "*", "date": "Wed, 27 Nov 2024 19:49:27 GMT", "content-length": "389" }, "request_headers": { "host": "127.0.0.1:9080", "user-agent": "curl/8.6.0", "accept": "*/*" }, "request_query": {}, "request_url": "http://127.0.0.1:9080/anything", "upstream": "18.208.8.205:80", "latency": 746.00005149841, "request_method": "GET", "request_size": 85, "response_status": 200} ### Log Request and Response Headers With Plugin Metadata[​](https://docs.api7.ai/hub/splunk-hec-logging/#log-request-and-response-headers-with-plugin-metadata "Direct link to Log Request and Response Headers With Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) and [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) to log specific headers from request and response. In APISIX, [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) is used to configure the common metadata fields of all plugin instances of the same plugin. It is useful when a plugin is enabled across multiple resources and requires a universal update to their metadata fields. Create a route as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "splunk-route", "uri": "/anything", "plugins": { "splunk-hec-logging":{ "endpoint":{ "uri":"http://192.168.2.108:8088/services/collector/event", "token":"26b15ddd-31db-455b-ak0c-9b5be3decc4a" } } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ Configure to the Splunk HTTP collector's endpoint. Replace with your IP address. ❷ Replace with your collector's token. Configure the plugin metadata for `splunk-hec-logging`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/splunk-hec-logging" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr", "env": "$http_env", "resp_content_type": "$sent_http_Content_Type" } }' ❶ log the custom request header `env`. ❷ log the response header `Content-Type`. Send a request to the route with the `env` header: curl -i "http://127.0.0.1:9080/anything" -H "env: dev" Navigate to Splunk Web and select **Search & Reporting** in the left menu. In the search box, enter `source="apache-apisix-splunk-hec-logging"` and search for events. You should see the latest event correspond to your request, similar to the following: { "host":"127.0.0.1", "env":"dev", "client_ip":"192.168.65.1", "@timestamp":"2024-11-27T20:59:28+00:00", "route_id":"splunk-route", "resp_content_type":"application/json"} * [Examples](https://docs.api7.ai/hub/splunk-hec-logging/#examples) * [Push Log to Splunk](https://docs.api7.ai/hub/splunk-hec-logging/#push-log-to-splunk) * [Log Request and Response Headers With Plugin Metadata](https://docs.api7.ai/hub/splunk-hec-logging/#log-request-and-response-headers-with-plugin-metadata) --- # HTTP Logger | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/http-logger/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `http-logger` plugin pushes request and response logs as JSON objects to HTTP(S) servers in batches and supports the customization of log formats. Examples[​](https://docs.api7.ai/hub/http-logger/#examples "Direct link to Examples") -------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `http-logger` plugin for different scenarios. To follow along the examples, start a mock HTTP logging endpoint using [mockbin](https://mockbin.io/) and note down the mockbin URL. ### Log Requests in Default Log Format[​](https://docs.api7.ai/hub/http-logger/#log-requests-in-default-log-format "Direct link to Log Requests in Default Log Format") The following example demonstrates how you can configure the `http-logger` plugin on a route to log information of requests hitting the route. Create a route with the `http-logger` plugin and configure the plugin with your server URI: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "http-logger-route", "uri": "/anything", "plugins": { "http-logger": { "uri": "https://669f05eb10ca49f18763e023312c3d77.api.mockbin.io/" } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Send a request to the route: curl "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. In your mockbin, you should see a log entry similar to the following: [ { "upstream": "3.213.1.197:80", "server": { "hostname": "7d8d831179d4", "version": "3.9.0" }, "start_time": 1718291190508, "client_ip": "192.168.65.1", "response": { "status": 200, "headers": { "server": "APISIX/3.9.0", "content-length": "390", "access-control-allow-credentials": "true", "connection": "close", "date": "Thu, 13 Jun 2024 15:06:31 GMT", "access-control-allow-origin": "*", "content-type": "application/json" }, "size": 617 }, "latency": 1200.0000476837, "upstream_latency": 1133, "apisix_latency": 67.000047683716, "request": { "url": "http://127.0.0.1:9080/anything", "querystring": {}, "method": "GET", "uri": "/anything", "headers": { "accept": "*/*", "user-agent": "curl/8.6.0", "host": "127.0.0.1:9080" }, "size": 85 }, "service_id": "", "route_id": "http-logger-route" }] ### Log Request and Response Headers With Plugin Metadata[​](https://docs.api7.ai/hub/http-logger/#log-request-and-response-headers-with-plugin-metadata "Direct link to Log Request and Response Headers With Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) and [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) to log specific headers from request and response. In APISIX, [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) is used to configure the common metadata fields of all plugin instances of the same plugin. It is useful when a plugin is enabled across multiple resources and requires a universal update to their metadata fields. First, create a route with the `http-logger` plugin and configure the plugin with your server URI: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "http-logger-route", "uri": "/anything", "plugins": { "http-logger": { "uri": "https://669f05eb10ca49f18763e023312c3d77.api.mockbin.io/" } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Next, configure the plugin metadata for `http-logger`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/http-logger" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr", "env": "$http_env", "resp_content_type": "$sent_http_Content_Type" } }' ❶ log the custom request header `env`. ❷ log the response header `Content-Type`. Send a request to the route with the `env` header: curl "http://127.0.0.1:9080/anything" -H "env: dev" You should receive an `HTTP/1.1 200 OK` response. In your mockbin, you should see a log entry similar to the following: [ { "route_id": "http-logger-route", "client_ip": "192.168.65.1", "@timestamp": "2024-06-13T15:19:34+00:00", "host": "127.0.0.1", "env": "dev", "resp_content_type": "application/json" }] ### Log Request Bodies Conditionally[​](https://docs.api7.ai/hub/http-logger/#log-request-bodies-conditionally "Direct link to Log Request Bodies Conditionally") The following example demonstrates how you can conditionally log request body. Create a route with the `http-logger` plugin as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "http-logger-route", "uri": "/anything", "plugins": { "http-logger": { "uri": "https://669f05eb10ca49f18763e023312c3d77.api.mockbin.io/", "include_req_body": true, "include_req_body_expr": [["arg_log_body", "==", "yes"]] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `include_req_body`: set to true to include request body. ❷ `include_req_body_expr`: only include request body if the URL query string `log_body` is `yes`. Send a request to the route with a URL query string satisfying the condition: curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST -d '{"env": "dev"}' You should see the request body logged: [ { "request": { "url": "http://127.0.0.1:9080/anything?log_body=yes", "querystring": { "log_body": "yes" }, "uri": "/anything?log_body=yes", ..., "body": "{\"env\": \"dev\"}", }, ... }] Send a request to the route without any URL query string: curl -i "http://127.0.0.1:9080/anything" -X POST -d '{"env": "dev"}' You should not observe the request body in the log. info If you have customized the `log_format` in addition to setting `include_req_body` or `include_resp_body` to `true`, the plugin would not include the bodies in the logs. As a workaround, you may be able to use the NGINX variable `$request_body` in the log format, such as: { "http-logger": { ..., "log_format": {"body": "$request_body"} }} * [Examples](https://docs.api7.ai/hub/http-logger/#examples) * [Log Requests in Default Log Format](https://docs.api7.ai/hub/http-logger/#log-requests-in-default-log-format) * [Log Request and Response Headers With Plugin Metadata](https://docs.api7.ai/hub/http-logger/#log-request-and-response-headers-with-plugin-metadata) * [Log Request Bodies Conditionally](https://docs.api7.ai/hub/http-logger/#log-request-bodies-conditionally) --- # SkyWalking Logger | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/skywalking-logger/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `skywalking-logger` plugin pushes request and response logs as JSON objects to SkyWalking OAP server in batches and supports the customization of log formats. If there is an existing tracing context, it sets up the trace-log correlation automatically and relies on [SkyWalking Cross Process Propagation Headers Protocol](https://skywalking.apache.org/docs/main/next/en/api/x-process-propagation-headers-v3/) . Examples[​](https://docs.api7.ai/hub/skywalking-logger/#examples "Direct link to Examples") -------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `skywalking-logger` plugin for different scenarios. To follow along the example, start a storage, OAP and Booster UI with Docker Compose, following [Skywalking's documentation](https://skywalking.apache.org/docs/main/next/en/setup/backend/backend-docker/) . Once set up, the OAP server should be listening on `12800` and you should be able to access the UI at [http://localhost:8080](http://localhost:8080/) . ### Log Requests in Default Log Format[​](https://docs.api7.ai/hub/skywalking-logger/#log-requests-in-default-log-format "Direct link to Log Requests in Default Log Format") The following example demonstrates how you can configure the `skywalking-logger` plugin on a route to log information of requests hitting the route. Create a route with the `skywalking-logger` plugin and configure the plugin with your OAP server URI: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "skywalking-logger-route", "uri": "/anything", "plugins": { "skywalking-logger": { "endpoint_addr": "http://192.168.2.103:12800" } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. In [Skywalking UI](http://localhost:8080/) , navigate to **General Service** > **Services**. You should see a service called `APISIX` with a log entry corresponding to your request: { "upstream_latency": 674, "request": { "method": "GET", "headers": { "user-agent": "curl/8.6.0", "host": "127.0.0.1:9080", "accept": "*/*" }, "url": "http://127.0.0.1:9080/anything", "size": 85, "querystring": {}, "uri": "/anything" }, "client_ip": "192.168.65.1", "route_id": "skywalking-logger-route", "start_time": 1736945107345, "upstream": "3.210.94.60:80", "server": { "version": "3.13.0", "hostname": "7edbcebe8eb3" }, "service_id": "", "response": { "size": 619, "status": 200, "headers": { "content-type": "application/json", "date": "Thu, 16 Jan 2025 12:45:08 GMT", "server": "APISIX/3.13.0", "access-control-allow-origin": "*", "connection": "close", "access-control-allow-credentials": "true", "content-length": "391" } }, "latency": 764.9998664856, "apisix_latency": 90.999866485596} ### Log Request and Response Headers With Plugin Metadata[​](https://docs.api7.ai/hub/skywalking-logger/#log-request-and-response-headers-with-plugin-metadata "Direct link to Log Request and Response Headers With Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) and [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) to log specific headers from request and response. In APISIX, [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) is used to configure the common metadata fields of all plugin instances of the same plugin. It is useful when a plugin is enabled across multiple resources and requires a universal update to their metadata fields. First, create a route with the `skywalking-logger` plugin and configure the plugin with your OAP server URI: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "skywalking-logger-route", "uri": "/anything", "plugins": { "skywalking-logger": { "endpoint_addr": "http://192.168.2.103:12800" } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Next, configure the plugin metadata for `skywalking-logger`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/skywalking-logger" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr", "env": "$http_env", "resp_content_type": "$sent_http_Content_Type" } }' ❶ log the custom request header `env`. ❷ log the response header `Content-Type`. Send a request to the route with the `env` header: curl -i "http://127.0.0.1:9080/anything" -H "env: dev" You should receive an `HTTP/1.1 200 OK` response. In [Skywalking UI](http://localhost:8080/) , navigate to **General Service** > **Services**. You should see a service called `APISIX` with a log entry corresponding to your request: [ { "route_id": "skywalking-logger-route", "client_ip": "192.168.65.1", "@timestamp": "2025-01-16T12:51:53+00:00", "host": "127.0.0.1", "env": "dev", "resp_content_type": "application/json" }] ### Log Request Bodies Conditionally[​](https://docs.api7.ai/hub/skywalking-logger/#log-request-bodies-conditionally "Direct link to Log Request Bodies Conditionally") The following example demonstrates how you can conditionally log request body. Create a route with the `skywalking-logger` plugin as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "skywalking-logger-route", "uri": "/anything", "plugins": { "skywalking-logger": { "endpoint_addr": "http://192.168.2.103:12800", "include_req_body": true, "include_req_body_expr": [["arg_log_body", "==", "yes"]] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `include_req_body`: set to true to include request body. ❷ `include_req_body_expr`: only include request body if the URL query string `log_body` is `yes`. Send a request to the route with a URL query string satisfying the condition: curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST -d '{"env": "dev"}' You should receive an `HTTP/1.1 200 OK` response. In [Skywalking UI](http://localhost:8080/) , navigate to **General Service** > **Services**. You should see a service called `APISIX` with a log entry corresponding to your request, with the request body logged: [ { "request": { "url": "http://127.0.0.1:9080/anything?log_body=yes", "querystring": { "log_body": "yes" }, "uri": "/anything?log_body=yes", ..., "body": "{\"env\": \"dev\"}", }, ... }] Send a request to the route without any URL query string: curl -i "http://127.0.0.1:9080/anything" -X POST -d '{"env": "dev"}' You should not observe a log entry without the request body. info If you have customized the `log_format` in addition to setting `include_req_body` or `include_resp_body` to `true`, the plugin would not include the bodies in the logs. As a workaround, you may be able to use the NGINX variable `$request_body` in the log format, such as: { "skywalking-logger": { ..., "log_format": {"body": "$request_body"} }} ### Associate Traces with Logs[​](https://docs.api7.ai/hub/skywalking-logger/#associate-traces-with-logs "Direct link to Associate Traces with Logs") The following example demonstrates how you can configure the `skywalking-logger` plugin on a route to log information of requests hitting the route. Create a route with the `skywalking-logger` plugin and configure the plugin with your OAP server URI: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "skywalking-logger-route", "uri": "/anything", "plugins": { "skywalking": { "sample_ratio": 1 }, "skywalking-logger": { "endpoint_addr": "http://192.168.2.103:12800" } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Generate a few requests to the route: curl -i "http://127.0.0.1:9080/anything" You should receive `HTTP/1.1 200 OK` responses. In [Skywalking UI](http://localhost:8080/) , navigate to **General Service** > **Services**. You should see a service called `APISIX` with a trace corresponding to your request, where you can view the associated logs: ![trace context](https://static.api7.ai/uploads/2025/01/16/soUpXm6b_trace-view-logs.png) ![associated log](https://static.api7.ai/uploads/2025/01/16/XD934LvU_associated-logs.png) * [Examples](https://docs.api7.ai/hub/skywalking-logger/#examples) * [Log Requests in Default Log Format](https://docs.api7.ai/hub/skywalking-logger/#log-requests-in-default-log-format) * [Log Request and Response Headers With Plugin Metadata](https://docs.api7.ai/hub/skywalking-logger/#log-request-and-response-headers-with-plugin-metadata) * [Log Request Bodies Conditionally](https://docs.api7.ai/hub/skywalking-logger/#log-request-bodies-conditionally) * [Associate Traces with Logs](https://docs.api7.ai/hub/skywalking-logger/#associate-traces-with-logs) --- # Google Cloud Logging | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/google-cloud-logging/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `google-cloud-logging` plugin pushes request and response logs in batches to [Google Cloud Logging Service](https://cloud.google.com/logging?hl=en) and supports the customization of log formats. Examples[​](https://docs.api7.ai/hub/google-cloud-logging/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `google-cloud-logging` plugin for different scenarios. To follow along with the examples, you should have a GCP account with active billing. You should also first obtain authentication credentials in GCP by completing the following steps: * Visit **IAM & Admin** to create a service account. * Assign the service account with the **Logs Writer** role, which assigns the account with `logging.logEntries.create` and `logging.logEntries.route` permissions. * Create a private key for the service account and download the credentials in JSON format. The credentials JSON file content should look similar to the following: { "type": "service_account", "project_id": "api7ai-docs", "private_key_id": "6330a8c37b15a26d3fb4e9e3986f04c004826d1a", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDYTl1QKxgClpgq\n1FyZNKZTq4os9AoXU+h/1gdngtc681xqMIWlwycrJ7Bo69L//7REyUKnuIOPgHU6\nPCp4rGFokxdXzBJC0+WsxwZ/FZoaqLAD5Fbs4BpZ9q2F8fKz07l9Da+Ul2lLlQq6\nEgij2NOh9ytBvFiYEAnMY5DDWFyoXWBB0OXfGEE6486+DcfG8gMWQ7rXKVbKNyA1\nJdbS63cDJNERLb6z8QsaZOqYZwaqIn6apEv9aadnNEU+4HrXrjxsoDtk7zLmsbtp\nUOpYVVSiYz2uYbUz3XRJjW+NAeyeVBK8tePbe1n5WHM4Sg1Mp1wYtaJknS5gmOXe\nxglMt4vTAgMBAAECggEAHzGZ6mRJ56GmcH1vRywyalw8JoR2ahZ7L+hX6VkTR0ND\nn2VqTf/pR6Nxy4fAG5QEKsFS1VOE1tk3I/6mP1XYtwHeEBbJcWK+kLP5CghoULzl\nTq0LeMikHu+uY6w8OUlVTS/UQtC+SxwVMbstlEGyhWERxjdu0VwL\nY/jb6DA123cqjHteEwOFuipG+GELKJGIjgNhzyRimowOsY6F+3WrDHZrf2sM7AlD\nLbjrA3MdvIe6rNC8zy7zf/didygjryrJpjiHkKsLIPIPbu0l5xENHd3TNWuVAg48\nhf4nRwyZ7q1RXgRYnp/SfPH1YB0p4+7D0xLQUd2OEQKBgQDxvOED6IQ3zxipW+uX\nX4c+6QxwnOCTY/oQOtCwmgPSvzIMSyoNCH0YY3sdoUmygSP0hmBFIaP\nBH6A5d3A06iMTUiAwEOp5JDQImqVTN+Sz/JBBOxCpjuW/dmG72MFlZBL161lY0g6\n79ku2xatxvncdJvcpEWqB4UBEQKBgQDlEV/Tapm950M+PYTtYHry1AYxGum+Eb2+\nNg9u5kWbgl6aWSgR/XsKQPTcsYX0gFSkrYhFrVwdruDeG9JYSCckH6FtCoa8yv5s\nMB+QR7VWJoa3ej7Hc0O6VUjwUfUkXuQRoFCEl8lFCZzugsjSw93xTeo6w3s9oaCB\neY9RXGn+owKBgQCMU/Tba/K04weR6MZOTSoZnveVt7u2U+cp3LqgigeGI29OK6Px\nhOf5bGZfwO0jLlJAVJin5tdtgK1FfUDPbPByqv2bnkLNj19zPikJSqG18QSmPsXa\nV9RtYgo0doNJF3tbFUQKTdRB8qW5oXSgofMVfCEiJ8uL6jVAVCwMk+jlwQKBgQCD\ntE6lbwhAcORvt81i8nMehRueRjwYpXi0Eb8j41AoTnf4RMTOOzDwP1LKRWOgpdyE\n5qWQclGhW3g9HD//tFSU537YBBJeIFTSfYTYXvJ7OyGAAtBvuu05CGosiuLo64o0\nPDmvUtpNUG6jkBzJWgaVBFhlOxnz4Kc5alwlyn3DAwKBgQCwNJsqb4pOjwjaJl/m\nePXpeX7YdVyFnBDbSQ1BFxDYGU12yTKRYqQVIB+VIIGN28acta1EPI8tF2ODG5az\nCBmgH5amLRHHCDYRKwrP+BTA39lK0pQEUP47RSzOdY82KQB13BW1uEZTcifjS9HN\niZPoV+OYHG5iJiiWEQi9/Q1AfQ==\n-----END PRIVATE KEY-----\n", "client_email": "api7-docs-log@api7ai-docs.iam.gserviceaccount.com", "client_id": "100920913890704420895", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/api7-docs-log%40api7ai-docs.iam.gserviceaccount.com", "universe_domain": "googleapis.com"} ### Configure Authentication Using `auth_config`[​](https://docs.api7.ai/hub/google-cloud-logging/#configure-authentication-using-auth_config "Direct link to configure-authentication-using-auth_config") The following example demonstrates how you can configure the `google-cloud-logging` plugin on a route, which logs client requests and responses, as well as pushing logs to Google Cloud Logging. You will be using the `auth_config` option to configure GCP authentication details. Create a route with `google-cloud-logging` as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "google-cloud-logging-route", "uri": "/anything", "plugins": { "google-cloud-logging": { "auth_config": { "client_email": "api7-docs-logging@api7ai-docs.iam.gserviceaccount.com", "project_id": "api7ai-docs", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDYTl1QKxgClpgq\n1FyZNKZTq4os9AoXU+h/1gdngtc681xqMIWlwycrJ7Bo69L//7REyUKnuIOPgHU6\nPCp4rGFokxdXzBJC0+WsxwZ/FZoaqLAD5Fbs4BpZ9q2F8fKz07l9Da+Ul2lLlQq6\nEgij2NOh9ytBvFiYEAnMY5DDWFyoXWBB0OXfGEE6486+DcfG8gMWQ7rXKVbKNyA1\nJdbS63cDJNERLb6z8QsaZOqYZwaqIn6apEv9aadnNEU+4HrXrjxsoDtk7zLmsbtp\nUOpYVVSiYz2uYbUz3XRJjW+NAeyeVBK8tePbe1n5WHM4SnS5gmOXe\nxglMt4vTAgMBAAECggEAHzGZ6mRJ56GmcH1vRywyalw8JoR2ahZ7L+hX6VkTR0ND\nn2VqTf/pR6Nxy4fAG5QEKsFS1VOE1tk3I/6mP1XYtwHeEBbJcWK+kLP5CghoULzl\nTq0LeMikHuI19FxH3HVwSV+uY6w8OUlVTS/UQtC+SxwVMbstlEGyhWERxjdu0VwL\nY/jb6DA123cqjHteEwOFuipG+GELKJGIjgNhzyRimowOsY6F+3WrDHZrf2sM7AlD\nLbjrA3MdvIe6rNC8zy7zf/didygjryrJpjiHkKsLIPIPbu0l5xENHd3TNWuVAg48\nhf4nRwyZ7q1RXgRYnp/SfPH1YB0p4+7D0xLQUd2xvOED6IQ3zxipW+uX\nX4c+6QxwnOCTY/oQOtCwmgPSvzIMSyoNCH0YY3sdoUmygS40v30OV8vP0hmBFIaP\nBH6A5d3A06iMTUiAwEOp5JDQImqVTN+Sz/JBBOxCpjuW/dmG72MFlZBL161lY0g6\n79ku2xatxvncdJvcpEWqB4UBEQKBgQDlEV/Tapm950M+PYTtYHry1AYxGum+Eb2+\nNg9u5kWbgl6aWSgR/XsKQPTcsYX0gFSkrYhFrVwdruDeG9JYSCckH6FtCoa8yv5s\nMB+QR7VWJoa3ej7Hc0O6VUjwUfUkXuQRoFCEl8lFCZzugsjSw93xTeo6w3s9oaCB\neY9RXGn+owKBgQCMU/Tba/K04weR6MZOTSoZnveVt7u2U+cp3LqgigeGI29OK6Px\nhOf5bGZfwO0jLlJAVJin5tdtgK1FfUDPbPByqv2bnkLNj19zPikJSqG18QSmPsXa\nV9RtYgo0doNJF3tbFUQKTdRB8qW5oXSgofMVfCEiJ8uL6jVAVCwMk+jlwQKBgQCD\ntE6lbwhAcORvt81i8nMehRueRjwYpXi0Eb8j41AoTnf4RMTOOzDwP1LKRWOgpdyE\n5qWQclGhW3g9HD//tFSU537YBBJeIFTSfYTYXvJ7OyGAAtBvuu05CGosiuLo64o0\nPDmvUtpNUG6jkBzJWgaVBFhlOxnz4Kc5alwlyn3DAwKBgQCwNJsqb4pOjwjaJl/m\nePXpeX7YdVyFnBDbSQ1BFxDYGU12yTKRYqQVIB+VIIGN28acta1EPI8tF2ODG5az\nCBmgH5amLRHHCDYRKwrP+BTA39lK0pQEUP47RSzOdY82KQB13BW1uEZTcifjS9HN\niZPoV+OYHG5iJiiWEQi9/Q1AfQ==\n-----END PRIVATE KEY-----\n", "token_uri": "https://oauth2.googleapis.com/token" } } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ Replace with your service account. ❷ Replace with your project ID. ❸ Replace with your private key. ❹ Replace with your token URI. Send a request to the route to generate a log entry: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Navigate to Google Cloud Logs Explorer, you should see a log entry corresponding to your request, similar to the following: { "insertId": "5400340ea330b35f2d557da2cbb9e88d", "jsonPayload": { "service_id": "", "route_id": "google-cloud-logging-route" }, "httpRequest": { "requestMethod": "GET", "requestUrl": "http://127.0.0.1:9080/anything", "requestSize": "85", "status": 200, "responseSize": "615", "userAgent": "curl/8.6.0", "remoteIp": "192.168.107.1", "serverIp": "54.86.137.185:80", "latency": "1.083s" }, "resource": { "type": "global", "labels": { "project_id": "api7ai-docs" } }, "timestamp": "2025-02-07T07:39:51.859Z", "labels": { "source": "apache-apisix-google-cloud-logging" }, "logName": "projects/api7ai-docs/logs/apisix.apache.org%2Flogs", "receiveTimestamp": "2025-02-07T07:39:58.012811475Z"} ### Configure Authentication Using `auth_file`[​](https://docs.api7.ai/hub/google-cloud-logging/#configure-authentication-using-auth_file "Direct link to configure-authentication-using-auth_file") The following example demonstrates how you can configure the `google-cloud-logging` plugin on a route, which logs client requests and responses, as well as pushing logs to Google Cloud Logging. You will be using the `auth_file` option to configure GCP authentication details. Copy the previously downloaded GCP service account credentials JSON file to a location accessible for APISIX. If you are running APISIX in Docker, you should copy the file into the container, for instance, to `/usr/local/apisix/conf/gcp-logging-auth.json`. Create a route with `google-cloud-logging` as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "google-cloud-logging-route", "uri": "/anything", "plugins": { "google-cloud-logging": { "auth_file": "/usr/local/apisix/conf/gcp-logging-auth.json" } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ Replace with your GCP service account credentials JSON file path. Send a request to the route to generate a log entry: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Navigate to Google Cloud Logs Explorer, you should see a log entry corresponding to your request, similar to the following: { "insertId": "5400340ea330b35f2d557da2cbb9e88d", "jsonPayload": { "service_id": "", "route_id": "google-cloud-logging-route" }, "httpRequest": { "requestMethod": "GET", "requestUrl": "http://127.0.0.1:9080/anything", "requestSize": "85", "status": 200, "responseSize": "615", "userAgent": "curl/8.6.0", "remoteIp": "192.168.107.1", "serverIp": "54.86.137.185:80", "latency": "1.083s" }, "resource": { "type": "global", "labels": { "project_id": "api7ai-docs" } }, "timestamp": "2025-02-07T08:25:11.325Z", "labels": { "source": "apache-apisix-google-cloud-logging" }, "logName": "projects/api7ai-docs/logs/apisix.apache.org%2Flogs", "receiveTimestamp": "2025-02-07T08:25:11.423190575Z"} ### Customize Log Format With Plugin Metadata[​](https://docs.api7.ai/hub/google-cloud-logging/#customize-log-format-with-plugin-metadata "Direct link to Customize Log Format With Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) and [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) to log specific headers from request and response. In APISIX, [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) is used to configure the common metadata fields of all plugin instances of the same plugin. It is useful when a plugin is enabled across multiple resources and requires a universal update to their metadata fields. First, create a route with `google-cloud-logging` as such, and replace with your credentials: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "google-cloud-logging-route", "uri": "/anything", "plugins": { "google-cloud-logging": { "auth_config": { "client_email": "api7-docs-logging@api7ai-docs.iam.gserviceaccount.com", "project_id": "api7ai-docs", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDYTl1QKxgClpgq\n1FyZNKZTq4os9AoXU+h/1gdngtc681xqMIWlwycrJ7Bo69L//7REyUKnuIOPgHU6\nPCp4rGFokxdXzBJC0+WsxwZ/FZoaqLAD5Fbs4BpZ9q2F8fKz07l9Da+Ul2lLlQq6\nEgij2NOh9ytBvFiYEAnMY5DDWFyoXWBB0OXfGEE6486+DcfG8gMWQ7rXKVbKNyA1\nJdbS63cDJNERLb6z8QsaZOqYZwaqIn6apEv9aadnNEU+4HrXrjxsoDtk7zLmsbtp\nUOpYVVSiYz2uYbUz3XRJjW+NAeyeVBK8tePbe1n5WHM4SnS5gmOXe\nxglMt4vTAgMBAAECggEAHzGZ6mRJ56GmcH1vRywyalw8JoR2ahZ7L+hX6VkTR0ND\nn2VqTf/pR6Nxy4fAG5QEKsFS1VOE1tk3I/6mP1XYtwHeEBbJcWK+kLP5CghoULzl\nTq0LeMikHuI19FxH3HVwSV+uY6w8OUlVTS/UQtC+SxwVMbstlEGyhWERxjdu0VwL\nY/jb6DA123cqjHteEwOFuipG+GELKJGIjgNhzyRimowOsY6F+3WrDHZrf2sM7AlD\nLbjrA3MdvIe6rNC8zy7zf/didygjryrJpjiHkKsLIPIPbu0l5xENHd3TNWuVAg48\nhf4nRwyZ7q1RXgRYnp/SfPH1YB0p4+7D0xLQUd2xvOED6IQ3zxipW+uX\nX4c+6QxwnOCTY/oQOtCwmgPSvzIMSyoNCH0YY3sdoUmygS40v30OV8vP0hmBFIaP\nBH6A5d3A06iMTUiAwEOp5JDQImqVTN+Sz/JBBOxCpjuW/dmG72MFlZBL161lY0g6\n79ku2xatxvncdJvcpEWqB4UBEQKBgQDlEV/Tapm950M+PYTtYHry1AYxGum+Eb2+\nNg9u5kWbgl6aWSgR/XsKQPTcsYX0gFSkrYhFrVwdruDeG9JYSCckH6FtCoa8yv5s\nMB+QR7VWJoa3ej7Hc0O6VUjwUfUkXuQRoFCEl8lFCZzugsjSw93xTeo6w3s9oaCB\neY9RXGn+owKBgQCMU/Tba/K04weR6MZOTSoZnveVt7u2U+cp3LqgigeGI29OK6Px\nhOf5bGZfwO0jLlJAVJin5tdtgK1FfUDPbPByqv2bnkLNj19zPikJSqG18QSmPsXa\nV9RtYgo0doNJF3tbFUQKTdRB8qW5oXSgofMVfCEiJ8uL6jVAVCwMk+jlwQKBgQCD\ntE6lbwhAcORvt81i8nMehRueRjwYpXi0Eb8j41AoTnf4RMTOOzDwP1LKRWOgpdyE\n5qWQclGhW3g9HD//tFSU537YBBJeIFTSfYTYXvJ7OyGAAtBvuu05CGosiuLo64o0\nPDmvUtpNUG6jkBzJWgaVBFhlOxnz4Kc5alwlyn3DAwKBgQCwNJsqb4pOjwjaJl/m\nePXpeX7YdVyFnBDbSQ1BFxDYGU12yTKRYqQVIB+VIIGN28acta1EPI8tF2ODG5az\nCBmgH5amLRHHCDYRKwrP+BTA39lK0pQEUP47RSzOdY82KQB13BW1uEZTcifjS9HN\niZPoV+OYHG5iJiiWEQi9/Q1AfQ==\n-----END PRIVATE KEY-----\n", "token_uri": "https://oauth2.googleapis.com/token" } } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Next, configure the plugin metadata for `google-cloud-logging`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/google-cloud-logging" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr", } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Navigate to Google Cloud Logs Explorer, you should see a log entry corresponding to your request, similar to the following: { "@timestamp":"2025-02-07T09:10:42+00:00", "client_ip":"192.168.107.1", "host":"127.0.0.1", "route_id":"google-cloud-logging-route"} The log format configured in plugin metadata is effective for all instances of `google-cloud-logging` if the log format is not specifically specified on the individual instance. If you specifically configure the log format in the `google-cloud-logging` plugin on the route: curl "http://127.0.0.1:9180/apisix/admin/routes/google-cloud-logging-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "google-cloud-logging": { "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr", "env": "$http_env", "resp_content_type": "$sent_http_Content_Type" } } } }' ❶ log the custom request header `env`. ❷ log the response header `Content-Type`. Send a request to the route with the `env` header: curl -i "http://127.0.0.1:9080/anything" -H "env: dev" You should receive an `HTTP/1.1 200 OK` response. Navigate to Google Cloud Logs Explorer, you should see a log entry corresponding to your request, similar to the following: { "@timestamp":"2025-02-07T09:38:55+00:00", "client_ip":"192.168.107.1", "host":"127.0.0.1", "env":"dev", "resp_content_type":"application/json", "route_id":"google-cloud-logging-route"} The configuration of log format on the route has taken precedence over the log format configured on the `google-cloud-logging` plugin metadata. * [Examples](https://docs.api7.ai/hub/google-cloud-logging/#examples) * [Configure Authentication Using `auth_config`](https://docs.api7.ai/hub/google-cloud-logging/#configure-authentication-using-auth_config) * [Configure Authentication Using `auth_file`](https://docs.api7.ai/hub/google-cloud-logging/#configure-authentication-using-auth_file) * [Customize Log Format With Plugin Metadata](https://docs.api7.ai/hub/google-cloud-logging/#customize-log-format-with-plugin-metadata) --- # Build Your Own Docker Images | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/install/docker/build-custom-images/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page You can build your own Docker images and customize them for your needs. Prerequisite(s)[​](https://docs.api7.ai/apisix/install/docker/build-custom-images/#prerequisites "Direct link to Prerequisite(s)") ----------------------------------------------------------------------------------------------------------------------------------- * Install [make](https://www.gnu.org/software/make) to build your Docker image from the makefile. Clone Repository[​](https://docs.api7.ai/apisix/install/docker/build-custom-images/#clone-repository "Direct link to Clone Repository") ---------------------------------------------------------------------------------------------------------------------------------------- Clone the repository and navigate into the repository: git clone https://github.com/apache/apisix-docker.gitcd apisix-docker Build a Docker Image[​](https://docs.api7.ai/apisix/install/docker/build-custom-images/#build-a-docker-image "Direct link to Build a Docker Image") ---------------------------------------------------------------------------------------------------------------------------------------------------- Build a Docker image from a release: APISIX_VERSION=3.15.0 # specify a release versionDISTRO=ubuntu # ubuntu, debian, centos, or redhatmake build-on-$DISTRO The make command builds a Docker image with this [Dockerfile](https://github.com/apache/apisix-docker/blob/master/ubuntu/Dockerfile) . You can further customize the relevant files for your needs. Check Docker Image[​](https://docs.api7.ai/apisix/install/docker/build-custom-images/#check-docker-image "Direct link to Check Docker Image") ---------------------------------------------------------------------------------------------------------------------------------------------- List all Docker images: docker images You should see the list including the image built in the last step: REPOSITORY TAG IMAGE ID CREATED SIZEapache/apisix 3.15.0-ubuntu 422e111797f3 About a minute ago 337MB * [Prerequisite(s)](https://docs.api7.ai/apisix/install/docker/build-custom-images/#prerequisites) * [Clone Repository](https://docs.api7.ai/apisix/install/docker/build-custom-images/#clone-repository) * [Build a Docker Image](https://docs.api7.ai/apisix/install/docker/build-custom-images/#build-a-docker-image) * [Check Docker Image](https://docs.api7.ai/apisix/install/docker/build-custom-images/#check-docker-image) --- # Plugin Global Rules | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/plugin-global-rules/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of plugin global rules in APISIX and why you may need them. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/plugin-global-rules/#overview "Direct link to Overview") -------------------------------------------------------------------------------------------------------------- In APISIX, a _global rule_ object is used to create [plugins](https://docs.api7.ai/apisix/key-concepts/plugins) that are triggered on every incoming request and executed before other plugins locally bound to objects, such as [routes](https://docs.api7.ai/apisix/key-concepts/routes) , [services](https://docs.api7.ai/apisix/key-concepts/services) , [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) , [consumer groups](https://docs.api7.ai/apisix/key-concepts/consumer-groups) , or [plugin configs](https://docs.api7.ai/apisix/key-concepts/plugin-configs) . Certain plugins, such as rate limiting and observability plugins, are frequently enabled globally in order to provide consistent and comprehensive protection for APIs. The following diagram illustrates an example of enabling [`key-auth`](https://docs.api7.ai/hub/key-auth) plugin globally for all incoming requests. Once a request is authenticated, the route uses the [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite) plugin to set an additional request header, which demonstrates the [plugins execution order](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-order) : ![key-auth global rule diagram](https://static.api7.ai/uploads/2024/09/12/oP3EEnCP_globalrule_cred.svg) This configuration ensures that only the authenticated requests are allowed to interact with the upstream service. If a request is sent to APISIX: * without any key or with a wrong key, the request is rejected. * with `global-key` but to a non-existent route, the request is authenticated but APISIX returns an error warning users that the route is not found. * with `global-key` to an existing route, the request is first authenticated, then the header of the request is modified by the plugin on the route, and finally the request is forwarded to the upstream service. The example above used two different plugins in a global rule and a route. If the same plugin is configured in both objects, both instances of the plugin will be [executed sequentially](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-order) , rather than overwriting each other. Additional Resources[​](https://docs.api7.ai/apisix/key-concepts/plugin-global-rules/#additional-resources "Direct link to Additional Resources") -------------------------------------------------------------------------------------------------------------------------------------------------- * Key Concepts * [Plugins](https://docs.api7.ai/apisix/key-concepts/plugins) * [Consumers](https://docs.api7.ai/apisix/key-concepts/consumers) * [Credentials](https://docs.api7.ai/apisix/key-concepts/credentials) * [Plugin Hub](https://docs.api7.ai/hub) * Admin API - [Plugin Global Rules](https://docs.api7.ai/apisix/reference/admin-api#tag/Global-Rule) * [Overview](https://docs.api7.ai/apisix/key-concepts/plugin-global-rules/#overview) * [Additional Resources](https://docs.api7.ai/apisix/key-concepts/plugin-global-rules/#additional-resources) --- # Plugin Metadata | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/plugin-metadata/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of plugin metadata in APISIX and why you may need them. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/plugin-metadata/#overview "Direct link to Overview") ---------------------------------------------------------------------------------------------------------- In APISIX, a _plugin metadata_ object is used to configure the common metadata field(s) of all plugin instances sharing the same plugin name. It is useful when a plugin is enabled across multiple objects and requires a universal update to their metadata fields. The following diagram illustrates the concept of plugin metadata using two instances of [`syslog`](https://docs.api7.ai/hub/syslog) plugins on two different routes, as well as a plugin metadata object setting a global `log_format` for the `syslog` plugin: ![Plugin Metadata diagram with two routes and one plugin metadata](https://static.api7.ai/uploads/2024/12/12/Jsj70TOD_plugin_metadata.svg) Unless otherwise specified, the `log_format` on plugin metadata object should apply the same log format uniformly to both `syslog` plugins. However, since the `syslog` plugin on the `/order` route has a different `log_format`, requests visiting this route will generate logs in the `log_format` specified by the plugin on the route. In general, if a field of a plugin is defined in both the plugin metadata and another object, such as a route, the definition on the other object **takes precedence** over the global definition in plugin metadata to provide a more granular level of control. Plugin metadata objects should only be used for plugins that have metadata fields. For more details on which plugins have metadata fields, please refer to the plugin reference guide (coming soon). Additional Resource(s)[​](https://docs.api7.ai/apisix/key-concepts/plugin-metadata/#additional-resources "Direct link to Additional Resource(s)") -------------------------------------------------------------------------------------------------------------------------------------------------- * Key Concepts - [Plugins](https://docs.api7.ai/apisix/key-concepts/plugins) * Control API - [Plugin Metadata](https://docs.api7.ai/apisix/reference/control-api#tag/Plugin-Metadata) * Admin API - [Plugin Metadata](https://docs.api7.ai/apisix/reference/admin-api#tag/Plugin-Metadata) * [Plugin Hub](https://docs.api7.ai/hub) * [Overview](https://docs.api7.ai/apisix/key-concepts/plugin-metadata/#overview) * [Additional Resource(s)](https://docs.api7.ai/apisix/key-concepts/plugin-metadata/#additional-resources) --- # Log with Elasticsearch | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page [Elasticsearch](https://www.elastic.co/elasticsearch) is a popular JSON-based datastore for storing and indexing large volumes of data. It is often used to store logs from various sources and works with tools like [Logstash](https://www.elastic.co/logstash) and [Kibana](https://www.elastic.co/kibana) to form an entire observability stack known as the Elastic (ELK) Stack. APISIX supports forwarding its logs directly to Elasticsearch through the [`elasticsearch-logger`](https://docs.api7.ai/hub/elasticsearch-logger) plugin. These logs can then be searched, filtered, and visualized through Kibana to gather insights to manage applications. This guide will show you how to enable the [`elasticsearch-logger`](https://docs.api7.ai/hub/elasticsearch-logger) plugin to integrate APISIX with the ELK stack for observability. Prerequisite(s)[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#prerequisites "Direct link to Prerequisite(s)") -------------------------------------------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker/) . * Install [cURL](https://curl.se/) to send requests to the services for validation. * Follow the [Getting Started tutorial](https://docs.api7.ai/apisix/getting-started/) to start a new APISIX instance in Docker or on Kubernetes. Start Elasticsearch and Kibana[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#start-elasticsearch-and-kibana "Direct link to Start Elasticsearch and Kibana") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- info APISIX currently supports Elasticsearch versions up to and including 7.x. This guide uses version 7.17.1 for both Elasticsearch and Kibana. Start an Elasticsearch instance in Docker: docker run -d \ --name elasticsearch \ --network apisix-quickstart-net \ -v elasticsearch_vol:/usr/share/elasticsearch/data/ \ -p 9200:9200 \ -p 9300:9300 \ -e ES_JAVA_OPTS="-Xms512m -Xmx512m" \ -e discovery.type=single-node \ -e xpack.security.enabled=false \ docker.elastic.co/elasticsearch/elasticsearch:7.17.1 Start a Kibana instance in Docker to visualize the indexed data in Elasticsearch: docker run -d \ --name kibana \ --network apisix-quickstart-net \ -p 5601:5601 \ -e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" \ docker.elastic.co/kibana/kibana:7.17.1 If successful, you should see the Kibana web dashboard on [localhost:5601](http://localhost:5601/) . Enable `elasticsearch-logger` Plugin[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#enable-elasticsearch-logger-plugin "Direct link to enable-elasticsearch-logger-plugin") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Enable `elasticsearch-logger` globally and create a sample route to generate logs. Alternatively, you can enable the plugin on a route. * Admin API * ADC Enable the `elasticsearch-logger` plugin on all routes: curl "http://127.0.0.1:9180/apisix/admin/global_rules/" -X PUT -d '{ "id": "elasticsearch", "plugins": { "elasticsearch-logger": { "endpoint_addrs": ["http://elasticsearch:9200"], "field": { "index": "gateway", "type": "logs" }, "ssl_verify": false, "timeout": 60, "retry_delay": 1, "buffer_duration": 60, "max_retry_count": 0, "batch_max_size": 5, "inactive_timeout": 5 } }}' Create a sample route on which you will collect logs: curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "getting-started-ip", "uri": "/ip", "upstream": { "nodes": { "httpbin.org:80":1 }, "type": "roundrobin" }}' Enable the `elasticsearch-logger` plugin globally and create a sample route on which you will collect logs: adc-elasticsearch.yaml global_rules: elasticsearch-logger: endpoint_addr: "http://elasticsearch:9200" field: index: "gateway" type: "logs" ssl_verify: false timeout: 60 retry_delay: 1 buffer_duration: 60 max_retry_count: 0 batch_max_size: 5 inactive_timeout: 5services: - name: httpbin Service routes: - uris: - /ip name: getting-started-ip upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configurations to APISIX: adc sync -f adc-elasticsearch.yaml Customize Log Format[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#customize-log-format "Direct link to Customize Log Format") ------------------------------------------------------------------------------------------------------------------------------------------------------------------- As an optional step, you can customize the log format for `elasticsearch-logger`. The log format of most APISIX logging plugins could be customized locally on the plugin (e.g. bound to a route) and/or globally with [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) . Add host address, timestamp, and client IP address to the logs with [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) : * Admin API * ADC curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger" -X PUT -d '{ "log_format":{ "host":"$host", "timestamp":"$time_iso8601", "client_ip":"$remote_addr" }}' adc-plugin-metadata.yaml plugin_metadata: elasticsearch-logger: log_format: host: $host client_ip: $remote_addr timestamp: $time_iso8601 Synchronize the configurations to APISIX: adc sync -f adc-plugin-metadata -f adc-elasticsearch.yaml Configure Kibana[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#configure-kibana "Direct link to Configure Kibana") ------------------------------------------------------------------------------------------------------------------------------------------------------- Send some requests to the route to generate access log entries: for i in {1..10}; do curl -i "http://127.0.0.1:9080/ip"done Open Kibana dashboard on [localhost:5601](http://localhost:5601/) and click the **Discover** tab from the menu. Create a new index pattern to fetch the data from Elasticsearch: ![Create index pattern](https://static.api7.ai/uploads/2023/08/29/JZCfY7DN_create-index-pattern.png) Create a pattern `gateway` to match the indexed data in Elasticsearch: ![Create index pattern for the gateway index](https://static.api7.ai/uploads/2023/08/29/07Tk0naZ_create-index-pattern-2.png) If your configuration is correct, you can go back to the **Discover** tab and view the logs from APISIX: ![Search through APISIX logs in Kibana](https://static.api7.ai/uploads/2023/08/29/gutumrzr_discover.png) Next Steps[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#next-steps "Direct link to Next Steps") ------------------------------------------------------------------------------------------------------------------------------------- See [`elasticsearch-logger`](https://docs.api7.ai/hub/elasticsearch-logger) plugin reference to learn more about the plugin configuration options. * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#prerequisites) * [Start Elasticsearch and Kibana](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#start-elasticsearch-and-kibana) * [Enable `elasticsearch-logger` Plugin](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#enable-elasticsearch-logger-plugin) * [Customize Log Format](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#customize-log-format) * [Configure Kibana](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#configure-kibana) * [Next Steps](https://docs.api7.ai/apisix/how-to-guide/observability/log-with-elasticsearch/#next-steps) --- # OPA | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/opa/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `opa` plugin supports the integration with [Open Policy Agent (OPA)](https://www.openpolicyagent.org/) , a unified policy engine and framework that helps defining and enforcing authorization policies. Authorization logics are defined in [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) and stored in OPA. Once configured, the OPA engine will evaluate the client request to a protected route to determine whether the request should have access to the upstream resource based on the defined policies. Examples[​](https://docs.api7.ai/hub/opa/#examples "Direct link to Examples") ------------------------------------------------------------------------------ The examples below demonstrate how you can work with the `opa` plugin for different scenarios. Before proceeding, you should have a running OPA server, or start a new one in Docker: docker run -d --name opa-server -p 8181:8181 openpolicyagent/opa:1.6.0 run --server --addr :8181 --log-level debug * `run -s` starts OPA as a server. * `--log-level debug` prints debug information to examine the data APISIX pushes to OPA. To verify that the OPA server is installed and port is exposed properly, run: curl http://127.0.0.1:8181 | grep Version You should see a response similar to the following: Version: 1.6.0 ### Implement a Basic Policy[​](https://docs.api7.ai/hub/opa/#implement-a-basic-policy "Direct link to Implement a Basic Policy") The following example implements a basic authorization policy in OPA to allow only GET requests. Create an OPA policy that only allows HTTP GET requests: curl "http://127.0.0.1:8181/v1/policies/getonly" -X PUT \ -H "Content-Type: text/plain" \ -d 'package getonlydefault allow = falseallow if { input.request.method == "GET"}' Create a route with the `opa` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "opa-route", "uri": "/anything", "plugins": { "opa": { "host": "http://192.168.2.104:8181", "policy": "getonly" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Configure the OPA server address. Replace with your IP address. ❷ Set the authorization policy to be `getonly`. To verify the policy, send a GET request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Send another request to the route using PUT: curl -i "http://127.0.0.1:9080/anything" -X PUT You should receive an `HTTP/1.1 403 Forbidden` response. ### Understand Data Format[​](https://docs.api7.ai/hub/opa/#understand-data-format "Direct link to Understand Data Format") The following example helps you understand the data and the format APISIX pushes to OPA to support authorization logic writing. The example continues with the policy and the route in the [last example](https://docs.api7.ai/hub/opa/#implement-a-basic-policy) Suppose your OPA server is started with `--log-level debug` and you have completed the verification steps in the [last example](https://docs.api7.ai/hub/opa/#implement-a-basic-policy) sending requests to the sample route. Navigate to the OPA server log. You should see an entry similar to the following: { "client_addr": "192.168.215.1:58467", "level": "info", "msg": "Received request.", "req_body": "{\"input\":{\"type\":\"http\",\"var\":{\"server_port\":\"9080\",\"timestamp\":1752400020,\"server_addr\":\"192.168.107.3\",\"remote_port\":\"58544\",\"remote_addr\":\"192.168.107.1\"},\"request\":{\"host\":\"127.0.0.1\",\"path\":\"/anything\",\"headers\":{\"host\":\"127.0.0.1:9080\",\"accept\":\"*/*\",\"user-agent\":\"curl/8.6.0\"},\"query\":{},\"port\":9080,\"scheme\":\"http\",\"method\":\"PUT\"}}}", "req_id": 12, "req_method": "POST", "req_params": {}, "req_path": "/v1/data/getonly", "time": "2025-07-14T15:07:00Z"} where the `req_body` shows the data APISIX pushed: { "input": { "type": "http", "var": { "server_port": "9080", "timestamp": 1752400020, "server_addr": "192.168.107.3", "remote_port": "58544", "remote_addr": "192.168.107.1" }, "request": { "host": "127.0.0.1", "path": "/anything", "headers": { "host": "127.0.0.1:9080", "accept": "*/*", "user-agent": "curl/8.6.0" }, "query": {}, "port": 9080, "scheme": "http", "method": "PUT" } }} Now, update the plugin on the [previously created route](https://docs.api7.ai/hub/opa/#implement-a-basic-policy) to include route information: curl "http://127.0.0.1:9180/apisix/admin/routes/opa-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "opa": { "with_route": true } } }' Send a request to the route: curl -i "http://127.0.0.1:9080/anything" In the OPA server log, you should see a new entry: { "client_addr": "192.168.215.1:43706", "level": "info", "msg": "Received request.", "req_body": "{\"input\":{\"route\":{\"id\":\"opa-route\",\"uri\":\"/anything\",\"update_time\":1752395758,\"plugins\":{\"opa\":{\"keepalive_pool\":5,\"keepalive_timeout\":60000,\"host\":\"http://172.17.1.196:8181\",\"ssl_verify\":true,\"with_route\":true,\"with_service\":false,\"with_consumer\":false,\"timeout\":3000,\"keepalive\":true,\"policy\":\"getonly\"}},\"priority\":0,\"status\":1,\"create_time\":1752393063},\"type\":\"http\",\"var\":{\"server_port\":\"9080\",\"timestamp\":1752396233,\"server_addr\":\"192.168.107.3\",\"remote_port\":\"47838\",\"remote_addr\":\"192.168.107.1\"},\"request\":{\"host\":\"127.0.0.1\",\"path\":\"/anything\",\"headers\":{\"host\":\"127.0.0.1:9080\",\"accept\":\"*/*\",\"user-agent\":\"curl/8.6.0\"},\"query\":{},\"port\":9080,\"scheme\":\"http\",\"method\":\"GET\"}}}", "req_id": 14, "req_method": "POST", "req_params": {}, "req_path": "/v1/data/getonly", "time": "2025-07-13T08:43:53Z"} The `req_body` now includes route information: { "input": { "route": { "id": "opa-route", "uri": "/anything", "update_time": 1752395758, "plugins": { "opa": { "keepalive_pool": 5, "keepalive_timeout": 60000, "host": "http://172.17.1.196:8181", "ssl_verify": true, "with_route": true, "with_service": false, "with_consumer": false, "timeout": 3000, "keepalive": true, "policy": "getonly" } }, "priority": 0, "status": 1, "create_time": 1752393063 }, "type": "http", "var": { "server_port": "9080", "timestamp": 1752396233, "server_addr": "192.168.107.3", "remote_port": "47838", "remote_addr": "192.168.107.1" }, "request": { "host": "127.0.0.1", "path": "/anything", "headers": { "host": "127.0.0.1:9080", "accept": "*/*", "user-agent": "curl/8.6.0" }, "query": {}, "port": 9080, "scheme": "http", "method": "GET" } }} ### Return Custom Response[​](https://docs.api7.ai/hub/opa/#return-custom-response "Direct link to Return Custom Response") The following example demonstrates how you can return custom response code and message when the request is unauthorized. Create an OPA policy that only allows HTTP GET requests and return `302` with a custom message the request is unauthorized: curl "127.0.0.1:8181/v1/policies/customresp" -X PUT \ -H "Content-Type: text/plain" \ -d 'package customrespdefault allow = falseallow if { input.request.method == "GET"}reason := "The resource has temporarily moved. Please follow the new URL." if { not allow}headers := { "Location": "http://example.com/auth"} if { not allow}status_code := 302 if { not allow}' Create a route with the `opa` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "opa-route", "uri": "/anything", "plugins": { "opa": { "host": "http://192.168.2.104:8181", "policy": "customresp" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Configure the OPA server address. Replace with your IP address. ❷ Set the authorization policy to be `customresp`. Send a GET request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Send a POST request to the route: curl -i "http://127.0.0.1:9080/anything" -X POST You should receive an `HTTP/1.1 302 Moved Temporarily` response: HTTP/1.1 302 Moved Temporarily...Location: http://example.com/authThe resource has temporarily moved. Please follow the new URL. ### Implement RBAC[​](https://docs.api7.ai/hub/opa/#implement-rbac "Direct link to Implement RBAC") The following example demonstrates how to implement authentication and RBAC using the [`jwt-auth`](https://docs.api7.ai/hub/jwt-auth) and `opa` plugins. You will be implementing RBAC logics such that: * An `user` role can only read the upstream resources. * An `admin` role can read and write the upstream resources. Create an OPA policy for RBAC of two example consumers, where `john` has the `user` role and `jane` has the `admin` role: curl "http://127.0.0.1:8181/v1/policies/rbac" -X PUT \ -H "Content-Type: text/plain" \ -d 'package rbac# Assign roles to usersuser_roles := { "john": ["user"], "jane": ["admin"]}# Map permissions to HTTP methodspermission_methods := { "read": "GET", "write": "POST"}# Assign role permissionsrole_permissions := { "user": ["read"], "admin": ["read", "write"]}# Get JWT authorization tokenbearer_token := t if { t := input.request.headers.authorization}# Decode the token to get role and permissiontoken := {"payload": payload} if { [_, payload, _] := io.jwt.decode(bearer_token)}# Normalize permission to a listnormalized_permissions := ps if { ps := token.payload.permission not is_string(ps)}normalized_permissions := [ps] if { ps := token.payload.permission is_string(ps)}# Implement RBAC logicdefault allow = falseallow if { # Look up the list of roles for the user roles := user_roles[input.consumer.username] # For each role in that list r := roles[_] # Look up the permissions list for the role permissions := role_permissions[r] # For each permission p := permissions[_] # Check if the permission matches the request method permission_methods[p] == input.request.method # Check if the normalized permissions include the permission p in normalized_permissions}' Create two consumers `john` and `jane` in APISIX: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT -d '{ "username": "john"}' curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT -d '{ "username": "jane"}' Configure the `jwt-auth` credentials for the consumers, using the default algorithm `HS256`: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-jwt-auth", "plugins": { "jwt-auth": { "key": "john-key", "secret": "john-hs256-secret-that-is-very-long" } } }' curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-jwt-auth", "plugins": { "jwt-auth": { "key": "jane-key", "secret": "jane-hs256-secret-that-is-very-long" } } }' Create a route and configure the `jwt-auth` and `opa` plugins as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "opa-route", "methods": ["GET", "POST"], "uris": ["/get","/post"], "plugins": { "jwt-auth": {}, "opa": { "host": "http://192.168.2.104:8181", "policy": "rbac", "with_consumer": true } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Enable the `jwt-auth` plugin on the route. ❷ Configure the OPA server address. Replace with your IP address. ❸ Set the authorization policy to be `rbac`. ❹ Set `with_consumer` to true to send cnosumer information. #### Verify as `john`[​](https://docs.api7.ai/hub/opa/#verify-as-john "Direct link to verify-as-john") To issue a JWT for `john`, you could use [JWT.io's JWT encoder](https://jwt.io/) or other utilities. If you are using [JWT.io's JWT encoder](https://jwt.io/) , do the following: * Fill in `HS256` as the algorithm. * Update the secret in the **Valid secret** section to be `john-hs256-secret-that-is-very-long`. * Update payload with role `user`, permission `read`, and consumer key `john-key`; as well as `exp` or `nbf` in UNIX timestamp. note If you are using API7 Enterprise, the requirement of `exp` or `nbf` is not mandatory. You can optionally include these claims and use the `claims_to_verify` parameter to configure which claim to verify. Your payload should look similar to the following: { "role": "user", "permission": "read", "key": "john-key", "nbf": 1729132271} Copy the generated JWT and save to a variable: export john_jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsInBlcm1pc3Npb24iOiJyZWFkIiwia2V5Ijoiam9obi1rZXkiLCJuYmYiOjE3MjkxMzIyNzF9.rAHMTQfnnGFnKYc3am_lpE9pZ9E8EaOT_NBQ5Ss8pk4 Send a GET request to the route with the JWT of `john`: curl -i "http://127.0.0.1:9080/get" -H "Authorization: ${john_jwt_token}" You should receive an `HTTP/1.1 200 OK` response. Send a POST request to the route with the same JWT: curl -i "http://127.0.0.1:9080/post" -X POST -H "Authorization: ${john_jwt_token}" You should receive an `HTTP/1.1 403 Forbidden` response. #### Verify as `jane`[​](https://docs.api7.ai/hub/opa/#verify-as-jane "Direct link to verify-as-jane") Similarly, to issue a JWT for `jane`, you could use [JWT.io's JWT encoder](https://jwt.io/) or other utilities. If you are using [JWT.io's JWT encoder](https://jwt.io/) , do the following: * Fill in `HS256` as the algorithm. * Update the secret in the **Valid secret** section to be `jane-hs256-secret-that-is-very-long`. * Update payload with role `admin`, permission `["read","write"]`, and consumer key `jane-key`; as well as `exp` or `nbf` in UNIX timestamp. note If you are using API7 Enterprise, the requirement of `exp` or `nbf` is not mandatory. You can optionally include these claims and use the `claims_to_verify` parameter to configure which claim to verify. Your payload should look similar to the following: { "role": "admin", "permission": ["read","write"], "key": "jane-key", "nbf": 1729132271} Copy the generated JWT and save to a variable: export jane_jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJwZXJtaXNzaW9uIjpbInJlYWQiLCJ3cml0ZSJdLCJrZXkiOiJqYW5lLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.meZ-AaGHUPwN_GvVOE3IkKuAJ1wqlCguaXf3gm3Ww8s Send a GET request to the route with the JWT of `jane`: curl -i "http://127.0.0.1:9080/get" -H "Authorization: ${jane_jwt_token}" You should receive an `HTTP/1.1 200 OK` response. Send a POST request to the route with the same JWT: curl -i "http://127.0.0.1:9080/post" -X POST -H "Authorization: ${jane_jwt_token}" You should also receive an `HTTP/1.1 200 OK` response. tip To examine whether the authorization decision comes from OPA, you should observe the following log in the OPA server if you have set `--log-level debug`: { "result":{ "allow": true, "bearer_token": "eyJ...", ... }} * [Examples](https://docs.api7.ai/hub/opa/#examples) * [Implement a Basic Policy](https://docs.api7.ai/hub/opa/#implement-a-basic-policy) * [Understand Data Format](https://docs.api7.ai/hub/opa/#understand-data-format) * [Return Custom Response](https://docs.api7.ai/hub/opa/#return-custom-response) * [Implement RBAC](https://docs.api7.ai/hub/opa/#implement-rbac) --- # Kafka Logger | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/kafka-logger/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `kafka-logger` plugin pushes request and response logs as JSON objects to Apache Kafka clusters in batches and supports the customization of log formats. Examples[​](https://docs.api7.ai/hub/kafka-logger/#examples "Direct link to Examples") --------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `kafka-logger` plugin for different scenarios. To follow along the examples, start a sample Kafka cluster using the below Docker compose file: docker-compose.yml services: zookeeper-server1: image: bitnami/zookeeper:3.6.0 environment: ALLOW_ANONYMOUS_LOGIN: "yes" restart: unless-stopped ports: - "2181:2181" networks: kafka_net: zookeeper-server2: image: bitnami/zookeeper:3.6.0 environment: ALLOW_ANONYMOUS_LOGIN: "yes" restart: unless-stopped ports: - "12181:12181" networks: kafka_net: kafka-server1: image: bitnami/kafka:2.8.1 container_name: notkafka environment: KAFKA_CFG_ZOOKEEPER_CONNECT: zookeeper-server1:2181 ALLOW_PLAINTEXT_LISTENER: "yes" KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: "true" KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://127.0.0.1:9092 restart: unless-stopped ports: - "9092:9092" depends_on: - zookeeper-server1 - zookeeper-server2 networks: kafka_net:networks: kafka_net: Start containers: docker compose up -d Wait for messages in the configured Kafka topic: docker exec -it notkafka kafka-console-consumer.sh --bootstrap-server kafka-server1:9092 --topic test2 --from-beginning Open a new terminal session for the following steps working with APISIX. ### Log in Different Meta Log Formats[​](https://docs.api7.ai/hub/kafka-logger/#log-in-different-meta-log-formats "Direct link to Log in Different Meta Log Formats") The following example demonstrates how you can enable the `kafka-logger` plugin on a route, which logs client requests to the route and pushes logs to Kafka. You will also understand the differences between the `default` and `origin` meta log formats. Create a route with `kafka-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "kafka-logger-route", "uri": "/get", "plugins": { "kafka-logger": { "meta_format": "default", "brokers": [ { "host": "127.0.0.1", "port": 9092 } ], "kafka_topic": "test2", "key": "key1", "batch_max_size": 1 } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `meta_format`: set to the `default` log format. ❷ `batch_max_size`: set to 1 to send the log entry immediately. Send a request to the route to generate a log entry: curl -i "http://127.0.0.1:9080/get" You should see an `HTTP/1.1 200 OK` response. You should see a log entry in the Kafka topic similar to the following: { "latency": 411.00001335144, "request": { "querystring": {}, "headers": { "host": "127.0.0.1:9080", "user-agent": "curl/7.74.0", "accept": "*/*" }, "method": "GET", "size": 83, "uri": "/get", "url": "http://127.0.0.1:9080/get" }, "response": { "headers": { "content-length": "233", "access-control-allow-credentials": "true", "content-type": "application/json", "connection": "close", "access-control-allow-origin": "*", "date": "Fri, 10 Nov 2023 06:02:44 GMT", "server": "APISIX/3.8.0" }, "status": 200, "size": 475 }, "route_id": "kafka-logger-route", "client_ip": "127.0.0.1", "server": { "hostname": "debian-apisix", "version": "3.8.0" }, "apisix_latency": 18.00001335144, "service_id": "", "upstream_latency": 393, "start_time": 1699596164550, "upstream": "54.90.18.68:80"} Update the meta log format to `origin`: curl "http://127.0.0.1:9180/apisix/admin/routes/kafka-logger-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "kafka-logger": { "meta_format": "origin" } } }' Send a request to the route again to generate a new log entry: curl -i "http://127.0.0.1:9080/get" You should see an `HTTP/1.1 200 OK` response. You should see a log entry in the Kafka topic similar to the following: GET /get HTTP/1.1host: 127.0.0.1:9080user-agent: curl/7.74.0accept: */* ### Log Request and Response Headers With Plugin Metadata[​](https://docs.api7.ai/hub/kafka-logger/#log-request-and-response-headers-with-plugin-metadata "Direct link to Log Request and Response Headers With Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) and [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) to log specific headers from request and response. In APISIX, [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) is used to configure the common metadata fields of all plugin instances of the same plugin. It is useful when a plugin is enabled across multiple resources and requires a universal update to their metadata fields. First, create a route with `kafka-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "kafka-logger-route", "uri": "/get", "plugins": { "kafka-logger": { "meta_format": "default", "brokers": [ { "host": "127.0.0.1", "port": 9092 } ], "kafka_topic": "test2", "key": "key1", "batch_max_size": 1 } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `meta_format`: set to the `default` log format. It is important to note that this is mandatory if you would like to customize log format with plugin metadata. If `meta_format` is set to `origin`, the log entries will remain in `origin` format. ❷ `batch_max_size`: set to 1 to send the log entry immediately. Next, configure the plugin metadata for `kafka-logger`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/kafka-logger" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr", "env": "$http_env", "resp_content_type": "$sent_http_Content_Type" } }' ❶ log the custom request header `env`. ❷ log the response header `Content-Type`. Send a request to the route with the `env` header: curl -i "http://127.0.0.1:9080/get" -H "env: dev" You should see a log entry in the Kafka topic similar to the following: { "@timestamp": "2023-11-10T23:09:04+00:00", "host": "127.0.0.1", "client_ip": "127.0.0.1", "route_id": "kafka-logger-route", "env": "dev", "resp_content_type":"application/json"} ### Log Request Bodies Conditionally[​](https://docs.api7.ai/hub/kafka-logger/#log-request-bodies-conditionally "Direct link to Log Request Bodies Conditionally") The following example demonstrates how you can conditionally log request body. Create a route with `kafka-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "kafka-logger-route", "uri": "/post", "plugins": { "kafka-logger": { "brokers": [ { "host": "127.0.0.1", "port": 9092 } ], "kafka_topic": "test2", "key": "key1", "batch_max_size": 1, "include_req_body": true, "include_req_body_expr": [["arg_log_body", "==", "yes"]] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `include_req_body`: set to true to include request body. ❷ `include_req_body_expr`: only include request body if the URL query string `log_body` is `yes`. Send a request to the route with a URL query string satisfying the condition: curl -i "http://127.0.0.1:9080/post?log_body=yes" -X POST -d '{"env": "dev"}' You should see the request body logged: { ..., "method": "POST", "body": "{\"env\": \"dev\"}", "size": 179 }} Send a request to the route without any URL query string: curl -i "http://127.0.0.1:9080/post" -X POST -d '{"env": "dev"}' You should not observe the request body in the log. info If you have customized the `log_format` in addition to setting `include_req_body` or `include_resp_body` to `true`, the plugin would not include the bodies in the logs. As a workaround, you may be able to use the NGINX variable `$request_body` in the log format, such as: { "kafka-logger": { ..., "log_format": {"body": "$request_body"} }} * [Examples](https://docs.api7.ai/hub/kafka-logger/#examples) * [Log in Different Meta Log Formats](https://docs.api7.ai/hub/kafka-logger/#log-in-different-meta-log-formats) * [Log Request and Response Headers With Plugin Metadata](https://docs.api7.ai/hub/kafka-logger/#log-request-and-response-headers-with-plugin-metadata) * [Log Request Bodies Conditionally](https://docs.api7.ai/hub/kafka-logger/#log-request-bodies-conditionally) --- # ACL | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/acl/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `acl` plugin allows or denies request access to upstream resources by verifying whether the user initiating the request is in the access control lists. Examples[​](https://docs.api7.ai/hub/acl/#examples "Direct link to Examples") ------------------------------------------------------------------------------ The examples below demonstrate how you can use the `acl` plugin for different scenarios. ### Control Access by Examining Consumer Labels[​](https://docs.api7.ai/hub/acl/#control-access-by-examining-consumer-labels "Direct link to Control Access by Examining Consumer Labels") The following example demonstrates how to control consumer access based on consumer labels, upon a successful authentication. Create two consumers, `john` and `jane`, each with their own labels for organizations and projects: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john", "labels": { "org": "[\"opensource\",\"apache\"]", "project": "[\"tomcat\",\"web-server\",\"http,server\"]" } }' curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jane", "labels": { "org": "apache", "project": "gateway,apisix,web-server" } }' Create `key-auth` credentials for `john` and `jane`: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-key-auth", "plugins": { "key-auth": { "key": "jane-key" } } }' tip Consumer labels can be configured with either of the two approaches: 1. comma-separated string value, such as `{"project": "gateway,apisix"}` 2. character escaped string array,such as `{"project": "[\"gateway\",\"apisix\"]"}` Create a route with `key-auth` enabled, and configure the `acl` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "acl-route", "uri": "/get", "plugins": { "key-auth": {}, "acl": { "allow_labels": { "org": ["opensource"] } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ allow only consumer with `org` label value `opensource` to access the upstream resource. Send a request to the route as consumer `jane`: curl -i "http://127.0.0.1:9080/get" -H 'apikey: jane-key' You should see an `HTTP/1.1 403 Forbidden` response, as consumer `jane` was not configured with the required label to access the route. Send a request to the route as consumer `john`: curl -i "http://127.0.0.1:9080/get" -H 'apikey: john-key' You should see an `HTTP/1.1 200 OK` response, as consumer `john` was configured with the required label to access the route. ### Control Access by Examining User Information from External Identity Provider[​](https://docs.api7.ai/hub/acl/#control-access-by-examining-user-information-from-external-identity-provider "Direct link to Control Access by Examining User Information from External Identity Provider") The following example demonstrates how to control user access based on user labels, upon a successful authentication with an external identity provider. Specifically, the example uses Keycloak and user groups as labels. Follow the steps in [set up SSO with Keycloak how-to guide](https://docs.api7.ai/apisix/how-to-guide/authentication/set-up-sso-with-keycloak) to create a realm, a client, and a user. Go to **Groups** and create two new groups, `apisix` and `opensource`: ![new-group](https://static.api7.ai/uploads/2024/02/05/n4M3Id6L_new-group.png) To add the user to group memberships, click into the user and go to the **Groups** tab. Select each group in turn and click **join**: ![join-user-to-groups](https://static.api7.ai/uploads/2024/02/05/vaBSVk5U_add-membership.png) To include the group membership when user info is requested from Keycloak, go to the client and go to the **Mappers** tab. Create a new mapper: ![create-mapper](https://static.api7.ai/uploads/2024/02/05/GTGLRwc9_tHUb4QIw_create-mapper.png) Fill in the name for the protocol mapper, select **Group Membership** as the mapper type, use `groups` as the token claim name, and click **Save**: ![save-protocol-mapper](https://static.api7.ai/uploads/2024/02/05/A5ABZSmE_protocol-mapper.png) To verify if the attribute will be visible when requesting user info, first obtain an access token from Keycloak: OIDC_USER=quickstart-userOIDC_PASSWORD=quickstart-user-passOIDC_CLIENT_ID=apisix-quickstart-clientOIDC_CLIENT_SECRET=bi9NFscFT4k0ljaRzQWlJWthrlygUn3x # replace with your client secretcurl "http://$KEYCLOAK_IP:8080/realms/quickstart-realm/protocol/openid-connect/token" -X POST \ -d 'grant_type=password' \ -d 'client_id='$OIDC_CLIENT_ID'' \ -d 'client_secret='$OIDC_CLIENT_SECRET'' \ -d 'username='$OIDC_USER'' \ -d 'password='$OIDC_PASSWORD'' Save the access token to an environment variable called `ACCESS_TOKEN` and send a request to the Keycloak user info endpoint with the token: curl "http://$KEYCLOAK_IP:8080/realms/quickstart-realm/protocol/openid-connect/userinfo" -H "Authorization: Bearer $ACCESS_TOKEN" You should see a response similar to the following: { "sub":"4310e97c-d4c3-479b-bbbd-8c66120e6cee", "email_verified":false, "groups":["/apisix", "/opensource"], "preferred_username":"quickstart-user"} Suppose you would like to only allow users with `/apisix` value in the `groups` attribute to access upstream resources. Create a route with [`openid-connect`](https://docs.api7.ai/hub/openid-connect) and `acl` plugins as such: KEYCLOAK_IP=192.168.1.81 # replace with your host IPOIDC_DISCOVERY=http://${KEYCLOAK_IP}:8080/realms/quickstart-realm/.well-known/openid-configurationcurl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "acl-route", "uri":"/anything/*", "plugins": { "openid-connect": { "bearer_only": true, "client_id": "'"$OIDC_CLIENT_ID"'", "client_secret": "'"$OIDC_CLIENT_SECRET"'", "discovery": "'"$OIDC_DISCOVERY"'", "scope": "openid profile", "redirect_uri": "http://localhost:9080/anything/callback" }, "acl": { "external_user_label_field": "groups", "allow_labels": { "groups": ["/apisix"] } } }, "upstream":{ "type":"roundrobin", "nodes":{ "httpbin.org:80":1 } } }' ❶ configure `external_user_label_field` to the name mapped to the group. ❷ allow only users with `groups` attribute value `/apisix` to access the upstream resource. Send a request to the route with a valid access token: curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer $ACCESS_TOKEN" You should see an `HTTP/1.1 200 OK` response, which verifies that the API call was authorized and allowed to access upstream resources. To see how the `acl` plugin restricts access, update the plugin to require an attribute that the user info does not include: curl "http://127.0.0.1:9180/apisix/admin/routes/acl-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "acl": { "allow_labels": { "groups": ["foobar"] } } } }' Send another request to the same route with a valid access token: curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer $ACCESS_TOKEN" You should see an `HTTP/1.1 403 Forbidden` response, which shows the access is restricted. ### Control Access by Examining Nested User Information from External Identity Provider[​](https://docs.api7.ai/hub/acl/#control-access-by-examining-nested-user-information-from-external-identity-provider "Direct link to Control Access by Examining Nested User Information from External Identity Provider") The following example demonstrates how you can configure the `acl` plugin to fetch labels from a nested JSON structure and control access by examining whether these labels are whitelisted. If you are using Keycloak as the identity provider, the steps of configuration are similar to the [last example](https://docs.api7.ai/hub/acl/#control-access-by-examining-user-information-from-external-identity-provider) , except that when you create the **Group Membership**, configure the **Token Claim Name** as a fully qualified name for group membership to be displayed in the nested JSON object: ![keycloak-nested-group-membership](https://static.api7.ai/uploads/2024/04/11/6lMJ1lH2_nested-group-membership.png) caution The earlier versions of Keycloak have [a bug](https://github.com/keycloak/keycloak/pull/20480) that caused Keycloak to return the dots in **Token Claim Name** as string literals when requesting user information, instead of returning a nested JSON structure. Please use Keycloak `>= 23.0.0`. Next, request user access token and user information from `/userinfo` endpoint, similar to the [last example](https://docs.api7.ai/hub/acl/#control-access-by-examining-user-information-from-external-identity-provider) . You should see Keycloak returning user information similar to the following: { "sub": "f62086ef-29e1-4401-8609-451a2d724bd7", "email_verified": false, "acl_labels": { "nested": { "groups": [ "/apisix", "/opensource" ] } }, "preferred_username": "quickstart-user"} In API7, create a route with [`openid-connect`](https://docs.api7.ai/hub/openid-connect) to authenticate with Keycloak and configure `acl` plugins as such: KEYCLOAK_IP=192.168.1.81 # replace with your host IPOIDC_DISCOVERY=http://${KEYCLOAK_IP}:8080/realms/quickstart-realm/.well-known/openid-configurationcurl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "acl-route", "uri":"/anything/*", "plugins": { "openid-connect": { "bearer_only": true, "client_id": "'"$OIDC_CLIENT_ID"'", "client_secret": "'"$OIDC_CLIENT_SECRET"'", "discovery": "'"$OIDC_DISCOVERY"'", "scope": "openid profile", "redirect_uri": "http://localhost:9080/anything/callback" }, "acl": { "external_user_label_field": "$..groups", "external_user_label_field_key": "groups", "external_user_label_field_parser": "json", "allow_labels": { "groups": ["/apisix"] } } }, "upstream":{ "type":"roundrobin", "nodes":{ "httpbin.org:80":1 } } }' ❶ configure user label field with a valid [JSONPath](https://goessner.net/articles/JsonPath) to the nested field with labels. ❷ set the field key to the name of the nested field. ❸ set the parser to `json` to be consistent with the user information data structure. ❹ allow only users with `groups` attribute value `/apisix` to access the upstream resource. To verify the ACL, send a request to the route with a valid access token: curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer $ACCESS_TOKEN" You should see an `HTTP/1.1 200 OK` response, which suggests that the API call was authorized and allowed to access upstream resources. To see how the `acl` plugin restricts access, update the plugin to require an attribute that the user info does not include: curl "http://127.0.0.1:9180/apisix/admin/routes/acl-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "acl": { "allow_labels": { "groups": ["foobar"] } } } }' Send another request to the same route with a valid access token: curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer $ACCESS_TOKEN" You should see an `HTTP/1.1 403 Forbidden` response, which shows the access is restricted. * [Examples](https://docs.api7.ai/hub/acl/#examples) * [Control Access by Examining Consumer Labels](https://docs.api7.ai/hub/acl/#control-access-by-examining-consumer-labels) * [Control Access by Examining User Information from External Identity Provider](https://docs.api7.ai/hub/acl/#control-access-by-examining-user-information-from-external-identity-provider) * [Control Access by Examining Nested User Information from External Identity Provider](https://docs.api7.ai/hub/acl/#control-access-by-examining-nested-user-information-from-external-identity-provider) --- # Consumer Restriction | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/consumer-restriction/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `consumer-restriction` plugin enables access controls based on consumer name, route ID, service ID, or consumer group ID. The plugin needs to work with authentication plugins, such as [`key-auth`](https://docs.api7.ai/hub/key-auth) and [`jwt-auth`](https://docs.api7.ai/hub/jwt-auth) , which means you should always create at least one [consumer](https://docs.api7.ai/apisix/key-concepts/consumers) in your use case. See examples below for more details. Examples[​](https://docs.api7.ai/hub/consumer-restriction/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `consumer-restriction` plugin for different scenarios. While the examples use [`key-auth`](https://docs.api7.ai/hub/key-auth) as the authentication method, you can easily adjust to other authentication plugins based on your needs. ### Restrict Access by Consumers[​](https://docs.api7.ai/hub/consumer-restriction/#restrict-access-by-consumers "Direct link to Restrict Access by Consumers") The example below demonstrates how you can use the `consumer-restriction` plugin on a route to restrict consumer access by consumer names, where consumers are authenticated with [`key-auth`](https://docs.api7.ai/hub/key-auth) . Create a consumer `JohnDoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "JohnDoe" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/JohnDoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Create a second consumer `JaneDoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "JaneDoe" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/JaneDoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-key-auth", "plugins": { "key-auth": { "key": "jane-key" } } }' Next, create a route with key authentication enabled, and configure `consumer-restriction` to allow only consumer `JaneDoe`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "consumer-restricted-route", "uri": "/get", "plugins": { "key-auth": {}, "consumer-restriction": { "whitelist": ["JaneDoe"] } }, "upstream" : { "nodes": { "httpbin.org":1 } } }' Send a request to the route as consumer `JohnDoe`: curl -i "http://127.0.0.1:9080/get" -H 'apikey: john-key' You should receive an `HTTP/1.1 403 Forbidden` response with the following message: {"message":"The consumer_name is forbidden."} Send another request to the route as consumer `JaneDoe`: curl -i "http://127.0.0.1:9080/get" -H 'apikey: jane-key' You should receive an `HTTP/1.1 200 OK` response, showing the consumer access is permitted. ### Restrict Access by Consumers and HTTP Methods[​](https://docs.api7.ai/hub/consumer-restriction/#restrict-access-by-consumers-and-http-methods "Direct link to Restrict Access by Consumers and HTTP Methods") The example below demonstrates how you can use the `consumer-restriction` plugin on a route to restrict consumer access by consumer name and HTTP methods, where consumers are authenticated with [`key-auth`](https://docs.api7.ai/hub/key-auth) . Create a consumer `JohnDoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "JohnDoe" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/JohnDoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Create a second consumer `JaneDoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "JaneDoe" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/JaneDoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-key-auth", "plugins": { "key-auth": { "key": "jane-key" } } }' Next, create a route with key authentication enabled, and use `consumer-restriction` to allow only the configured HTTP methods by consumers: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "consumer-restricted-route", "uri": "/anything", "plugins": { "key-auth": {}, "consumer-restriction": { "allowed_by_methods":[ { "user": "JohnDoe", "methods": ["GET"] }, { "user": "JaneDoe", "methods": ["POST"] } ] } }, "upstream" : { "nodes": { "httpbin.org":1 } } }' Send a POST request to the route as consumer `JohnDoe`: curl -i "http://127.0.0.1:9080/anything" -X POST -H 'apikey: john-key' You should receive an `HTTP/1.1 403 Forbidden` response with the following message: {"message":"The consumer_name is forbidden."} Now, send a GET request to the route as consumer `JohnDoe`: curl -i "http://127.0.0.1:9080/anything" -X GET -H 'apikey: john-key' You should receive an `HTTP/1.1 200 OK` response, showing the consumer access is permitted. You can also verify the configurations by sending requests as consumer `JaneDoe` and observe the behaviours match up to what was configured in the `consumer-restriction` plugin on the route. ### Restricting by Service ID[​](https://docs.api7.ai/hub/consumer-restriction/#restricting-by-service-id "Direct link to Restricting by Service ID") The example below demonstrates how you can use the `consumer-restriction` plugin to restrict consumer access by service ID, where the consumer is authenticated with [`key-auth`](https://docs.api7.ai/hub/key-auth) . Create two sample services: curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "srv-1", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org":1 } } }' curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "srv-2", "upstream": { "type": "roundrobin", "nodes": { "mock.api7.ai":1 } } }' Next, create a consumer with `key-auth` and configure `consumer-restriction` to allow only `srv-1` service: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "JohnDoe", "plugins": { "key-auth": { "key": "john-key" }, "consumer-restriction": { "type": "service_id", "whitelist": ["srv-1"] } } }' Finally, create two routes, with each belonging to one of the services created earlier: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "srv-1-route", "uri": "/anything", "service_id": "srv-1" }' curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "srv-2-route", "uri": "/srv-2", "service_id": "srv-2" }' Send a request to the route in the `srv-1` service: curl -i "http://127.0.0.1:9080/anything" -H 'apikey: john-key' You should receive an `HTTP/1.1 200 OK` response, showing the consumer access is permitted. Send a request to the route in the `srv-2` service: curl -i "http://127.0.0.1:9080/srv-2" -H 'apikey: john-key' You should receive an `HTTP/1.1 401 Unauthorized` response with the following message: {"message":"The request is rejected, please check the service_id for this request"} * [Examples](https://docs.api7.ai/hub/consumer-restriction/#examples) * [Restrict Access by Consumers](https://docs.api7.ai/hub/consumer-restriction/#restrict-access-by-consumers) * [Restrict Access by Consumers and HTTP Methods](https://docs.api7.ai/hub/consumer-restriction/#restrict-access-by-consumers-and-http-methods) * [Restricting by Service ID](https://docs.api7.ai/hub/consumer-restriction/#restricting-by-service-id) --- # Anonymous Consumers | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page The anonymous consumer feature in API7 Enterprise enables selective bypass of authentication requirements by allowing the configuration of an anonymous consumer on authentication plugins. This feature allows protected routes to grant access to non-authenticated callers. Consider a scenario where a company would like to offer certain freemium API access for demonstrating the product, in addition to the paid premium product. Anonymous consumers can be assigned lower rate limiting quotas than authenticated users, helping prioritize resources for premium users while still allowing unauthenticated access where necessary. When there are excessive anonymous requests, requests exceeding the assigned quota will be rejected and the quota will only be reset in the next rate limiting cycle. Authenticated premium users, in contrast, receive higher quotas, ensuring they have priority access and better service reliability. ![diagram of anonymous freemium consumers and premium consumers with different rate limiting quotas](https://static.api7.ai/uploads/2024/11/26/BnoIKodj_anonymous-consumer.png) Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#key-features "Direct link to Key Features") -------------------------------------------------------------------------------------------------------------------------------- * Enhance ease of access for non-critical data or trial features by eliminating authentication steps for public or demo endpoints. * Ensure higher service availability for authenticated users. * Support multiple authentication methods, including key authentication, JWT, HMAC, and basic authentication. * Forward the `X-Consumer-Username` header containing the anonymous consumer name to upstream services, which allows additional business logic to be implemented. * Maintain security boundaries by encouraging the isolation of anonymous consumer permissions from those fully authenticated, reducing the risk of exposing sensitive data or impacting critical operations. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#use-cases "Direct link to Use Cases") ----------------------------------------------------------------------------------------------------------------------- ### Implement Different Rate Limiting Quotas[​](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#implement-different-rate-limiting-quotas "Direct link to Implement Different Rate Limiting Quotas") Anonymous consumers can be assigned specific rate limiting policies that differ from those of authenticated users. By design, anonymous consumers often have stricter quotas to limit the amount of data or requests they can access, helping to safeguard resources and maintain service quality. For example, an API might allow unauthenticated users to make up to 100 requests per day, while authenticated users could be permitted several thousand. This distinction protects core resources for paying customers or users with verified identities and helps to mitigate potential abuse or overuse by unknown entities. ### Enable Public Access for Trial or Freemium Models[​](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#enable-public-access-for-trial-or-freemium-models "Direct link to Enable Public Access for Trial or Freemium Models") For businesses offering freemium or trial-based access, the anonymous consumer feature provides a streamlined way to allow access to a limited set of API functionalities without requiring new users to register or authenticate. This setup is particularly useful for lowering onboarding friction and enabling prospective customers to explore features. For instance, a weather API could allow anonymous access to basic forecast data while reserving premium data, such as historical trends or high-resolution radar images, for authenticated users. This encourages users to upgrade if they require more comprehensive or unrestricted access. ### Simplify Onboarding for Demo or Testing Environments[​](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#simplify-onboarding-for-demo-or-testing-environments "Direct link to Simplify Onboarding for Demo or Testing Environments") By designating anonymous consumers for demo environments or testing endpoints, organizations can allow developers to access sample APIs without the need for creating a full account or verifying identity. This can help reduce entry barriers for third-party developers or clients who want to assess API integration feasibility. For example, an e-commerce platform might provide sample product and pricing data via anonymous consumer access in a sandbox environment, making it easy for prospective partners to test integration without onboarding delays or unnecessary account setup steps. * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#key-features) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#use-cases) * [Implement Different Rate Limiting Quotas](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#implement-different-rate-limiting-quotas) * [Enable Public Access for Trial or Freemium Models](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#enable-public-access-for-trial-or-freemium-models) * [Simplify Onboarding for Demo or Testing Environments](https://docs.api7.ai/apisix/enterprise-feature/anonymous-consumers/#simplify-onboarding-for-demo-or-testing-environments) --- # Router Options | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/reference/router-options/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In APISIX, there are three HTTP router options: 1. `radixtree_host_uri` routes requests by hosts and URI paths, prioritizing hostnames over URI paths during matching. This is the default setting and the behaviour is the same as NGINX. 2. `radixtree_uri` routes requests by hosts and URI paths, prioritizing URI paths over hostnames during matching. 3. `radixtree_uri_with_parameter` supports the use of parameters in path matching. These router options can be configured in `conf/config.yaml` under `apisix.router.http`. `radixtree_host_uri`[​](https://docs.api7.ai/apisix/reference/router-options/#radixtree_host_uri "Direct link to radixtree_host_uri") -------------------------------------------------------------------------------------------------------------------------------------- This is the default router setting, which prioritizes hostnames over URI paths when route matching. If you would like to explicitly configure the option, add the following block to your configuration file: config.yaml apisix: router: http: radixtree_host_uri [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. Configuring two routes with the same URI but different matching hosts is useful for scenarios like multi-tenant SaaS platforms, where each tenant is served through a custom subdomain. For example, both tenants may access the same endpoint, but the requests are routed to different upstream services based on the host. To do so, you can configure two routes with the same matching URI but different matching hosts and upstream services: curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "httpbin-host-test1", "uri": "/get", "host": "test1.com", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "postman-host-test2", "uri": "/get", "host": "test2.com", "upstream": { "type": "roundrobin", "nodes": { "postman-echo.com:80": 1 } } }' Send a request that matches the host of the first route: curl "http://127.0.0.1:9080/get" -H 'host: test1.com' You should see a response from `httpbin.org`: { "args": {}, "headers": { "Accept": "*/*", "Host": "test1.com", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-6746c0bf-653fac896be8818275f1e8da", "X-Forwarded-Host": "test1.com" }, "origin": "192.168.65.1, 43.252.208.90", "url": "http://test1.com/get"} Send a request that matches the host of the second route: curl "http://127.0.0.1:9080/get" -H 'host: test2.com' You should see a response from `postman-echo.com`: { "args": {}, "headers": { "host": "test2.com", "x-request-start": "t=1732834286", "connection": "close", "x-forwarded-proto": "http", "x-forwarded-port": "80", "x-amzn-trace-id": "Root=1-6746c0ca-2b0b323902e784f512051ef6", "x-forwarded-host": "test2.com", "user-agent": "curl/8.6.0", "accept": "*/*" }, "url": "http://test2.com/get"} To understand the behaviour more, create a third route that matches all requests: curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "mock-api7-all", "uri": "/*", "upstream": { "type": "roundrobin", "nodes": { "mock.api7.ai:443": 1 }, "scheme": "https", "pass_host": "node" } }' Send a request with a random host name: curl "http://127.0.0.1:9080/get" -H 'host: random.com' You should see the request is forwarded to the third route: API7.ai, the creator of Apache APISIX, delivers a cloud-native API Gateway solution for the Enterprise, to help you maximize the value of APIs. Send another request that matches the host of the first route: curl "http://127.0.0.1:9080/get" -H 'host: test1.com' You should see the request is forwarded to the `httpbin.org` upstream. This demonstrates how the router prioritizes hostnames in routing when it is set to `radixtree_host_uri`. `radixtree_uri`[​](https://docs.api7.ai/apisix/reference/router-options/#radixtree_uri "Direct link to radixtree_uri") ----------------------------------------------------------------------------------------------------------------------- `radixtree_uri` prioritizes URI paths over hostnames when route matching. To use `radixtree_uri` as the HTTP router setting, add the following block to your configuration file: config.yaml apisix: router: http: radixtree_uri [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. To understand the behaviour, create a route that matches `test1.com` host and all URI paths: curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "httpbin-host-test1", "uri": "/*", "host": "test1.com", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create another route that matches requests to `/get`: curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "mock-api7-all", "uri": "/get", "upstream": { "type": "roundrobin", "nodes": { "mock.api7.ai:443": 1 }, "scheme": "https", "pass_host": "node" } }' Send a request to `/get` with `test1.com` host: curl "http://127.0.0.1:9080/get" -H 'host: test1.com' You should see the request is forwarded to `mock.api7.ai` API7.ai, the creator of Apache APISIX, delivers a cloud-native API Gateway solution for the Enterprise, to help you maximize the value of APIs. This shows the hostname is not being prioritized when router operates in `radixtree_uri` mode and full path matching takes the [priority](https://docs.api7.ai/apisix/reference/router-options/#matching-priority) . Send another request to `/anything` with `test1.com` host: curl "http://127.0.0.1:9080/anything" -H 'host: test1.com' You should see the request is forwarded to the `httpbin.org` upstream. `radixtree_uri_with_parameter`[​](https://docs.api7.ai/apisix/reference/router-options/#radixtree_uri_with_parameter "Direct link to radixtree_uri_with_parameter") -------------------------------------------------------------------------------------------------------------------------------------------------------------------- `radixtree_uri_with_parameter` supports the use of parameters when route matching. To use `radixtree_uri_with_parameter` as the HTTP router setting, add the following block to your configuration file: config.yaml apisix: router: http: radixtree_uri_with_parameter [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. A common use case is extracting a part of the URI path and using it to construct the request sent to upstream services. This can be used to rewrite the URL path or set a custom header, enabling flexible dynamic routing and request customization based on client input. The matched URI path can be easily extracted with `uri_param_`. For example, you can match a URI path like `/user/123/profile`, extract the user ID `123`, and forward the value to the upstream service in a new header. To do so, create a route as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "httpbin", "uri": "/anything/user/:user_id/profile", "plugins":{ "proxy-rewrite": { "headers": { "set": { "X-User-ID": "$uri_param_user_id" } } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Match requests to `/anything/user/:user_id/profile` where `user_id` is a parameter. ❷ Assign the `user_id` parameter value to a new header `X-User-ID`. To verify, send a request to the route: curl "http://127.0.0.1:9080/anything/user/123/profile" You should see the following response: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-68873cf5-7248f64d19d607ea50aa9735", "X-Forwarded-Host": "127.0.0.1", "X-User-Id": "123" }, ...} The route parameter can also accept URL-encoded string. For instance, if you send a request as such: curl -i "http://127.0.0.1:9080/anything/user/123%20456/profile" The user ID would be extracted as `123 456`: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-68873d37-7634825b20d05dee3a852cb9", "X-Forwarded-Host": "127.0.0.1", "X-User-Id": "123 456" }, ...} Understand Route Matching[​](https://docs.api7.ai/apisix/reference/router-options/#understand-route-matching "Direct link to Understand Route Matching") --------------------------------------------------------------------------------------------------------------------------------------------------------- APISIX leverages the [luaradixtree](https://github.com/api7/lua-resty-radixtree) library for routing, an adaptive radix tree implemented in Lua for OpenResty. It utilizes the Foreign Function Interface (FFI) to integrate with [rax](https://github.com/antirez/rax) , ensuring efficient and high-performance routing. There are several ways of route matching. ### Full Path[​](https://docs.api7.ai/apisix/reference/router-options/#full-path "Direct link to Full Path") Suppose the route URI is: /anything/foo The route will only match requests to `/anything/foo`. ### Wildcard[​](https://docs.api7.ai/apisix/reference/router-options/#wildcard "Direct link to Wildcard") Suppose the route URI is: /anything/* The route will match any request to sub-paths of `/anything`, such as `/anything/foo` and `/anything/bar`. Note that it will not match requests to `/anything`. The wildcard does not need to be at the end of the URI. You can also have a route URI as such: /*/*/test This will match requests with URIs like `/anything/foo/test`. ### Matching Priority[​](https://docs.api7.ai/apisix/reference/router-options/#matching-priority "Direct link to Matching Priority") Full path matching has a higher priority than wildcard matching. Suppose you have two routes with URI `/anything/foo` and `/anything/*` respectively. The request to `/anything/foo` will be matched by the `/anything/foo` route, instead of the `/anything/*` route. * [`radixtree_host_uri`](https://docs.api7.ai/apisix/reference/router-options/#radixtree_host_uri) * [`radixtree_uri`](https://docs.api7.ai/apisix/reference/router-options/#radixtree_uri) * [`radixtree_uri_with_parameter`](https://docs.api7.ai/apisix/reference/router-options/#radixtree_uri_with_parameter) * [Understand Route Matching](https://docs.api7.ai/apisix/reference/router-options/#understand-route-matching) * [Full Path](https://docs.api7.ai/apisix/reference/router-options/#full-path) * [Wildcard](https://docs.api7.ai/apisix/reference/router-options/#wildcard) * [Matching Priority](https://docs.api7.ai/apisix/reference/router-options/#matching-priority) --- # Set Breakpoints | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/set-breakpoints/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 Coming soon. --- # Log Consumer Label in Access Log | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Logging consumer labels in APISIX’s access log allows for more visibility and control over API traffic. By capturing consumer labels, organizations can easily identify which clients are accessing specific routes, track usage patterns, and enforce security policies based on these labels. This guide will show you how to log consumer labels in the gateway's access log. note Ingress Controller currently does not support configuring consumer labels. Prerequisite(s)[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#prerequisites "Direct link to Prerequisite(s)") ------------------------------------------------------------------------------------------------------------------------------------------------------------ * Install [Docker](https://docs.docker.com/get-docker/) . * Install [cURL](https://curl.se/) to send requests to the services for validation. * Follow the [Getting Started tutorial](https://docs.api7.ai/apisix/getting-started/) to start a new APISIX instance in Docker. Create and Use the New Variable in Access Log[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#create-and-use-the-new-variable-in-access-log "Direct link to Create and Use the New Variable in Access Log") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In the [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) , initialize a custom variable for the consumer label and assign it a default value `-`. In this example, you will initialize a variable called `$consumer_company`, but you could always initialize more as needed: conf/config.yaml nginx_config: http_server_location_configuration_snippet: | set $consumer_company "-"; In the same file, update the access log format to include the newly initialized variable: conf/config.yaml nginx_config: http: access_log_format: >- $remote_addr - $remote_user [$time_local] $http_host \"$request\" $status $body_bytes_sent $request_time \"$http_referer\" \"$http_user_agent\" $upstream_addr $upstream_status $upstream_response_time \"$upstream_scheme://$upstream_host$upstream_uri\" "$consumer_company" [Reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. Assign Value to the New Variable[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#assign-value-to-the-new-variable "Direct link to Assign Value to the New Variable") ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Use [serverless functions](https://docs.api7.ai/hub/serverless-functions) to assign consumer label value to the new variable. For instance, configure the serverless plugin as a global plugin as such: curl "http://127.0.0.1:9180/apisix/admin/global_rules" -X PUT -d '{ "id": "serverless-consumer-label", "plugins": { "serverless-pre-function": { "phase": "log", "functions": [ "return function (conf, ctx) ngx.var.consumer_company = ctx.consumer and ctx.consumer.labels and ctx.consumer.labels[\"company\"] or \"unknown\" end" ] } }}' The function obtains the consumer label `company` value and assigns it to the `consumer_company` variable. If the consumer does not have a `company` label, the `consumer_company` variable value will be assigned `unknown`. Configure Consumer and Authentication[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#configure-consumer-and-authentication "Direct link to Configure Consumer and Authentication") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Create a consumer `john` with a custom label `company`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT -d '{ "username": "john", "labels": { "company": "smart-technology" }}' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } }}' Create a route and enable `key-auth` on the route: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "key-auth-route", "uri": "/anything", "plugins": { "key-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' Verify[​](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#verify "Direct link to Verify") ----------------------------------------------------------------------------------------------------------------------------------- Send a request to the route with the valid key: curl -i "http://127.0.0.1:9080/anything" -H 'apikey: john-key' You should receive an `HTTP/1.1 200 OK` response and see the following in the access log, where the company name is logged as `smart-technology`: 192.168.107.1 - - [18/Mar/2025:09:17:28 +0000] 127.0.0.1:9080 "GET /anything HTTP/1.1" 200 508 1.260 "-" "curl/8.6.0" 13.210.43.76:80 200 1.153 "http://127.0.0.1:9080" "smart-technology" Send a request to the route without the key: curl -i "http://127.0.0.1:9080/anything" You should see an `HTTP/1.1 401 Unauthorized` response and see the following in the access log, where the company name is logged as `unknown`: 192.168.107.1 - - [18/Mar/2025:09:18:27 +0000] 127.0.0.1:9080 "GET /anything HTTP/1.1" 401 52 0.000 "-" "curl/8.6.0" - - - "http://127.0.0.1:9080" "unknown" * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#prerequisites) * [Create and Use the New Variable in Access Log](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#create-and-use-the-new-variable-in-access-log) * [Assign Value to the New Variable](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#assign-value-to-the-new-variable) * [Configure Consumer and Authentication](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#configure-consumer-and-authentication) * [Verify](https://docs.api7.ai/apisix/how-to-guide/observability/log-consumer-label-in-access-log/#verify) --- # Elasticsearch Logger | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/elasticsearch-logger/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `elasticsearch-logger` plugin pushes request and response logs in batches to [Elasticsearch](https://www.elastic.co/) and supports the customization of log formats. When enabled, the plugin will serialize the request context information to [Elasticsearch Bulk format](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html#docs-bulk) and add them to the queue, before they are pushed to Elasticsearch. Examples[​](https://docs.api7.ai/hub/elasticsearch-logger/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `elasticsearch-logger` plugin for different scenarios. To follow along the examples, start an Elasticsearch instance in Docker: docker run -d \ --name elasticsearch \ --network apisix-quickstart-net \ -v elasticsearch_vol:/usr/share/elasticsearch/data/ \ -p 9200:9200 \ -p 9300:9300 \ -e ES_JAVA_OPTS="-Xms512m -Xmx512m" \ -e discovery.type=single-node \ -e xpack.security.enabled=false \ docker.elastic.co/elasticsearch/elasticsearch:7.17.1 Start a Kibana instance in Docker to visualize the indexed data in Elasticsearch: docker run -d \ --name kibana \ --network apisix-quickstart-net \ -p 5601:5601 \ -e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" \ docker.elastic.co/kibana/kibana:7.17.1 If successful, you should see the Kibana dashboard on [localhost:5601](http://localhost:5601/) . ### Log in the Default Log Format[​](https://docs.api7.ai/hub/elasticsearch-logger/#log-in-the-default-log-format "Direct link to Log in the Default Log Format") The following example demonstrates how you can enable the `elasticsearch-logger` plugin on a route, which logs client requests and responses, as well as pushing logs to Elasticsearch. Create a route with `elasticsearch-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "elasticsearch-logger-route", "uri": "/anything", "plugins": { "elasticsearch-logger": { "endpoint_addrs": ["http://elasticsearch:9200"], "field": { "index": "gateway", "type": "logs" } } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ Configure the endpoint address to Elasticsearch. ❷ Configure the `index` field as `gateway`. ❸ Configure the `type` field as `logs`. Send a request to the route to generate a log entry: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Navigate to the Kibana dashboard on [localhost:5601](http://localhost:5601/) and under **Discover** tab, create a new index pattern `gateway` to fetch the data from Elasticsearch. Once configured, navigate back to the **Discover** tab and you should see a log generated, similar to the following: { "_index": "gateway", "_type": "logs", "_id": "CE-JL5QBOkdYRG7kEjTJ", "_version": 1, "_score": 1, "_source": { "request": { "headers": { "host": "127.0.0.1:9080", "accept": "*/*", "user-agent": "curl/8.6.0" }, "size": 85, "querystring": {}, "method": "GET", "url": "http://127.0.0.1:9080/anything", "uri": "/anything" }, "response": { "headers": { "content-type": "application/json", "access-control-allow-credentials": "true", "server": "APISIX/3.13.0", "content-length": "390", "access-control-allow-origin": "*", "connection": "close", "date": "Mon, 13 Jan 2025 10:18:14 GMT" }, "status": 200, "size": 618 }, "route_id": "elasticsearch-logger-route", "latency": 585.00003814697, "apisix_latency": 18.000038146973, "upstream_latency": 567, "upstream": "50.19.58.113:80", "server": { "hostname": "0b9a772e68f8", "version": "3.13.0" }, "service_id": "", "client_ip": "192.168.65.1" }, "fields": { ... }} ### Customize Log Format With Plugin Metadata[​](https://docs.api7.ai/hub/elasticsearch-logger/#customize-log-format-with-plugin-metadata "Direct link to Customize Log Format With Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) and [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) to log specific headers from request and response. In APISIX, [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) is used to configure the common metadata fields of all plugin instances of the same plugin. It is useful when a plugin is enabled across multiple resources and requires a universal update to their metadata fields. First, create a route with `elasticsearch-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "elasticsearch-logger-route", "uri": "/anything", "plugins": { "elasticsearch-logger": { "endpoint_addrs": ["http://elasticsearch:9200"], "field": { "index": "gateway", "type": "logs" } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' Next, configure the plugin metadata for `elasticsearch-logger`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr", "env": "$http_env", "resp_content_type": "$sent_http_Content_Type" } }' ❶ log the custom request header `env`. ❷ log the response header `Content-Type`. Send a request to the route with the `env` header: curl -i "http://127.0.0.1:9080/anything" -H "env: dev" You should receive an `HTTP/1.1 200 OK` response. Navigate to the Kibana dashboard on [localhost:5601](http://localhost:5601/) and under **Discover** tab, create a new index pattern `gateway` to fetch the data from Elasticsearch, if you have not done so already. Once configured, navigate back to the **Discover** tab and you should see a log generated, similar to the following: { "_index": "gateway", "_type": "logs", "_id": "Ck-WL5QBOkdYRG7kODS0", "_version": 1, "_score": 1, "_source": { "client_ip": "192.168.65.1", "route_id": "elasticsearch-logger-route", "@timestamp": "2025-01-06T10:32:36+00:00", "host": "127.0.0.1", "resp_content_type": "application/json" }, "fields": { ... }} ### Log Request Bodies Conditionally[​](https://docs.api7.ai/hub/elasticsearch-logger/#log-request-bodies-conditionally "Direct link to Log Request Bodies Conditionally") The following example demonstrates how you can conditionally log request body. Create a route with `elasticsearch-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "elasticsearch-logger": { "endpoint_addrs": ["http://elasticsearch:9200"], "field": { "index": "gateway", "type": "logs" }, "include_req_body": true, "include_req_body_expr": [["arg_log_body", "==", "yes"]] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" }, "uri": "/anything", "id": "elasticsearch-logger-route"}' ❶ `include_req_body`: set to true to include request body. ❷ `include_req_body_expr`: only include request body if the URL query string `log_body` is `true`. Send a request to the route with an URL query string satisfying the condition: curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST -d '{"env": "dev"}' You should receive an `HTTP/1.1 200 OK` response. Navigate to the Kibana dashboard on [localhost:5601](http://localhost:5601/) and under **Discover** tab, create a new index pattern `gateway` to fetch the data from Elasticsearch, if you have not done so already. Once configured, navigate back to the **Discover** tab and you should see a log generated, similar to the following: { "_index": "gateway", "_type": "logs", "_id": "Dk-cL5QBOkdYRG7k7DSW", "_version": 1, "_score": 1, "_source": { "request": { "headers": { "user-agent": "curl/8.6.0", "accept": "*/*", "content-length": "14", "host": "127.0.0.1:9080", "content-type": "application/x-www-form-urlencoded" }, "size": 182, "querystring": { "log_body": "yes" }, "body": "{\"env\": \"dev\"}", "method": "POST", "url": "http://127.0.0.1:9080/anything?log_body=yes", "uri": "/anything?log_body=yes" }, "start_time": 1735965595203, "response": { "headers": { "content-type": "application/json", "server": "APISIX/3.13.0", "access-control-allow-credentials": "true", "content-length": "548", "access-control-allow-origin": "*", "connection": "close", "date": "Mon, 13 Jan 2025 11:02:32 GMT" }, "status": 200, "size": 776 }, "route_id": "elasticsearch-logger-route", "latency": 703.9999961853, "apisix_latency": 34.999996185303, "upstream_latency": 669, "upstream": "34.197.122.172:80", "server": { "hostname": "0b9a772e68f8", "version": "3.13.0" }, "service_id": "", "client_ip": "192.168.65.1" }, "fields": { ... }} Send a request to the route without any URL query string: curl -i "http://127.0.0.1:9080/anything" -X POST -d '{"env": "dev"}' Navigate to the Kibana dashboard **Discover** tab and you should see a log generated, but without the request body: { "_index": "gateway", "_type": "logs", "_id": "EU-eL5QBOkdYRG7kUDST", "_version": 1, "_score": 1, "_source": { "request": { "headers": { "content-type": "application/x-www-form-urlencoded", "accept": "*/*", "content-length": "14", "host": "127.0.0.1:9080", "user-agent": "curl/8.6.0" }, "size": 169, "querystring": {}, "method": "POST", "url": "http://127.0.0.1:9080/anything", "uri": "/anything" }, "start_time": 1735965686363, "response": { "headers": { "content-type": "application/json", "access-control-allow-credentials": "true", "server": "APISIX/3.13.0", "content-length": "510", "access-control-allow-origin": "*", "connection": "close", "date": "Mon, 13 Jan 2025 11:15:54 GMT" }, "status": 200, "size": 738 }, "route_id": "elasticsearch-logger-route", "latency": 680.99999427795, "apisix_latency": 4.9999942779541, "upstream_latency": 676, "upstream": "34.197.122.172:80", "server": { "hostname": "0b9a772e68f8", "version": "3.13.0" }, "service_id": "", "client_ip": "192.168.65.1" }, "fields": { ... }} info If you have customized the `log_format` in addition to setting `include_req_body` or `include_resp_body` to `true`, the plugin would not include the bodies in the logs. As a workaround, you may be able to use the NGINX variable `$request_body` in the log format, such as: { "elasticsearch-logger": { ..., "log_format": {"body": "$request_body"} }} ### Include Request Date in Elasticsearch Index[​](https://docs.api7.ai/hub/elasticsearch-logger/#include-request-date-in-elasticsearch-index "Direct link to Include Request Date in Elasticsearch Index") The following example demonstrates how you can configure the `elasticsearch-logger` plugin to include the request date in Elasticsearch index. Create a route with `elasticsearch-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "elasticsearch-logger-route", "uri": "/anything", "plugins": { "elasticsearch-logger": { "endpoint_addrs": ["http://elasticsearch:9200"], "field": { "index": "api7-{%Y.%m.%d}", "type": "logs" } } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ Configure the endpoint address to Elasticsearch. ❷ Configure the `index` field to use the current year, month, and date. ❸ Configure the `type` field as `logs`. Send a request to the route to generate a log entry: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response. Navigate to the Kibana dashboard on [localhost:5601](http://localhost:5601/) and under **Discover** tab, create a new index pattern `api7*` to fetch the data from Elasticsearch. Once configured, navigate back to the **Discover** tab and you should see a log generated, similar to the following: { "_index": "api7-2025.3.10", "_type": "logs", "_id": "CE-KL5QB0kdYRG7dEiTJ", "_version": 1, "_score": 1, "_source": { "request": { ... }, "response": { ... }, "status": 200, "size": 618 }, ...} * [Examples](https://docs.api7.ai/hub/elasticsearch-logger/#examples) * [Log in the Default Log Format](https://docs.api7.ai/hub/elasticsearch-logger/#log-in-the-default-log-format) * [Customize Log Format With Plugin Metadata](https://docs.api7.ai/hub/elasticsearch-logger/#customize-log-format-with-plugin-metadata) * [Log Request Bodies Conditionally](https://docs.api7.ai/hub/elasticsearch-logger/#log-request-bodies-conditionally) * [Include Request Date in Elasticsearch Index](https://docs.api7.ai/hub/elasticsearch-logger/#include-request-date-in-elasticsearch-index) --- # Body Transformer | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/body-transformer/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `body-transformer` plugin performs template-based transformations to transform the request and/or response bodies from one format to another. Examples[​](https://docs.api7.ai/hub/body-transformer/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `body-transformer` for different scenarios. The transformation template uses [lua-resty-template](https://github.com/bungle/lua-resty-template) syntax. See the [template syntax](https://github.com/bungle/lua-resty-template#template-syntax) to learn more. You can also use auxiliary functions `_escape_json()` and `_escape_xml()` to escape special characters such as double quotes, `_body` to access request body, and `_ctx` to access context variables. In all cases, you should ensure that the transformation template is a valid JSON string. ### Transform between JSON and XML SOAP[​](https://docs.api7.ai/hub/body-transformer/#transform-between-json-and-xml-soap "Direct link to Transform between JSON and XML SOAP") The following example demonstrates how to transform the request body from JSON to XML and the response body from XML to JSON when working with a SOAP upstream service. Start the sample SOAP service: cd /tmpgit clone https://github.com/spring-guides/gs-soap-service.gitcd gs-soap-service/complete./mvnw spring-boot:run Create the request and response transformation templates: req_template=$(cat < {{_escape_xml(name)}} EOF)rsp_template=$(cat < 18 then context._multipart:set_simple("status", "adult") else context._multipart:set_simple("status", "minor") end local body = context._multipart:tostring()%}{* body *}EOF) Create a route with `body-transformer` as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "body-transformer-route", "uri": "/anything", "plugins": { "body-transformer": { "request": { "input_format": "multipart", "template": "'"$req_template"'" } } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Set the `input_format` to `multipart`. ❷ Set to the previously created request template. Send a multipart POST request to the route: curl -X POST \ -F "name=john" \ -F "age=10" \ "http://127.0.0.1:9080/anything" You should see a response similar to the following: { "args": {}, "data": "", "files": {}, "form": { "age": "10", "name": "john", "status": "minor" }, "headers": { "Accept": "*/*", "Content-Length": "361", "Content-Type": "multipart/form-data; boundary=------------------------qtPjk4c8ZjmGOXNKzhqnOP", ... }, ...} ### Transform Response Body Based on Consumer Identity[​](https://docs.api7.ai/hub/body-transformer/#transform-response-body-based-on-consumer-identity "Direct link to Transform Response Body Based on Consumer Identity") The following example demonstrates how to customize response body transformations based on different consumer identities. The example shows how to return different response formats to different consumers while filtering sensitive fields and renaming properties. Create the response transformation template that applies different transformations based on the consumer identity: rsp_template=$(cat < web1.conf echo 'worker_processes 1;error_log stderr notice;events { worker_connections 1024;}http { variables_hash_max_size 1024; access_log off; real_ip_header X-Real-IP; charset utf-8; server { listen 80; location / { return 200 "Application 2 is running"; } location /static/ { alias static/; } }}' > web2.conf Start the web services `web1` and `web2`: docker run -d \ --name web1 \ --restart always \ -v $(pwd)/web1.conf:/etc/nginx/nginx.conf \ -p 9081:80 \ --network=apisix-quickstart-net \ nginx:1.25-alpine docker run -d \ --name web2 \ --restart always \ -v $(pwd)/web2.conf:/etc/nginx/nginx.conf \ -p 9082:80 \ --network=apisix-quickstart-net \ nginx:1.25-alpine ❶ Mount `web1.conf` and `web2.conf` from the host machine to `/etc/nginx/nginx.conf` inside the containers. ❷ Map port `80` of the containers to port `9081` and `9082` on the host. Register Services in Eureka[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#register-services-in-eureka "Direct link to Register Services in Eureka") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Save the IP address of your host to an environment variable: HOST_IP=192.168.31.89 # replace with your host IP Register two services in Eureka: curl "http://127.0.0.1:8761/eureka/apps/web" -X POST \ -H "Content-Type: application/json" \ -d '{"instance":{ "instanceId": "'"$HOST_IP"':9081", "hostName": "'"$HOST_IP"'", "ipAddr": "'"$HOST_IP"'", "port":{ "$":9081, "@enabled":true }, "status": "UP", "app": "web", "dataCenterInfo": { "name": "MyOwn", "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo" } }}' curl "http://127.0.0.1:8761/eureka/apps/web" -X POST \ -H "Content-Type: application/json" \ -d '{"instance":{ "instanceId": "'"$HOST_IP"':9082", "hostName": "'"$HOST_IP"'", "ipAddr": "'"$HOST_IP"'", "port":{ "$":9082, "@enabled":true }, "status": "UP", "app": "web", "dataCenterInfo": { "name": "MyOwn", "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo" } }}' Verify if the services are registered successfully: curl "http://127.0.0.1:8761/eureka/apps/web" You should see an XML response including information of your services. Connect Eureka to APISIX[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#connect-eureka-to-apisix "Direct link to Connect Eureka to APISIX") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Add Eureka discovery configurations to APISIX's `config.yaml` [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) : docker exec apisix-quickstart /bin/sh -c "echo 'discovery: eureka: host: - "http://eureka:8761" prefix: /eureka/ fetch_interval: 30 weight: 100 timeout: connect: 2000 send: 2000 read: 5000' >> /usr/local/apisix/conf/config.yaml" ❶ `host`: address of the Eureka server. ❷ `prefix`: the base API path used by the Eureka server for service registration and discovery requests. Reload APISIX for configuration changes to take effect: docker exec apisix-quickstart apisix reload Create a Route in APISIX[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#create-a-route-in-apisix "Direct link to Create a Route in APISIX") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Create a route and configure the upstream to use Eureka for service discovery of `WEB`: * Admin API * ADC curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "eureka-web-route", "uri": "/eureka/web/*", "upstream": { "service_name": "WEB", "discovery_type": "eureka", "type": "roundrobin" }}' An `HTTP/1.1 200 OK` response verifies that the route is created successfully. adc.yaml services: - name: Eureka Service routes: - uris: - /eureka/web/* name: eureka-web-route upstream: service_name: WEB discovery_type: eureka type: roundrobin Synchronize the configuration to APISIX: adc sync -f adc.yaml Validate[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#validate "Direct link to Validate") ------------------------------------------------------------------------------------------------------------------------------- Generate a few requests to the previously created route: curl "http://127.0.0.1:9080/eureka/web/" You should see the responses alternating between the following: Application 1 is running% Application 2 is running% Next Steps[​](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#next-steps "Direct link to Next Steps") ------------------------------------------------------------------------------------------------------------------------------------- In addition to Eureka, APISIX also supports the integration with [HashiCorp Consul](https://docs.api7.ai/apisix/how-to-guide/service-discovery/consul-integration) , Nacos, and other service discovery platforms (coming soon). * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#prerequisites) * [Start Eureka Instance](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#start-eureka-instance) * [Start Sample Web Services](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#start-sample-web-services) * [Register Services in Eureka](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#register-services-in-eureka) * [Connect Eureka to APISIX](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#connect-eureka-to-apisix) * [Create a Route in APISIX](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#create-a-route-in-apisix) * [Validate](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#validate) * [Next Steps](https://docs.api7.ai/apisix/how-to-guide/service-discovery/eureka-integration/#next-steps) --- # JWT Auth | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/jwt-auth/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `jwt-auth` plugin supports the use of [JSON Web Token (JWT)](https://jwt.io/) as a mechanism for clients to authenticate themselves before accessing upstream resources. Once enabled, the plugin exposes an endpoint to create JWT credentials by [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) . The process generates a token that client requests should carry to identify themselves to APISIX. The token can be included in the request URL query string, request header, or cookie. APISIX will then verify the token to determine if a request should be allowed or denied to access upstream resources. When a consumer is successfully authenticated, APISIX adds additional headers, such as `X-Consumer-Username`, `X-Credential-Identifier`, and other consumer custom headers if configured, to the request, before proxying it to the upstream service. The upstream service will be able to differentiate between consumers and implement additional logics as needed. If any of these values is not available, the corresponding header will not be added. Examples[​](https://docs.api7.ai/hub/jwt-auth/#examples "Direct link to Examples") ----------------------------------------------------------------------------------- The examples below demonstrate how you can work with the `jwt-auth` plugin for different scenarios. ### Use JWT for Consumer Authentication[​](https://docs.api7.ai/hub/jwt-auth/#use-jwt-for-consumer-authentication "Direct link to Use JWT for Consumer Authentication") The following example demonstrates how to implement JWT for consumer key authentication. Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Create `jwt-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-jwt-auth", "plugins": { "jwt-auth": { "key": "jack-key", "secret": "jack-hs256-secret-that-is-very-long" } } }' Create a route with `jwt-auth` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "jwt-route", "uri": "/headers", "plugins": { "jwt-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' To issue a JWT for `jack`, you could use [JWT.io's JWT encoder](https://jwt.io/) or other utilities. If you are using [JWT.io's JWT encoder](https://jwt.io/) , do the following: * Fill in `HS256` as the algorithm. * Update the secret in the **Valid secret** section to be `jack-hs256-secret-that-is-very-long`. * Update payload with consumer key `jack-key`; and add `exp` or `nbf` in UNIX timestamp. note If you are using API7 Enterprise, the requirement of `exp` or `nbf` is not mandatory. You can optionally include these claims and use the `claims_to_verify` parameter to configure which claim to verify. Your payload should look similar to the following: { "key": "jack-key", "nbf": 1729132271} Copy the generated JWT and save to a variable: export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU Send a request to the route with the JWT in the `Authorization` header: curl -i "http://127.0.0.1:9080/headers" -H "Authorization: ${jwt_token}" You should receive an `HTTP/1.1 200 OK` response similar to the following: { "headers": { "Accept": "*/*", "Authorization": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MjY2NDk2NDAsImtleSI6ImphY2sta2V5In0.kdhumNWrZFxjUvYzWLt4lFr546PNsr9TXuf0Az5opoM", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66ea951a-4d740d724bd2a44f174d4daf", "X-Consumer-Username": "jack", "X-Credential-Identifier": "cred-jack-jwt-auth", "X-Forwarded-Host": "127.0.0.1" }} Send a request with an invalid token: curl -i "http://127.0.0.1:9080/headers" -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MjY2NDk2NDAsImtleSI6ImphY2sta2V5In0.kdhumNWrZFxjU_random_random" You should receive an `HTTP/1.1 401 Unauthorized` response similar to the following: {"message":"failed to verify jwt"} ### Carry JWT in Request Header, Query String, or Cookie[​](https://docs.api7.ai/hub/jwt-auth/#carry-jwt-in-request-header-query-string-or-cookie "Direct link to Carry JWT in Request Header, Query String, or Cookie") The following example demonstrates how to accept JWT in specified header, query string, and cookie. Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Create `jwt-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-jwt-auth", "plugins": { "jwt-auth": { "key": "jack-key", "secret": "jack-hs256-secret-that-is-very-long" } } }' Create a route with `jwt-auth` plugin, and specify the request parameters carrying the token: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "jwt-route", "uri": "/get", "plugins": { "jwt-auth": { "header": "jwt-auth-header", "query": "jwt-query", "cookie": "jwt-cookie" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' To issue a JWT for `jack`, you could use [JWT.io's JWT encoder](https://jwt.io/) or other utilities. If you are using [JWT.io's JWT encoder](https://jwt.io/) , do the following: * Fill in `HS256` as the algorithm. * Update the secret in the **Valid secret** section to be `jack-hs256-secret-that-is-very-long`. * Update payload with consumer key `jack-key`; and add `exp` or `nbf` in UNIX timestamp. note If you are using API7 Enterprise, the requirement of `exp` or `nbf` is not mandatory. You can optionally include these claims and use the `claims_to_verify` parameter to configure which claim to verify. Your payload should look similar to the following: { "key": "jack-key", "nbf": 1729132271} Copy the generated JWT and save to a variable: export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU #### Verify With JWT in Header[​](https://docs.api7.ai/hub/jwt-auth/#verify-with-jwt-in-header "Direct link to Verify With JWT in Header") Sending request with JWT in the header: curl -i "http://127.0.0.1:9080/get" -H "jwt-auth-header: ${jwt_token}" You should receive an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "Jwt-Auth-Header": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU", ... }, ...} #### Verify With JWT in Query String[​](https://docs.api7.ai/hub/jwt-auth/#verify-with-jwt-in-query-string "Direct link to Verify With JWT in Query String") Sending request with JWT in the query string: curl -i "http://127.0.0.1:9080/get?jwt-query=${jwt_token}" You should receive an `HTTP/1.1 200 OK` response similar to the following: { "args": { "jwt-query": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU" }, "headers": { "Accept": "*/*", ... }, "origin": "127.0.0.1, 183.17.233.107", "url": "http://127.0.0.1/get?jwt-query=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTY5NTEyOTA0NH0.EiktFX7di_tBbspbjmqDKoWAD9JG39Wo_CAQ1LZ9voQ"} #### Verify With JWT in Cookie[​](https://docs.api7.ai/hub/jwt-auth/#verify-with-jwt-in-cookie "Direct link to Verify With JWT in Cookie") Sending request with JWT in the cookie: curl -i "http://127.0.0.1:9080/get" --cookie jwt-cookie=${jwt_token} You should receive an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "headers": { "Accept": "*/*", "Cookie": "jwt-cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU", ... }, ...} ### Manage Secrets in Environment Variables[​](https://docs.api7.ai/hub/jwt-auth/#manage-secrets-in-environment-variables "Direct link to Manage Secrets in Environment Variables") The following example demonstrates how to save `jwt-auth` consumer key to an environment variable and reference it in configuration. APISIX supports referencing system and user environment variables configured through the [NGINX `env` directive](https://nginx.org/en/docs/ngx_core_module.html#env) . Save the key to an environment variable: export JACK_JWT_SECRET=jack-hs256-secret-that-is-very-long tip If you are running APISIX in Docker, you should set the environment variable using the `-e` flag when starting the container. Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Create `jwt-auth` credential for the consumer and reference the environment variable: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-jwt-auth", "plugins": { "jwt-auth": { "key": "jack-key", "secret": "$env://JACK_JWT_SECRET" } } }' Create a route with `jwt-auth` enabled: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "jwt-route", "uri": "/get", "plugins": { "jwt-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' To issue a JWT for `jack`, you could use [JWT.io's JWT encoder](https://jwt.io/) or other utilities. If you are using [JWT.io's JWT encoder](https://jwt.io/) , do the following: * Fill in `HS256` as the algorithm. * Update the secret in the **Valid secret** section to be `jack-hs256-secret-that-is-very-long`. * Update payload with consumer key `jack-key`; and add `exp` or `nbf` in UNIX timestamp. note If you are using API7 Enterprise, the requirement of `exp` or `nbf` is not mandatory. You can optionally include these claims and use the `claims_to_verify` parameter to configure which claim to verify. Your payload should look similar to the following: { "key": "jack-key", "nbf": 1729132271} Copy the generated JWT and save to a variable: export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU Sending request with JWT in the header: curl -i "http://127.0.0.1:9080/get" -H "Authorization: ${jwt_token}" You should receive an `HTTP/1.1 200 OK` response. ### Manage Secrets in Secret Manager[​](https://docs.api7.ai/hub/jwt-auth/#manage-secrets-in-secret-manager "Direct link to Manage Secrets in Secret Manager") The following example demonstrates how to manage `jwt-auth` consumer key in [HashiCorp Vault](https://www.vaultproject.io/) and reference it in plugin configuration. Start a Vault development server in Docker: docker run -d \ --name vault \ -p 8200:8200 \ --cap-add IPC_LOCK \ -e VAULT_DEV_ROOT_TOKEN_ID=root \ -e VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200 \ vault:1.9.0 \ vault server -dev APISIX currently supports [Vault KV engine version 1](https://developer.hashicorp.com/vault/docs/secrets/kv#kv-version-1) . Enable it in Vault: docker exec -i vault sh -c "VAULT_TOKEN='root' VAULT_ADDR='http://0.0.0.0:8200' vault secrets enable -path=kv -version=1 kv" You should see a response similar to the following: Success! Enabled the kv secrets engine at: kv/ Create a [secret](https://docs.api7.ai/apisix/key-concepts/secrets) and configure the Vault address and other connection information: curl "http://127.0.0.1:9180/apisix/admin/secrets/vault/jwt" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "uri": "https://127.0.0.1:8200", "prefix": "kv/apisix", "token": "root" }' ❶ Adjust the Vault address accordingly. Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Create `jwt-auth` credential for the consumer and reference the secret: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-jwt-auth", "plugins": { "jwt-auth": { "key": "jwt-vault-key", "secret": "$secret://vault/jwt/jack/jwt-secret" } } }' Create a route with `jwt-auth` enabled: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "jwt-route", "uri": "/get", "plugins": { "jwt-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Set `jwt-auth` key value to be `vault-hs256-secret-that-is-very-long` in Vault: docker exec -i vault sh -c "VAULT_TOKEN='root' VAULT_ADDR='http://0.0.0.0:8200' vault kv put kv/apisix/jack jwt-secret=vault-hs256-secret-that-is-very-long" You should see a response similar to the following: Success! Data written to: kv/apisix/jack To issue a JWT, you could use [JWT.io's JWT encoder](https://jwt.io/) or other utilities. If you are using [JWT.io's JWT encoder](https://jwt.io/) , do the following: * Fill in `HS256` as the algorithm. * Update the secret in the **Valid secret** section to be `vault-hs256-secret-that-is-very-long`. * Update payload with consumer key `jwt-vault-key`; and add `exp` or `nbf` in UNIX timestamp. note If you are using API7 Enterprise, the requirement of `exp` or `nbf` is not mandatory. You can optionally include these claims and use the `claims_to_verify` parameter to configure which claim to verify. Your payload should look similar to the following: { "key": "jwt-vault-key", "nbf": 1729132271} Copy the generated JWT and save to a variable: export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqd3QtdmF1bHQta2V5IiwibmJmIjoxNzI5MTMyMjcxfQ.i2pLj7QcQvnlSjB7iV5V522tIV43boQRtee7L0rwlkQ Send a request with the token in the header: curl -i "http://127.0.0.1:9080/get" -H "Authorization: ${jwt_token}" You should receive an `HTTP/1.1 200 OK` response. ### Sign JWT with RS256 Algorithm[​](https://docs.api7.ai/hub/jwt-auth/#sign-jwt-with-rs256-algorithm "Direct link to Sign JWT with RS256 Algorithm") The following example demonstrates how you can use asymmetric algorithms, such as RS256, to sign and validate JWT when implementing JWT for consumer authentication. You will be generating RSA key pairs using [openssl](https://openssl-library.org/source/) and generating JWT using [JWT.io](https://jwt.io/) to better understand the composition of JWT. Generate a 2048-bit RSA private key and extract the corresponding public key in PEM format: openssl genrsa -out jwt-rsa256-private.pem 2048openssl rsa -in jwt-rsa256-private.pem -pubout -out jwt-rsa256-public.pem You should see `jwt-rsa256-private.pem` and `jwt-rsa256-public.pem` generated in your current working directory. Visit [JWT.io's JWT encoder](https://jwt.io/) and do the following: * Fill in `RS256` as the algorithm. * Copy and paste the private key content into the **SIGN JWT: PRIVATE KEY** section. * Update payload with consumer key `jack-key`; and add `exp` or `nbf` in UNIX timestamp. Your payload should look similar to the following: { "key": "jack-key", "nbf": 1729132271} note If you are using API7 Enterprise, the requirement of `exp` or `nbf` is not mandatory. You can optionally include these claims and use the `claims_to_verify` parameter to configure which claim to verify. Copy the generated JWT and save to a variable: export jwt_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.K-I13em84kAcyH1jfIJl7ls_4jlwg1GzEzo5_xrDu-3wt3Xa3irS6naUsWpxX-a-hmcZZxRa9zqunqQjUP4kvn5e3xg2f_KyCR-_ZbwqYEPk3bXeFV1l4iypv6z5L7W1Niharun-dpMU03b1Tz64vhFx6UwxNL5UIZ7bunDAo_BXZ7Xe8rFhNHvIHyBFsDEXIBgx8lNYMq8QJk3iKxZhZZ5Om7lgYjOOKRgew4WkhBAY0v1AkO77nTlvSK0OEeeiwhkROyntggyx-S-U222ykMQ6mBLxkP4Cq5qHwXD8AUcLk5mhEij-3QhboYnt7yhKeZ3wDSpcjDvvL2aasC25ng Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Create `jwt-auth` credential for the consumer and configure the RSA keys: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-jwt-auth", "plugins": { "jwt-auth": { "key": "jack-key", "algorithm": "RS256", "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoTxe7ZPycrEP0SK4OBA2\n0OUQsDN9gSFSHVvx/t++nZNrFxzZnV6q6/TRsihNXUIgwaOu5icFlIcxPL9Mf9UJ\na5/XCQExp1TxpuSmjkhIFAJ/x5zXrC8SGTztP3SjkhYnQO9PKVXI6ljwgakVCfpl\numuTYqI+ev7e45NdK8gJoJxPp8bPMdf8/nHfLXZuqhO/btrDg1x+j7frDNrEw+6B\nCK2SsuypmYN+LwHfaH4Of7MQFk3LNIxyBz0mdbsKJBzp360rbWnQeauWtDymZxLT\nATRNBVyl3nCNsURRTkc7eyknLaDt2N5xTIoUGHTUFYSdE68QWmukYMVGcEHEEPkp\naQIDAQAB\n-----END PUBLIC KEY-----" } } }' ❶ Configure the consumer key to be `jack-key`. ❷ Configure the JWT signing algorithm to be `RS256`. ❸ Configure the RSA public key. tip You should add a newline character after the opening line and before the closing line, for example `-----BEGIN PUBLIC KEY-----\n......\n-----END PUBLIC KEY-----`. The key content can be directly concatenated. Create a route with the `jwt-auth` plugin: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "jwt-route", "uri": "/headers", "plugins": { "jwt-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' To verify, send a request to the route with the JWT in the `Authorization` header: curl -i "http://127.0.0.1:9080/headers" -H "Authorization: ${jwt_token}" You should receive an `HTTP/1.1 200 OK` response. ### Add Consumer Custom ID to Header[​](https://docs.api7.ai/hub/jwt-auth/#add-consumer-custom-id-to-header "Direct link to Add Consumer Custom ID to Header") The following example demonstrates how you can attach a consumer custom ID to authenticated request in the `Consumer-Custom-Id` header, which can be used to implement additional logics as needed. Create a consumer `jack` with a custom ID label: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack", "labels": { "custom_id": "495aec6a" } }' Create `jwt-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-jwt-auth", "plugins": { "jwt-auth": { "key": "jack-key", "secret": "jack-hs256-secret-that-is-very-long" } } }' Create a route with `jwt-auth`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "jwt-auth-route", "uri": "/anything", "plugins": { "jwt-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' To issue a JWT for `jack`, you could use [JWT.io's JWT encoder](https://jwt.io/) or other utilities. If you are using [JWT.io's JWT encoder](https://jwt.io/) , do the following: * Fill in `HS256` as the algorithm. * Update the secret in the **Valid secret** section to be `jack-hs256-secret-that-is-very-long`. * Update payload with consumer key `jack-key`; and add `exp` or `nbf` in UNIX timestamp. note If you are using API7 Enterprise, the requirement of `exp` or `nbf` is not mandatory. You can optionally include these claims and use the `claims_to_verify` parameter to configure which claim to verify. Your payload should look similar to the following: { "key": "jack-key", "nbf": 1729132271} Copy the generated JWT and save to a variable: export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU To verify, send a request to the route with the JWT in the `Authorization` header: curl -i "http://127.0.0.1:9080/headers" -H "Authorization: ${jwt_token}" You should see an `HTTP/1.1 200 OK` response similar to the following: { "headers": { "Accept": "*/*", "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-6873b19d-329331db76e5e7194c942b47", "X-Consumer-Custom-Id": "495aec6a", "X-Consumer-Username": "jack", "X-Credential-Identifier": "cred-jack-jwt-auth", "X-Forwarded-Host": "127.0.0.1" }} If you would like to attach more consumer custom headers to authenticated requests, see the [`attach-consumer-label`](https://docs.api7.ai/hub/attach-consumer-label) plugin. ### Rate Limit with Anonymous Consumer[​](https://docs.api7.ai/hub/jwt-auth/#rate-limit-with-anonymous-consumer "Direct link to Rate Limit with Anonymous Consumer") The following example demonstrates how you can configure different rate limiting policies by regular and anonymous consumers, where the anonymous consumer does not need to authenticate and has less quota. Create a regular consumer `jack` and configure the `limit-count` plugin to allow for a quota of 3 within a 30-second window: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack", "plugins": { "limit-count": { "count": 3, "time_window": 30, "rejected_code": 429 } } }' Create the `jwt-auth` credential for the consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-jwt-auth", "plugins": { "jwt-auth": { "key": "jack-key", "secret": "jack-hs256-secret-that-is-very-long" } } }' Create an anonymous user `anonymous` and configure the `limit-count` plugin to allow for a quota of 1 within a 30-second window: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "anonymous", "plugins": { "limit-count": { "count": 1, "time_window": 30, "rejected_code": 429 } } }' Create a route and configure the `jwt-auth` plugin to accept anonymous consumer `anonymous` from bypassing the authentication: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "jwt-auth-route", "uri": "/anything", "plugins": { "jwt-auth": { "anonymous_consumer": "anonymous" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' To issue a JWT for `jack`, you could use [JWT.io's JWT encoder](https://jwt.io/) or other utilities. If you are using [JWT.io's JWT encoder](https://jwt.io/) , do the following: * Fill in `HS256` as the algorithm. * Update the secret in the **Valid secret** section to be `jack-hs256-secret-that-is-very-long`. * Update payload with consumer key `jack-key`; and add `exp` or `nbf` in UNIX timestamp. note If you are using API7 Enterprise, the requirement of `exp` or `nbf` is not mandatory. You can optionally include these claims and use the `claims_to_verify` parameter to configure which claim to verify. Your payload should look similar to the following: { "key": "jack-key", "nbf": 1729132271} Copy the generated JWT and save to a variable: export jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJqYWNrLWtleSIsIm5iZiI6MTcyOTEzMjI3MX0.UEPXy5jpid624T1XpfjM0PLY73LZPjV3Qt8yZ92kVuU To verify the rate limiting, send five consecutive requests with `jack`'s JWT: resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H "Authorization: ${jwt_token}" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that out of the 5 requests, 3 requests were successful (status code 200) while the others were rejected (status code 429). 200: 3, 429: 2 Send five anonymous requests: resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that only one request was successful: 200: 1, 429: 4 * [Examples](https://docs.api7.ai/hub/jwt-auth/#examples) * [Use JWT for Consumer Authentication](https://docs.api7.ai/hub/jwt-auth/#use-jwt-for-consumer-authentication) * [Carry JWT in Request Header, Query String, or Cookie](https://docs.api7.ai/hub/jwt-auth/#carry-jwt-in-request-header-query-string-or-cookie) * [Manage Secrets in Environment Variables](https://docs.api7.ai/hub/jwt-auth/#manage-secrets-in-environment-variables) * [Manage Secrets in Secret Manager](https://docs.api7.ai/hub/jwt-auth/#manage-secrets-in-secret-manager) * [Sign JWT with RS256 Algorithm](https://docs.api7.ai/hub/jwt-auth/#sign-jwt-with-rs256-algorithm) * [Add Consumer Custom ID to Header](https://docs.api7.ai/hub/jwt-auth/#add-consumer-custom-id-to-header) * [Rate Limit with Anonymous Consumer](https://docs.api7.ai/hub/jwt-auth/#rate-limit-with-anonymous-consumer) --- # Data Mask | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/data-mask/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `data-mask` plugin masks sensitive information in request headers, bodies, and URL queries when using logging plugins. Note that it does not modify the actual request or response traffic. To mask sensitive information in the gateway’s access log, see [Mask Sensitive Data in Access Log](https://docs.api7.ai/enterprise/best-practices/data-masking-access-log) . About Plugin Execution Order The plugin can be configured on routes, services, or as a global plugin. However, be aware that [global plugins are always executed before route- or service-level plugins](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-order) , so data masking may occur after logging. For instance, if a logging plugin is configured globally while `data-mask` is applied at the route level, requests will be logged before masking occurs, and sensitive data will appear in plaintext. To ensure the intended behavior, it is recommended to configure both plugins at the same level: 1. Both at the global level (recommended if suitable for your use case) 2. Both at the route or service level Examples[​](https://docs.api7.ai/hub/data-mask/#examples "Direct link to Examples") ------------------------------------------------------------------------------------ The examples below demonstrate how you can use the `data-mask` plugin for different scenarios. While all examples use the `file-logger` plugin for logging, the plugin is used only to demonstrate the results of data masking. Select the logging plugin that best suits your environment. ### Mask Sensitive Information in URL Query[​](https://docs.api7.ai/hub/data-mask/#mask-sensitive-information-in-url-query "Direct link to Mask Sensitive Information in URL Query") The following example demonstrates how you can mask sensitive information in the request URL queries, before the request is logged to a local file by the `file-logger` plugin. Create a route with the `file-logger` plugin to log requests and the `data-mask` plugin with three data masking rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "data-mask-route", "uri": "/anything", "plugins": { "data-mask": { "request": [ { "action": "remove", "name": "password", "type": "query" }, { "action": "replace", "name": "token", "type": "query", "value": "*****" }, { "action": "regex", "name": "card", "regex": "(\\d+)\\-\\d+\\-\\d+\\-(\\d+)", "type": "query", "value": "$1-****-****-$2" } ] }, "file-logger": { "path": "/tmp/mask-query.log" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ data masking rule to remove `password` URL query from the request. ❷ data masking rule to replace the value of `token` URL query with `*****`. ❸ data masking rule that matches card number in the URL query with RegEx and mask the middle portion of the card number. ❹ path to the log file on the filesystem where logs should be saved. Send a request to the route with sensitive information in URL queries: curl -i "http://127.0.0.1:9080/anything?password=abc&token=xyz&card=1234-1234-1234-1234" You should receive an `HTTP/1.1 200 OK` response. Navigating to the `/tmp/mask-query.log` file and examining the log content, you should see a log entry similar to the following: { "request": { "uri": "/anything?token=*****&card=1234-****-****-1234", "method": "GET", "url": "http://127.0.0.1:9080/anything?token=*****&card=1234-****-****-1234", "querystring": { "token": "*****", "card": "1234-****-****-1234" } }} ### Mask Sensitive Information in Request Headers[​](https://docs.api7.ai/hub/data-mask/#mask-sensitive-information-in-request-headers "Direct link to Mask Sensitive Information in Request Headers") The following example demonstrates how you can mask sensitive information in request headers, before the request is logged to a local file by the `file-logger` plugin. Create a route with the `file-logger` plugin to log requests and the `data-mask` plugin with three data masking rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "data-mask-route", "uri": "/anything", "plugins": { "data-mask": { "request": [ { "action": "remove", "name": "password", "type": "header" }, { "action": "replace", "name": "token", "type": "header", "value": "*****" }, { "action": "regex", "name": "card", "regex": "(\\d+)\\-\\d+\\-\\d+\\-(\\d+)", "type": "header", "value": "$1-****-****-$2" } ] }, "file-logger": { "path": "/tmp/mask-header.log" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ data masking rule to remove `password` header from the request. ❷ data masking rule to replace the value of `token` request header with `*****`. ❸ data masking rule that matches card number in the request header with RegEx and mask the middle portion of the card number. ❹ path to the log file on the filesystem where logs should be saved. Send a POST request to the route with sensitive information in headers: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "password: abc" \ -H "token: xyz" \ -H "card: 1234-1234-1234-1234" You should receive an `HTTP/1.1 200 OK` response. Navigating to the `/tmp/mask-header.log` file and examining the log content, you should see a log entry similar to the following: { "request": { "uri": "/anything", "method": "GET", "url": "http://127.0.0.1:9080/anything", "headers": { "user-agent": "curl/8.6.0", "token": "*****", "card": "1234-****-****-1234" } }} ### Mask Sensitive Information in URL-Encoded Request Bodies[​](https://docs.api7.ai/hub/data-mask/#mask-sensitive-information-in-url-encoded-request-bodies "Direct link to Mask Sensitive Information in URL-Encoded Request Bodies") The following example demonstrates how you can mask sensitive information in URL-encoded request bodies, before the request is logged to a local file by the `file-logger` plugin. Create a route with the `file-logger` plugin to log requests and the `data-mask` plugin with three data masking rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "data-mask-route", "uri": "/anything", "plugins": { "data-mask": { "request": [ { "action": "remove", "body_format": "urlencoded", "name": "password", "type": "body" }, { "action": "replace", "body_format": "urlencoded", "name": "token", "type": "body", "value": "*****" }, { "action": "regex", "body_format": "urlencoded", "name": "card", "regex": "(\\d+)\\-\\d+\\-\\d+\\-(\\d+)", "type": "body", "value": "$1-****-****-$2" } ] }, "file-logger": { "include_req_body": true, "path": "/tmp/mask-urlencoded-body.log" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ data masking rule to remove `password` information from the request body. ❷ data masking rule to replace `token` information in the request body with `*****`. ❸ data masking rule that matches card number in the request body with RegEx and mask the middle portion of the card number. ❹ path to the log file on the filesystem where logs should be saved. Send a request to the route: curl -i "http://127.0.0.1:9080/anything" \ --data-urlencode "password=abc" \ --data-urlencode "token=xyz" \ --data-urlencode "card=1234-1234-1234-1234" You should receive an `HTTP/1.1 200 OK` response. Navigating to the `/tmp/mask-urlencoded-body.log` file and examining the log content, you should see a log entry similar to the following: { "request": { "uri": "/anything", "body": "token=*****&card=1234-****-****-1234", "method": "POST", "url": "http://127.0.0.1:9080/anything" }} ### Mask Sensitive Information in JSON-Encoded Request Bodies[​](https://docs.api7.ai/hub/data-mask/#mask-sensitive-information-in-json-encoded-request-bodies "Direct link to Mask Sensitive Information in JSON-Encoded Request Bodies") The following example demonstrates how you can mask sensitive information in JSON-encoded request bodies using [JSON path](https://goessner.net/articles/JsonPath) syntax in the plugin to look for the target field, before the request is logged to a local file by the `file-logger` plugin. Create a route with the `file-logger` plugin to log requests and the `data-mask` plugin with three data masking rules: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "data-mask-route", "uri": "/anything", "plugins": { "data-mask": { "request": [ { "action": "remove", "body_format": "json", "name": "$.password", "type": "body" }, { "action": "replace", "body_format": "json", "name": "users[*].token", "type": "body", "value": "*****" }, { "action": "regex", "body_format": "json", "name": "$.users[*].credit.card", "regex": "(\\d+)\\-\\d+\\-\\d+\\-(\\d+)", "type": "body", "value": "$1-****-****-$2" } ] }, "file-logger": { "path": "/tmp/mask-json-body.log" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ data masking rule to remove `password` information from the request body. ❷ data masking rule to replace `token` information in the request body with `*****`. ❸ data masking rule that matches card number in the request body with RegEx and mask the middle portion of the card number. ❹ path to the log file on the filesystem where logs should be saved. Send a request to the route with sensitive information in the request body: curl -i "http://127.0.0.1:9080/anything" -X POST -d '{ "password": "abc", "users": [ { "token": "xyz", "credit": { "card": "1234-1234-1234-1234" } }, { "token": "xyz", "credit": { "card": "1234-1234-1234-1234" } } ]}' You should receive an `HTTP/1.1 200 OK` response. Navigating to the `/tmp/mask-json-body.log` file and examining the log content, you should see a log entry similar to the following: { "request": { "uri": "/anything", "body": "{\"users\":[{\"token\":\"*****\",\"credit\":{\"card\":\"1234-****-****-1234\"}},{\"token\":\"*****\",\"credit\":{\"card\":\"1234-****-****-1234\"}}]}", "method": "POST", "url": "http://127.0.0.1:9080/anything" }} * [Examples](https://docs.api7.ai/hub/data-mask/#examples) * [Mask Sensitive Information in URL Query](https://docs.api7.ai/hub/data-mask/#mask-sensitive-information-in-url-query) * [Mask Sensitive Information in Request Headers](https://docs.api7.ai/hub/data-mask/#mask-sensitive-information-in-request-headers) * [Mask Sensitive Information in URL-Encoded Request Bodies](https://docs.api7.ai/hub/data-mask/#mask-sensitive-information-in-url-encoded-request-bodies) * [Mask Sensitive Information in JSON-Encoded Request Bodies](https://docs.api7.ai/hub/data-mask/#mask-sensitive-information-in-json-encoded-request-bodies) --- # Limit Count Advanced | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/limit-count-advanced/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `limit-count-advanced` plugin uses a fixed or sliding window algorithm to limit the rate of requests by the number of requests within a given time interval. Requests exceeding the configured quota will be rejected. Specifically: * Fixed window algorithm tracks requests in non-overlapping time intervals. If the request count exceeds the quota in any interval, excess requests are immediately rejected until the next time window begins. * Sliding window algorithm tracks requests in overlapping intervals, smoothing out the rate limit by counting recent requests within the last configured time period, regardless of when the interval began. This method reduces traffic spikes and is more effective at evenly distributing requests over time. Additionally, you may also see the following rate limiting response headers, the name of which can be customized using [plugin metadata](https://docs.api7.ai/hub/limit-count-advanced/#customize-rate-limiting-headers) : * `X-RateLimit-Limit`: the total quota * `X-RateLimit-Remaining`: the remaining quota * `X-RateLimit-Reset`: number of seconds left for the counter to reset Occasionally, you might observe a small negative value for the `X-RateLimit-Remaining`. This is acceptable as the sliding window algorithm is an approximation. Local vs Redis Rate Limiting[​](https://docs.api7.ai/hub/limit-count-advanced/#local-vs-redis-rate-limiting "Direct link to Local vs Redis Rate Limiting") ----------------------------------------------------------------------------------------------------------------------------------------------------------- The `limit-count-advanced` plugin supports two modes of rate limiting: * **Local rate limiting**: Limits are enforced independently on each gateway instance. Each instance maintains its own counters, so the effective limit is roughly (limit × number of instances) when traffic is spread across instances. This is the default when no `policy` is set or when `policy` is `local`. * **Redis-based rate limiting**: Limits are shared across all gateway instances through Redis. All instances share the same quota, so the configured limit applies to all gateway instances. Examples[​](https://docs.api7.ai/hub/limit-count-advanced/#examples "Direct link to Examples") ----------------------------------------------------------------------------------------------- The plugin supports sliding window algorithm in addition to the [`limit-count`](https://docs.api7.ai/hub/limit-count) plugin features. Please refer to [`limit-count`](https://docs.api7.ai/hub/limit-count#examples) plugin for fixed window examples, which can also be configured in `limit-count-advanced`. The examples below demonstrate how you can use `limit-count-advanced` to rate limit using sliding window algorithm. ### Rate Limit with Local Counters[​](https://docs.api7.ai/hub/limit-count-advanced/#rate-limit-with-local-counters "Direct link to Rate Limit with Local Counters") The following example demonstrates how you can configure `limit-count-advanced` to use the sliding window algorithm for rate limiting on a route, using the counter in the gateway. Note that each gateway instance has its own counter and independent quota. If you have multiple gateway instances that need to share the same quota, please see [share quota among gateways with a Redis server](https://docs.api7.ai/hub/limit-count-advanced/#share-quota-among-apisix-nodes-with-a-redis-server) . Create a route with `limit-count-advanced` plugin that allows for a quota of 5 within a 10-second sliding window per remote address: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "limit-count-sliding-route", "uri": "/get", "plugins": { "limit-count-advanced": { "policy": "local", "count": 5, "time_window": 10, "rejected_code": 429, "key_type": "var", "key": "remote_addr", "window_type": "sliding" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Generate 7 requests to the route every other second: for i in $(seq 7); do (curl -I "http://127.0.0.1:9080/get" &) sleep 1done You should receive `HTTP/1.1 200 OK` responses for most requests, with the remainder being `HTTP 429 Too Many Requests` responses. The specific number rejected depends on when the first request is sent. ### Share Quota Among Gateways with a Redis Server[​](https://docs.api7.ai/hub/limit-count-advanced/#share-quota-among-gateways-with-a-redis-server "Direct link to Share Quota Among Gateways with a Redis Server") The following example demonstrates the rate limiting of requests across multiple gateway nodes with a Redis server using the sliding window algorithm, such that different gateway nodes share the same rate limiting quota. Create a route with the following configurations in the gateway group: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "limit-count-sliding-route", "uri": "/get", "plugins": { "limit-count-advanced": { "count": 1, "time_window": 30, "rejected_code": 429, "key": "remote_addr", "policy": "redis", "redis_host": "192.168.xxx.xxx", "redis_port": 6379, "redis_password": "p@ssw0rd", "redis_database": 1, "window_type": "sliding", "sync_interval": 0.2 } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ `policy`: Set to `redis` to use a Redis instance for rate limiting. ❷ `redis_host`: Set to Redis instance IP address. ❸ `redis_port`: Set to Redis instance listening port. ❹ `redis_password`: Set to the password of the Redis instance, if any. ❺ `redis_database`: Set to the database number in the Redis instance. ❻ `window_type`: Set the window type to sliding window. ❼ `sync_interval`: Set the synchronization interval (optional). Generate 7 requests to the route every other second: for i in $(seq 7); do (curl -I "http://127.0.0.1:9080/get" &) sleep 1done You should receive `HTTP/1.1 200 OK` responses for most requests, with the remainder being `HTTP 429 Too Many Requests` responses. The specific number rejected depends on when the first request is sent. This verifies routes configured in different gateway nodes share the same quota. ### Share Quota Among Gateway Nodes with a Redis Cluster[​](https://docs.api7.ai/hub/limit-count-advanced/#share-quota-among-gateway-nodes-with-a-redis-cluster "Direct link to Share Quota Among Gateway Nodes with a Redis Cluster") The following example demonstrates how you can configure `limit-count-advanced` to use the sliding window algorithm and apply the same quota across multiple gateway nodes, such that different gateway nodes share the same rate limiting quota. Ensure that your Redis instances are running in [cluster mode](https://redis.io/docs/management/scaling/#create-and-use-a-redis-cluster) . A minimum of two nodes are required for the `limit-count-advanced` plugin configurations. Create a route with the following configurations in the gateway group: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "limit-count-route", "uri": "/get", "plugins": { "limit-count-advanced": { "count": 1, "time_window": 30, "rejected_code": 429, "key": "remote_addr", "policy": "redis-cluster", "redis_cluster_nodes": [ "192.168.xxx.xxx:6379", "192.168.xxx.xxx:16379" ], "redis_password": "p@ssw0rd", "redis_cluster_name": "redis-cluster-1", "redis_cluster_ssl": true, "window_type": "sliding", "sync_interval": 0.2 } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ `policy`: Set to `redis-cluster` to use a Redis cluster for rate limiting. ❷ `redis_cluster_nodes`: Set to Redis node addresses in the Redis cluster. ❸ `redis_password`: Set to the password of the Redis cluster, if any. ❹ `redis_cluster_name`: Set to the Redis cluster name. ➎ `redis_cluster_ssl`: Enable SSL/TLS communication with Redis cluster. ❻ `window_type`: Set the window type to sliding window. ❼ `sync_interval`: Set the synchronization interval (optional). Generate 7 requests to the route every other second: for i in $(seq 7); do (curl -I "http://127.0.0.1:9080/get" &) sleep 1done You should receive `HTTP/1.1 200 OK` responses for most requests, with the remainder being `HTTP 429 Too Many Requests` responses. The specific number rejected depends on when the first request is sent. This verifies routes configured in different gateway nodes share the same quota. ### Customize Rate Limiting Headers[​](https://docs.api7.ai/hub/limit-count-advanced/#customize-rate-limiting-headers "Direct link to Customize Rate Limiting Headers") The following example demonstrates how you can use plugin metadata to customize the rate limiting response header names, which are by default `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`. Configure the plugin metadata for this plugin and update the headers: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/limit-count" -X PUT -d '{ "log_format": { "limit_header": "X-Custom-RateLimit-Limit", "remaining_header": "X-Custom-RateLimit-Remaining", "reset_header": "X-Custom-RateLimit-Reset" }}' Create a route with `limit-count-advanced` plugin that allows for a quota of 1 within a 30-second window per remote address: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "limit-count-advanced-route", "uri": "/get", "plugins": { "limit-count-advanced": { "policy": "local", "count": 1, "time_window": 30, "rejected_code": 429, "key_type": "var", "key": "remote_addr", "window_type": "sliding" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Send a request to verify: curl -i "http://127.0.0.1:9080/get" You should receive an `HTTP/1.1 200 OK` response and see the following headers: X-Custom-RateLimit-Limit: 1X-Custom-RateLimit-Remaining: 0X-Custom-RateLimit-Reset: 28 ### Share Quota Among Gateway Nodes with Redis Sentinel[​](https://docs.api7.ai/hub/limit-count-advanced/#share-quota-among-gateway-nodes-with-redis-sentinel "Direct link to Share Quota Among Gateway Nodes with Redis Sentinel") The following example demonstrates how you can use `limit-count-advanced` plugin with Redis Sentinel policy for rate limiting. Ensure that your Redis instances are running in [Sentinel mode](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/) . Create a route with the following configurations in the gateway group: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "limit-count-route", "uri": "/get", "plugins": { "limit-count-advanced": { "count": 1, "time_window": 30, "rejected_code": 429, "key": "remote_addr", "policy": "redis-sentinel", "redis_sentinels": [ {"host": "127.0.0.1", "port": 26379}, {"host": "127.0.10.1", "port": 26379}, {"host": "127.0.101.1", "port": 26379} ], "redis_master_name": "mymaster", "redis_role": "master", "sentinel_username": "admin", "sentinel_password": "admin-password" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ `policy`: Set to `redis-sentinel` to use a Redis in sentinel mode for rate limiting. ❷ `redis_sentinels`: Configure a list of Sentinel node addresses (host and port). ❸ `redis_master_name`: Configure the name of the Redis master group that Sentinels are monitoring. ❹ `redis_role`: Set to `master` to connect to the current Redis master. ❺ `sentinel_username`: Configure the username used to authenticate with Redis Sentinel. ❻ `sentinel_password`: Configure the password used to authenticate with Redis Sentinel. Generate 5 requests to the route every other second: for i in $(seq 5); do (curl -I "http://127.0.0.1:9080/get" &) sleep 1done You should receive an `HTTP/1.1 200 OK` response for one request in a 30-second window, while the rest being `HTTP 429 Too Many Requests` responses. ### Rate Limit by Rules[​](https://docs.api7.ai/hub/limit-count-advanced/#rate-limit-by-rules "Direct link to Rate Limit by Rules") The following example demonstrates how you can configure `limit-count-advanced` to apply different rate-limiting rules (available from API7 Enterprise 3.8.17) based on request attributes. In this example, rate limits are applied based on HTTP header values that represent the caller’s access tier. Note that all rules are applied sequentially. If a configured key does not exist, the corresponding rule will be skipped. tip In addition to HTTP headers, you can also base rules on other [built-in variables](https://docs.api7.ai/enterprise/reference/built-in-variables) to implement more flexible and fine-grained rate-limiting strategies. Create a route with the `limit-count-advanced` plugin that applies different rate limits based on request headers, allowing requests to be rate limited per subscription (`X-Subscription-ID`) and enforcing a stricter limit for trial users (`X-Trial-ID`): curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "limit-count-rules-route", "uri": "/get", "plugins": { "limit-count-advanced": { "policy": "local", "rejected_code": 429, "rules": [ { "key": "${http_x_subscription_id}", "count": "${http_x_custom_count ?? 5}", "time_window": 60 }, { "key": "${http_x_trial_id}", "count": 1, "time_window": 60 } ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Use the value of the `X-Subscription-ID` request header as the rate-limiting key. ❷ Set the request limit dynamically based on the `X-Custom-Count` header. If the header is not provided, a default count of 5 requests is applied. ❸ Use the value of the `X-Trial-ID` request header as the rate-limiting key. To verify rate limiting, generate 7 requests to the route with the same subscription ID: resp=$(seq 7 | xargs -I{} curl "http://127.0.0.1:9080/get" -H "X-Subscription-ID: sub-123456789" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that the default count of 5 requests is applied when `X-Custom-Count` header is not provided: 200: 5, 429: 2 Wait for the time window to reset. Generate 5 requests to the route with the same subscription ID and set the `X-Custom-Count` header to 3: resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/get" -H "X-Subscription-ID: sub-123456789" -H "X-Custom-Count: 3" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that the count of 3 requests from the `X-Custom-Count` header is applied: 200: 3, 429: 2 Finally, generate 3 requests to the route with the same trial ID: resp=$(seq 3 | xargs -I{} curl "http://127.0.0.1:9080/get" -H "X-Trial-ID: trial-123456789" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that the count of 1 request from the second rule is applied: 200: 1, 429: 2 * [Local vs Redis Rate Limiting](https://docs.api7.ai/hub/limit-count-advanced/#local-vs-redis-rate-limiting) * [Examples](https://docs.api7.ai/hub/limit-count-advanced/#examples) * [Rate Limit with Local Counters](https://docs.api7.ai/hub/limit-count-advanced/#rate-limit-with-local-counters) * [Share Quota Among Gateways with a Redis Server](https://docs.api7.ai/hub/limit-count-advanced/#share-quota-among-gateways-with-a-redis-server) * [Share Quota Among Gateway Nodes with a Redis Cluster](https://docs.api7.ai/hub/limit-count-advanced/#share-quota-among-gateway-nodes-with-a-redis-cluster) * [Customize Rate Limiting Headers](https://docs.api7.ai/hub/limit-count-advanced/#customize-rate-limiting-headers) * [Share Quota Among Gateway Nodes with Redis Sentinel](https://docs.api7.ai/hub/limit-count-advanced/#share-quota-among-gateway-nodes-with-redis-sentinel) * [Rate Limit by Rules](https://docs.api7.ai/hub/limit-count-advanced/#rate-limit-by-rules) --- # Use Debug Mode | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page APISIX provides a debug mode to help developers better understand and troubleshoot the runtime behavior of the gateway. The complete configuration options can be found in [`debug.yaml`](https://github.com/apache/apisix/blob/master/conf/debug.yaml) . This guide will show you how to enable the debug mode to inspect what plugins are enabled on the requested route and how to add hooks to log input arguments and returned values of APISIX module's functions. Prerequisite(s)[​](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#prerequisites "Direct link to Prerequisite(s)") ---------------------------------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker/) . * Install [cURL](https://curl.se/) to send requests to APISIX for validation. * Follow the [Getting Started tutorial](https://docs.api7.ai/apisix/getting-started/) to start a new APISIX instance in Docker or on Kubernetes. Enable Basic Debug Mode[​](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#enable-basic-debug-mode "Direct link to Enable Basic Debug Mode") ------------------------------------------------------------------------------------------------------------------------------------------------------------------ Enabling the basic debug mode will allow you to inspect what plugins are enabled on the requested route in the `Apisix-Plugins` header. By default, the debug mode is turned off. To enable the debug mode, you can set `basic.enable` to `true` in your `debug.yaml` file. docker exec apisix-quickstart /bin/sh -c "echo 'basic: enable: true#END' > /usr/local/apisix/conf/debug.yaml" The `debug.yaml` file is loaded into memory at startup and monitored for changes at a regular interval, so that you do not need to manually reload APISIX after updating the file. info The `debug.yaml` configurations should end with `#END`, or else APISIX will not load the configurations. You can also use the `#END` flag as a breakpoint for APISIX to only load configurations up to the specified point. To verify, create a route without any plugin: * Admin API * ADC curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '{ "id": "getting-started-anything", "uri": "/anything", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' adc.yaml services: - name: httpbin Service routes: - uris: - /anything name: getting-started-anything upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to APISIX: adc sync -f adc.yaml Send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response and observe the following headers: Content-Type: application/jsonContent-Length: 390Connection: keep-aliveApisix-Plugins: no plugin... * Admin API * ADC Update the route with a plugin: curl -i "http://127.0.0.1:9180/apisix/admin/routes/getting-started-anything" -X PATCH -d '{ "plugins": { "limit-count": { "count": 5, "time_window": 10, "rejected_code": 429 } }}' Additionally, add a [global plugin](https://docs.api7.ai/apisix/key-concepts/plugin-global-rules) : curl -i "http://127.0.0.1:9180/apisix/admin/global_rules" -X PUT -d '{ "id": "global-prometheus", "plugins": { "prometheus":{} }}' Update the route with a plugin and add another [global plugin](https://docs.api7.ai/apisix/key-concepts/plugin-global-rules) : adc.yaml services: - name: httpbin Service routes: - uris: - /anything name: getting-started-anything plugins: limit-count: count: 5 time_window: 10 rejected_code: 429 upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1global_rules: prometheus: {} Synchronize the configuration to APISIX: adc sync -f adc.yaml Send another request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response with headers similar to the following: Content-Type: application/jsonContent-Length: 388Connection: keep-aliveX-RateLimit-Limit: 5X-RateLimit-Remaining: 4X-RateLimit-Reset: 10Apisix-Plugins: limit-count, prometheus... info If the plugin information cannot be included in a response header (e.g. L4 stream plugins), the debug information will be logged in the error log at the `warn` severity level. Configure Advanced Debug Mode[​](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#configure-advanced-debug-mode "Direct link to Configure Advanced Debug Mode") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ There are a few other advanced options in `debug.yaml` that you can configure. ### Log Function Input Arguments and Returned Values[​](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#log-function-input-arguments-and-returned-values "Direct link to Log Function Input Arguments and Returned Values") You can add hooks to [APISIX phases](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-lifecycle) to log input arguments and return values with the following configuration: debug.yaml basic: enable: truehook_conf: enable: true name: hook_phase log_level: warn is_print_input_args: true is_print_return_value: truehook_phase: apisix: - http_access_phase - http_header_filter_phase - http_body_filter_phase - http_log_phase#END ❶ Enable hook debug trace to log the target module function's input arguments or returned values. ❷ Name of module and function list. ❸ Severity level for input arguments and returned values in the error log. ❹ Log the input arguments. ❺ Log the return values. ❻ Name of module and function list. To verify, send a request to the route: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response and see the following information in the error log: call require("apisix").http_access_phase() args:{}call require("apisix").http_access_phase() return:{}call require("apisix").http_header_filter_phase() args:{}call require("apisix").http_header_filter_phase() return:{}call require("apisix").http_body_filter_phase() args:{}call require("apisix").http_body_filter_phase() return:{}call require("apisix").http_log_phase() args:{}call require("apisix").http_log_phase() return:{} ### Apply Advanced Debug Settings Dynamically[​](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#apply-advanced-debug-settings-dynamically "Direct link to Apply Advanced Debug Settings Dynamically") You can also log function input arguments and returned values dynamically when a certain header is present with the following configuration: debug.yaml docker exec apisix-quickstart /bin/sh -c "echo 'basic: enable: truehttp_filter: enable: true enable_header_name: X-APISIX-Dynamic-Debughook_conf: enable: true name: hook_phase log_level: warn is_print_input_args: true is_print_return_value: truehook_phase: apisix: - http_access_phase - http_header_filter_phase - http_body_filter_phase - http_log_phase#END ❶ Enable HTTP filter to dynamically apply advanced debug settings. ❷ Log input arguments and returned values only when the request has the `X-APISIX-Dynamic-Debug` header. To verify, send a request to the route without any header: curl -i "http://127.0.0.1:9080/anything" You should receive an `HTTP/1.1 200 OK` response but observe no debugging information in the error log. Send another request with the debug header: curl -i "http://127.0.0.1:9080/anything" -H "X-APISIX-Dynamic-Debug: 1" You should receive an `HTTP/1.1 200 OK` response and see the following information in the error log: call require("apisix").http_access_phase() args:{}call require("apisix").http_access_phase() return:{}call require("apisix").http_header_filter_phase() args:{}call require("apisix").http_header_filter_phase() return:{}call require("apisix").http_body_filter_phase() args:{}call require("apisix").http_body_filter_phase() return:{}call require("apisix").http_log_phase() args:{}call require("apisix").http_log_phase() return:{} Manage `debug.yaml` by Environments[​](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#manage-debugyaml-by-environments "Direct link to manage-debugyaml-by-environments") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ You may wish to have different `debug.yaml` files for each environment, such as using `conf/debug-dev.yaml` for development and `conf/debug-prod.yaml` for production. This can be done by setting the `APISIX_PROFILE` variable. For more details, see [Manage Configuration Files by Environments](https://docs.api7.ai/apisix/reference/configuration-files#manage-configuration-files-by-environments) . Next Steps[​](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#next-steps "Direct link to Next Steps") --------------------------------------------------------------------------------------------------------------------------- You have now learned how to leverage the debug mode in APISIX for troubleshooting and the various debugging options it offers. You can find the complete configuration options in [`debug.yaml`](https://github.com/apache/apisix/blob/master/conf/debug.yaml) . * [Prerequisite(s)](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#prerequisites) * [Enable Basic Debug Mode](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#enable-basic-debug-mode) * [Configure Advanced Debug Mode](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#configure-advanced-debug-mode) * [Log Function Input Arguments and Returned Values](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#log-function-input-arguments-and-returned-values) * [Apply Advanced Debug Settings Dynamically](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#apply-advanced-debug-settings-dynamically) * [Manage `debug.yaml` by Environments](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#manage-debugyaml-by-environments) * [Next Steps](https://docs.api7.ai/apisix/how-to-guide/troubleshooting/debug-mode/#next-steps) --- # Organization and RBAC | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Organization Management[​](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#organization-management "Direct link to Organization Management") ------------------------------------------------------------------------------------------------------------------------------------------------------------------- The organization module in API7 Enterprise includes users, roles, permission policies, licenses, audit, contact points, and settings. * **Users**: The built-in `Admin` user in API7 Enterprise can manage users within the organization, including assigning roles to users and other user management tasks. * **Roles**: The built-in `Super Admin` role is provided, and custom roles can be added or deleted. Roles assigned to users cannot be directly deleted; only unused roles can be removed. * **Permission Policies**: A built-in permission policy called `super-admin-permission-policy` is bound to the `Super Admin` role. This policy grants full access to all operations and resources within API7 Enterprise. The role can also be associated with custom roles. Built-in roles and policies are non-editable to ensure the security of the core system. Combining users, roles, and permission policies enables fine-grained [permission management](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries) , preventing privilege escalation and potential security vulnerabilities. * **Licenses**: This module displays important information, such as the validity, issuance date, and licensed CPU cores. * **Audit**: Detailed records of all user actions within API7 Enterprise are maintained. Each audit log contains a series of attributes, such as event, time, operator, resource ID, gateway group ID, and details of the audit logs. * **Contact Points**: This can be integrated with alert policies to configure Webhook and email alert triggers, enhancing the gateway's monitoring and response capabilities. * **Settings**: It includes configurations for API7 integrated authentication, SCIM provisioning, login options, and SMTP server settings. These features allow API7 Enterprise to manage users and permissions effectively, ensuring system security and stability. RBAC[​](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#rbac "Direct link to RBAC") ---------------------------------------------------------------------------------------------------------- RBAC (Role-Based Access Control) is an access management method where users are assigned roles to obtain permissions, enabling flexible and efficient access control. Besides the RBAC model, API7 Enterprise introduces a more flexible and powerful IAM (Identity and Access Management) policy model. It allows administrators to define specific policies, each consisting of a set of rules that specify the range of resources that users and roles can access. API7 Enterprise supports enabling SCIM (System for Cross-domain Identity Management) configuration, which allows synchronizing user and group information from the source identity provider (IdP) to API7 Enterprise. Paste the SCIM token generated on the API7 dashboard and save it in the IdP system. After configuring SCIM, any updates made to users in the connected Identity Provider (IdP) system—like adding or removing users—are automatically synchronized with API7 Enterprise. This saves users' efforts in user management across multiple systems, streamlining identity management and enhancing data consistency. API7 Enterprise automatically assigns roles to users imported from the IdP system based on relevant attributes (e.g., title, position, department). These roles are synchronized and refreshed upon user login. API7 Enterprise also supports SSO (Single Sign-On) role mapping. When administrators add an SSO login option, corresponding SSO role mapping rules can be created. Role mappings may include multiple rules, which together determine users' roles and access rights. Role mappings take precedence over manual role assignments. When role mapping is enabled, any manual changes to user roles will be overwritten during the user's next login. API7 Enterprise's role mapping supports four matching methods: Exact Match, Contain String, Exact Match in Array, and Contain String in Array. This allows for many-to-many mappings between internal roles and IdP roles. For example, when creating an SSO login option and setting a role mapping for the `Super Admin` internal role, the role attribute can be set to `Position` in the IdP system. If the matching method is set to "exact match" (`=`), and the role value is set to `Apps Engineer`, all users with the Apps Engineer position will be assigned the `Super Admin` role. Since role mapping is dynamically applied, if the position of an `Apps Engineer` in the IdP system changes to `Ops Engineer`, that user will lose the `Super Admin` role on their next login to API7 Enterprise. ![Organization and RBAC](https://static.api7.ai/uploads/2024/11/27/VuAT9Z2S_organization-and-rbac.png) Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#key-features "Direct link to Key Features") ---------------------------------------------------------------------------------------------------------------------------------- * Provides a flexible IAM policy model, supporting fine-grained permission configuration based on users, roles, resources, and environments. * Built-in support for SCIM configuration, enabling automatic synchronization of user and group information between mainstream IdP systems. * Supports dynamic role mapping, allowing user roles to be dynamically adjusted based on flexible rule configurations. * Suitable for multi-tenant, multi-team, and multi-department architectures, ensuring access control and data isolation between tenants. * Highly compatible with major cloud platforms (such as AWS, Azure, and GCP), while also supporting locally deployed IdP systems. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#use-cases "Direct link to Use Cases") ------------------------------------------------------------------------------------------------------------------------- ### Automate User Management[​](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#automate-user-management "Direct link to Automate User Management") API7 Enterprise supports enabling SCIM configuration, which, combined with organization and RBAC features, enables automatic synchronization and management of user information. Enterprises can import users from existing IdP systems and automatically assign roles based on user attributes (e.g., position, department, or project) in the IdP system. This approach not only improves system operation and management efficiency but also enhances security. In fast-changing business environments, roles and permissions may need frequent adjustments. For example, when an employee changes roles, the user information in the IdP system is updated. API7 Enterprise can automatically synchronize the role changes, reducing manual complexity and improving data consistency. ### Multi-department or Multi-team Management[​](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#multi-department-or-multi-team-management "Direct link to Multi-department or Multi-team Management") Through the organization's hierarchical structure, businesses can create independent permission management rules for different departments, subsidiaries, or project teams, ensuring that permissions do not interfere with one another. For example, financial data is only accessible to the finance team, while the development team can access and manage the code repository. With fine-grained permission management, enterprises can precisely define access rules for shared resources, ensuring that only authorized personnel can view or modify specific resources. RBAC provides flexible permission allocation, enabling businesses to adjust permission granularity according to actual needs. It allows for cross-department collaboration without compromising data security. By binding permissions to specific roles, businesses ensure that each employee can only access information within their responsibility scope, minimizing the risk of unauthorized access or errors. ### Achieve Fine-Grained Permission Access[​](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#achieve-fine-grained-permission-access "Direct link to Achieve Fine-Grained Permission Access") Compared to traditional broad permission allocations, the fine-grained control empowered by RBAC is more flexible and secure. For example, by defining role templates such as "view-only" and "admin," permissions are refined based on user roles, with the ability to expand across additional operational dimensions. This further strengthens access control precision and ensures the protection of sensitive data. API7 Enterprise supports more complex access rules, such as attribute-based mappings, SSO-integrated role mappings, and condition-based role mappings. These enable administrators to make flexible adjustments in specific situations, addressing complex enterprise permission management needs and effectively reducing the risks of privilege abuse and operational mistakes. * [Organization Management](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#organization-management) * [RBAC](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#rbac) * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#key-features) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#use-cases) * [Automate User Management](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#automate-user-management) * [Multi-department or Multi-team Management](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#multi-department-or-multi-team-management) * [Achieve Fine-Grained Permission Access](https://docs.api7.ai/apisix/enterprise-feature/organization-and-rbac/#achieve-fine-grained-permission-access) --- # Basic Auth | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/basic-auth/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `basic-auth` plugin adds [basic access authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) for [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) to authenticate themselves before being able to access upstream resources. When a consumer is successfully authenticated, APISIX adds additional headers, such as `X-Consumer-Username`, `X-Credential-Identifier`, and other consumer custom headers if configured, to the request, before proxying it to the upstream service. The upstream service will be able to differentiate between consumers and implement additional logics as needed. If any of these values is not available, the corresponding header will not be added. About X-Consumer-Username When consumers are configured using the Ingress Controller, the consumer name is generated in the format `namespace_consumername`. As a result, the `X-Consumer-Username` header will also follow this format instead of just `consumername`. Examples[​](https://docs.api7.ai/hub/basic-auth/#examples "Direct link to Examples") ------------------------------------------------------------------------------------- The examples below demonstrate how you can work with the `basic-auth` plugin for different scenarios. ### Implement Basic Authentication on Route[​](https://docs.api7.ai/hub/basic-auth/#implement-basic-authentication-on-route "Direct link to Implement Basic Authentication on Route") The following example demonstrates how to implement basic authentication on a route. * Admin API * ADC * Ingress Controller Create a consumer `johndoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "johndoe" }' Create `basic-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-basic-auth", "plugins": { "basic-auth": { "username": "johndoe", "password": "john-key" } } }' Create a route with `basic-auth`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "basic-auth-route", "uri": "/anything", "plugins": { "basic-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create a consumer with `basic-auth` credential and a route with `basic-auth` plugin configured: adc.yaml consumers: - username: johndoe credentials: - name: basic-auth type: basic-auth config: username: johndoe password: john-keyservices: - name: basic-auth-service routes: - name: basic-auth-route uris: - /anything plugins: basic-auth: {} upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Create a consumer with `basic-auth` credential and a route with `basic-auth` plugin configured: * Gateway API * APISIX CRD basic-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: johndoespec: gatewayRef: name: apisix credentials: - type: basic-auth name: primary-cred config: username: johndoe password: john-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: basic-auth-plugin-configspec: plugins: - name: basic-auth config: _meta: disable: false---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: basic-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: basic-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f basic-auth-ic.yaml basic-auth-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: johndoespec: ingressClassName: apisix authParameter: basicAuth: value: username: johndoe password: john-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: basic-auth-routespec: ingressClassName: apisix http: - name: basic-auth-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: basic-auth enable: true Apply the configuration to your cluster: kubectl apply -f basic-auth-ic.yaml #### Verify with Valid Credentials[​](https://docs.api7.ai/hub/basic-auth/#verify-with-valid-credentials "Direct link to Verify with Valid Credentials") Send a request to the route with valid credentials: curl -i "http://127.0.0.1:9080/anything" -u johndoe:john-key You should see an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "headers": { "Accept": "*/*", "Authorization": "Basic am9obmRvZTpqb2huLWtleQ==", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66e5107c-5bb3e24f2de5baf733aec1cc", "X-Consumer-Username": "johndoe", "X-Credential-Identifier": "cred-john-basic-auth", "X-Forwarded-Host": "127.0.0.1" }, "origin": "192.168.65.1, 205.198.122.37", "url": "http://127.0.0.1/anything"} #### Verify with Invalid Credentials[​](https://docs.api7.ai/hub/basic-auth/#verify-with-invalid-credentials "Direct link to Verify with Invalid Credentials") Send a request with invalid credentials: curl -i "http://127.0.0.1:9080/anything" -u johndoe:invalid-password You should see an `HTTP/1.1 401 Unauthorized` response with the following: {"message":"Invalid user authorization"} #### Verify without Credentials[​](https://docs.api7.ai/hub/basic-auth/#verify-without-credentials "Direct link to Verify without Credentials") Send a request without credentials: curl -i "http://127.0.0.1:9080/anything" You should see an `HTTP/1.1 401 Unauthorized` response with the following: {"message":"Missing authorization in request"} ### Hide Authentication Information From Upstream[​](https://docs.api7.ai/hub/basic-auth/#hide-authentication-information-from-upstream "Direct link to Hide Authentication Information From Upstream") The following example demonstrates how to prevent the client's credentials (the `Authorization` header) from being sent to the upstream services by configuring `hide_credentials`. If you are using APISIX, the `Authorization` header containing the client's credentials is forwarded to the upstream services by default, which might lead to security risks in some circumstances and you should consider updating `hide_credentials` as shown in this example. * Admin API * ADC * Ingress Controller Create a consumer `johndoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "johndoe" }' Create `basic-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-basic-auth", "plugins": { "basic-auth": { "username": "johndoe", "password": "john-key" } } }' #### Without Hiding Credentials[​](https://docs.api7.ai/hub/basic-auth/#without-hiding-credentials "Direct link to Without Hiding Credentials") Create a route with `basic-auth` and configure `hide_credentials` to `false`, which is the default configuration: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \-H "X-API-KEY: ${ADMIN_API_KEY}" \-d '{ "id": "basic-auth-route", "uri": "/anything", "plugins": { "basic-auth": { "hide_credentials": false } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' Create a consumer with `basic-auth` credential and a route with `basic-auth` plugin configured: adc.yaml consumers: - username: johndoe credentials: - name: basic-auth type: basic-auth config: username: johndoe password: john-keyservices: - name: basic-auth-service routes: - name: basic-auth-route uris: - /anything plugins: basic-auth: hide_credentials: false upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Create a consumer with `basic-auth` credential and a route with `basic-auth` plugin configured: * Gateway API * APISIX CRD basic-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: johndoespec: gatewayRef: name: apisix credentials: - type: basic-auth name: primary-cred config: username: johndoe password: john-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: basic-auth-plugin-configspec: plugins: - name: basic-auth config: _meta: disable: false hide_credentials: false---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: basic-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: basic-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f basic-auth-ic.yaml basic-auth-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: johndoespec: ingressClassName: apisix authParameter: basicAuth: value: username: johndoe password: john-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: basic-auth-routespec: ingressClassName: apisix http: - name: basic-auth-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: basic-auth enable: true config: hide_credentials: false Apply the configuration to your cluster: kubectl apply -f basic-auth-ic.yaml Send a request with the valid key: curl -i "http://127.0.0.1:9080/anything" -u johndoe:john-key You should see an `HTTP/1.1 200 OK` response with the following: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Authorization": "Basic am9obmRvZTpqb2huLWtleQ==", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66cc2195-22bd5f401b13480e63c498c6", "X-Consumer-Username": "johndoe", "X-Credential-Identifier": "cred-john-basic-auth", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "GET", "origin": "192.168.65.1, 43.228.226.23", "url": "http://127.0.0.1/anything"} Note that the credentials are visible to the upstream service in base64-encoded format. tip You can also pass the base64-encoded credentials in the request using the `Authorization` header as such: curl -i "http://127.0.0.1:9080/anything" -H "Authorization: Basic am9obmRvZTpqb2huLWtleQ==" #### Hide Credentials[​](https://docs.api7.ai/hub/basic-auth/#hide-credentials "Direct link to Hide Credentials") * Admin API * ADC * Ingress Controller Update the plugin's `hide_credentials` to `true`: curl "http://127.0.0.1:9180/apisix/admin/routes/basic-auth-route" -X PATCH \-H "X-API-KEY: ${ADMIN_API_KEY}" \-d '{ "plugins": { "basic-auth": { "hide_credentials": true } }}' Update the route configuration: adc.yaml # other configs# ...services: - name: basic-auth-service routes: - name: basic-auth-route uris: - /anything plugins: basic-auth: hide_credentials: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Update the PluginConfig to set `hide_credentials` to `true`: basic-auth-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: basic-auth-plugin-configspec: plugins: - name: basic-auth config: _meta: disable: false hide_credentials: true Apply the configuration to your cluster: kubectl apply -f basic-auth-ic.yaml Update the ApisixRoute to set `hide_credentials` to `true`: basic-auth-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: basic-auth-routespec: ingressClassName: apisix http: - name: basic-auth-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: basic-auth enable: true config: hide_credentials: true Apply the configuration to your cluster: kubectl apply -f basic-auth-ic.yaml Send a request with the valid key: curl -i "http://127.0.0.1:9080/anything" -u johndoe:john-key You should see an `HTTP/1.1 200 OK` response with the following: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66cc21a7-4f6ac87946e25f325167d53a", "X-Consumer-Username": "johndoe", "X-Credential-Identifier": "cred-john-basic-auth", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "GET", "origin": "192.168.65.1, 43.228.226.23", "url": "http://127.0.0.1/anything"} Note that the credentials are no longer visible to the upstream service. ### Add Consumer Custom ID to Header[​](https://docs.api7.ai/hub/basic-auth/#add-consumer-custom-id-to-header "Direct link to Add Consumer Custom ID to Header") The following example demonstrates how you can attach a consumer custom ID to authenticated request in the `Consumer-Custom-Id` header, which can be used to implement additional logics as needed. * Admin API * ADC * Ingress Controller Create a consumer `johndoe` with a custom ID label: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "johndoe", "labels": { "custom_id": "495aec6a" } }' Create `basic-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-basic-auth", "plugins": { "basic-auth": { "username": "johndoe", "password": "john-key" } } }' Create a route with `basic-auth`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "basic-auth-route", "uri": "/anything", "plugins": { "basic-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create a consumer with `basic-auth` credential and a route with `basic-auth` plugin enabled: adc.yaml consumers: - username: johndoe labels: custom_id: "495aec6a" credentials: - name: basic-auth type: basic-auth config: username: johndoe password: john-keyservices: - name: basic-auth-service routes: - name: basic-auth-route uris: - /anything plugins: basic-auth: {} upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Consumer custom labels are currently not supported when configuring resources through the Ingress Controller, and the `X-Consumer-Custom-Id` header is not included in requests. At the moment, this example cannot be completed with the Ingress Controller. To verify, send a request to the route with the valid key: curl -i "http://127.0.0.1:9080/anything" -u johndoe:john-key You should see an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Authorization": "Basic am9obmRvZTpqb2huLWtleQ==", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66ea8d64-33df89052ae198a706e18c2a", "X-Consumer-Username": "johndoe", "X-Credential-Identifier": "cred-john-basic-auth", "X-Consumer-Custom-Id": "495aec6a", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "GET", "origin": "192.168.65.1, 205.198.122.37", "url": "http://127.0.0.1/anything"} If you would like to attach more consumer custom headers to authenticated requests, see the [`attach-consumer-label`](https://docs.api7.ai/hub/attach-consumer-label) plugin. ### Rate Limit with Anonymous Consumer[​](https://docs.api7.ai/hub/basic-auth/#rate-limit-with-anonymous-consumer "Direct link to Rate Limit with Anonymous Consumer") The following example demonstrates how you can configure different rate limiting policies by regular and anonymous consumers, where the anonymous consumer does not need to authenticate and has less quota. * Admin API * ADC * Ingress Controller Create a regular consumer `johndoe` and configure the `limit-count` plugin to allow for a quota of 3 within a 30-second window: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "johndoe", "plugins": { "limit-count": { "count": 3, "time_window": 30, "rejected_code": 429, "policy": "local" } } }' Create the `basic-auth` credential for the consumer `johndoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-basic-auth", "plugins": { "basic-auth": { "username": "johndoe", "password": "john-key" } } }' Create an anonymous user `anonymous` and configure the `limit-count` plugin to allow for a quota of 1 within a 30-second window: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "anonymous", "plugins": { "limit-count": { "count": 1, "time_window": 30, "rejected_code": 429, "policy": "local" } } }' Create a route and configure the `basic-auth` plugin to accept anonymous consumer `anonymous` from bypassing the authentication: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "basic-auth-route", "uri": "/anything", "plugins": { "basic-auth": { "anonymous_consumer": "anonymous" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Configure consumers with different rate limits and a route that accepts anonymous users: adc.yaml consumers: - username: johndoe plugins: limit-count: count: 3 time_window: 30 rejected_code: 429 policy: local credentials: - name: basic-auth type: basic-auth config: username: johndoe password: john-key - username: anonymous plugins: limit-count: count: 1 time_window: 30 rejected_code: 429 policy: localservices: - name: anonymous-rate-limit-service routes: - name: basic-auth-route uris: - /anything plugins: basic-auth: anonymous_consumer: anonymous upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Configure consumers with different rate limits and a route that accepts anonymous users: basic-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: johndoespec: gatewayRef: name: apisix credentials: - type: basic-auth name: primary-key config: username: johndoe password: john-key plugins: - name: limit-count config: count: 3 time_window: 30 rejected_code: 429 policy: local---apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: anonymousspec: gatewayRef: name: apisix plugins: - name: limit-count config: count: 1 time_window: 30 rejected_code: 429 policy: local---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: basic-auth-plugin-configspec: plugins: - name: basic-auth config: anonymous_consumer: aic_anonymous # namespace_consumername---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: basic-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: basic-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f basic-auth-ic.yaml note The ApisixConsumer CRD currently does not support configuring plugins on consumers, except for the authentication plugins allowed in `authParameter`. This example cannot be completed with APISIX CRDs. To verify, send five consecutive requests with `johndoe`'s credentials: resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -u johndoe:john-key -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that out of the 5 requests, 3 requests were successful (status code 200) while the others were rejected (status code 429). 200: 3, 429: 2 Send five anonymous requests: resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that only one request was successful: 200: 1, 429: 4 * [Examples](https://docs.api7.ai/hub/basic-auth/#examples) * [Implement Basic Authentication on Route](https://docs.api7.ai/hub/basic-auth/#implement-basic-authentication-on-route) * [Hide Authentication Information From Upstream](https://docs.api7.ai/hub/basic-auth/#hide-authentication-information-from-upstream) * [Add Consumer Custom ID to Header](https://docs.api7.ai/hub/basic-auth/#add-consumer-custom-id-to-header) * [Rate Limit with Anonymous Consumer](https://docs.api7.ai/hub/basic-auth/#rate-limit-with-anonymous-consumer) --- # AI Rate Limiting | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ai-rate-limiting/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ai-rate-limiting` plugin enforces token-based rate limiting for requests sent to LLM services. It helps manage API usage by controlling the number of tokens consumed within a specified time frame, ensuring fair resource allocation and preventing excessive load on the service. It is often used with [`ai-proxy-multi`](https://docs.api7.ai/hub/ai-proxy-multi) plugin. Local vs Redis Rate Limiting[​](https://docs.api7.ai/hub/ai-rate-limiting/#local-vs-redis-rate-limiting "Direct link to Local vs Redis Rate Limiting") ------------------------------------------------------------------------------------------------------------------------------------------------------- The `ai-rate-limiting` plugin supports two modes of rate limiting: * **Local rate limiting**: Limits are enforced independently on each gateway instance. Each instance maintains its own counters, so the effective limit is roughly (limit × number of instances) when traffic is spread across instances. This is the default when no `policy` is set or when `policy` is `local`. * **Redis-based rate limiting**: Limits are shared across all gateway instances through Redis. All instances share the same quota, so the configured limit applies to all gateway instances. Demo[​](https://docs.api7.ai/hub/ai-rate-limiting/#demo "Direct link to Demo") ------------------------------------------------------------------------------- The following demonstrates the [configure instance priority and rate limiting example](https://docs.api7.ai/hub/ai-rate-limiting/#configure-instance-priority-and-rate-limiting) . It shows how you can configure two models with different priorities and apply rate limiting on the instance with a higher priority in API7 Enterprise using the Dashboard. In the case where `fallback_strategy` is set to `["rate_limiting"]`, the plugin should continue to forward requests to the low priority instance once the high priority instance's rate limiting quota is fully consumed. Examples[​](https://docs.api7.ai/hub/ai-rate-limiting/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------- The examples below demonstrate how you can configure `ai-rate-limiting` for different scenarios. ### Rate Limit One Instance[​](https://docs.api7.ai/hub/ai-rate-limiting/#rate-limit-one-instance "Direct link to Rate Limit One Instance") The following example demonstrates how you can use `ai-proxy-multi` to configure two models for load balancing, forwarding 80% of the traffic to one instance and 20% to the other. Additionally, use `ai-rate-limiting` to configure token-based rate limiting on the instance that receives 80% of the traffic, such that when the configured quota is fully consumed, the additional traffic will be forwarded to the other instance. Create a route as such and update with your LLM providers, models, API keys, and endpoints: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-rate-limiting-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-proxy-multi": { "instances": [ { "name": "deepseek-instance-1", "provider": "deepseek", "weight": 8, "auth": { "header": { "Authorization": "Bearer '"$DEEPSEEK_API_KEY"'" } }, "options": { "model": "deepseek-chat" } }, { "name": "deepseek-instance-2", "provider": "deepseek", "weight": 2, "auth": { "header": { "Authorization": "Bearer '"$DEEPSEEK_API_KEY"'" } }, "options": { "model": "deepseek-chat" } } ] }, "ai-rate-limiting": { "policy": "local", "instances": [ { "name": "deepseek-instance-1", "limit_strategy": "total_tokens", "limit": 100, "time_window": 30 } ] } } }' ❶ Apply rate limiting on `deepseek-instance-1` instance. ❷ Apply rate limiting by `total_tokens`. ❸ Configure a quota of 100 tokens. ❹ Configure the time window to be 30 seconds. Send a POST request to the route with a system prompt and a sample user question in the request body: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should receive a response similar to the following: { ... "model": "deepseek-chat", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "1 + 1 equals 2. This is a fundamental arithmetic operation where adding one unit to another results in a total of two units." }, "logprobs": null, "finish_reason": "stop" } ], ...} If `deepseek-instance-1` instance rate limiting quota of 100 tokens has been consumed in a 30-second window, the additional requests will all be forwarded to `deepseek-instance-2`, which is not rate limited. ### Apply the Same Quota to All Instances[​](https://docs.api7.ai/hub/ai-rate-limiting/#apply-the-same-quota-to-all-instances "Direct link to Apply the Same Quota to All Instances") The following example demonstrates how you can apply the same rate limiting quota to all LLM upstream instances in `ai-rate-limiting`. For demonstration and easier differentiation, you will be configuring one OpenAI instance and one DeepSeek instance as the upstream LLM services. Create a route as such and update with your LLM providers, models, API keys, and endpoints: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-rate-limiting-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-proxy-multi": { "instances": [ { "name": "openai-instance", "provider": "openai", "weight": 0, "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options": { "model": "gpt-4" } }, { "name": "deepseek-instance", "provider": "deepseek", "weight": 0, "auth": { "header": { "Authorization": "Bearer '"$DEEPSEEK_API_KEY"'" } }, "options": { "model": "deepseek-chat" } } ] }, "ai-rate-limiting": { "policy": "local", "limit": 100, "time_window": 60, "rejected_code": 429, "limit_strategy": "total_tokens" } } }' ❶ Configure a rate limiting quota of 100 tokens for all instances. ❷ Configure the time window to be 60 seconds. ❸ Set the rejection response HTTP status code to 429. ❹ Apply rate limiting by `total_tokens`. Send a POST request to the route with a system prompt and a sample user question in the request body: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Explain Newtons laws" } ] }' You should receive a response from either LLM instance, similar to the following: { ..., "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Sure! Sir Isaac Newton formulated three laws of motion that describe the motion of objects. These laws are widely used in physics and engineering for studying and understanding how things move. Here they are:\n\n1. Newton's First Law - Law of Inertia: An object at rest tends to stay at rest and an object in motion tends to stay in motion with the same speed and in the same direction unless acted upon by an unbalanced force. This is also known as the principle of inertia.\n\n2. Newton's Second Law of Motion - Force and Acceleration: The acceleration of an object is directly proportional to the net force acting on it and inversely proportional to its mass. This is usually formulated as F=ma where F is the force applied, m is the mass of the object and a is the acceleration produced.\n\n3. Newton's Third Law - Action and Reaction: For every action, there is an equal and opposite reaction. This means that any force exerted on a body will create a force of equal magnitude but in the opposite direction on the object that exerted the first force.\n\nIn simple terms: \n1. If you slide a book on a table and let go, it will stop because of the friction (or force) between it and the table.\n2.", "refusal": null }, "logprobs": null, "finish_reason": "length" } ], "usage": { "prompt_tokens": 23, "completion_tokens": 256, "total_tokens": 279, "prompt_tokens_details": { "cached_tokens": 0, "audio_tokens": 0 }, "completion_tokens_details": { "reasoning_tokens": 0, "audio_tokens": 0, "accepted_prediction_tokens": 0, "rejected_prediction_tokens": 0 } }, "service_tier": "default", "system_fingerprint": null} Since the `total_tokens` value exceeds the configured quota of `100`, the next request within the 60-second window is expected to be forwarded to the other instance. Within the same 60-second window, send another POST request to the route: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Explain Newtons laws" } ] }' You should receive a response from the other LLM instance, similar to the following: { ... "model": "deepseek-chat", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Sure! Newton's laws of motion are three fundamental principles that describe the relationship between the motion of an object and the forces acting on it. They were formulated by Sir Isaac Newton in the late 17th century and are foundational to classical mechanics. Here's an explanation of each law:\n\n---\n\n### **1. Newton's First Law (Law of Inertia)**\n- **Statement**: An object will remain at rest or in uniform motion in a straight line unless acted upon by an external force.\n- **What it means**: This law introduces the concept of **inertia**, which is the tendency of an object to resist changes in its state of motion. If no net force acts on an object, its velocity (speed and direction) will not change.\n- **Example**: A book lying on a table will stay at rest unless you push it. Similarly, a hockey puck sliding on ice will keep moving at a constant speed unless friction or another force slows it down.\n\n---\n\n### **2. Newton's Second Law (Law of Acceleration)**\n- **Statement**: The acceleration of an object is directly proportional to the net force acting on it and inversely proportional to its mass. Mathematically, this is expressed as:\n \\[\n F = ma\n \\]\n" }, "logprobs": null, "finish_reason": "length" } ], "usage": { "prompt_tokens": 13, "completion_tokens": 256, "total_tokens": 269, "prompt_tokens_details": { "cached_tokens": 0 }, "prompt_cache_hit_tokens": 0, "prompt_cache_miss_tokens": 13 }, "system_fingerprint": "fp_3a5770e1b4_prod0225"} Since the `total_tokens` value exceeds the configured quota of `100`, the next request within the 60-second window is expected to be rejected. Within the same 60-second window, send a third POST request to the route: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Explain Newtons laws" } ] }' You should receive an `HTTP 429 Too Many Requests` response and observe the following headers: X-AI-RateLimit-Limit-openai-instance: 100X-AI-RateLimit-Remaining-openai-instance: 0X-AI-RateLimit-Reset-openai-instance: 0X-AI-RateLimit-Limit-deepseek-instance: 100X-AI-RateLimit-Remaining-deepseek-instance: 0X-AI-RateLimit-Reset-deepseek-instance: 0 ### Configure Instance Priority and Rate Limiting[​](https://docs.api7.ai/hub/ai-rate-limiting/#configure-instance-priority-and-rate-limiting "Direct link to Configure Instance Priority and Rate Limiting") The following example demonstrates how you can configure two models with different priorities and apply rate limiting on the instance with a higher priority. In the case where `fallback_strategy` is set to `["rate_limiting"]`, the plugin should continue to forward requests to the low priority instance once the high priority instance's rate limiting quota is fully consumed. Create a route as such and update with your LLM providers, models, API keys, and endpoints: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-rate-limiting-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-proxy-multi": { "fallback_strategy": ["rate_limiting"], "instances": [ { "name": "openai-instance", "provider": "openai", "priority": 1, "weight": 0, "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options": { "model": "gpt-4" } }, { "name": "deepseek-instance", "provider": "deepseek", "priority": 0, "weight": 0, "auth": { "header": { "Authorization": "Bearer '"$DEEPSEEK_API_KEY"'" } }, "options": { "model": "deepseek-chat" } } ] }, "ai-rate-limiting": { "policy": "local", "instances": [ { "name": "openai-instance", "limit": 10, "time_window": 60 } ], "limit_strategy": "total_tokens" } } }' ❶ Set the `fallback_strategy` to `["rate_limiting"]`. ❷ Set a higher priority on `openai-instance` instance. ❸ Set a lower priority on `deepseek-instance` instance. ❹ Apply rate limiting on `openai-instance` instance. ❺ Configure a quota of 10 tokens. ❻ Configure the time window to be 60 seconds. ❼ Apply rate limiting by `total_tokens`. Send a POST request to the route with a system prompt and a sample user question in the request body: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should receive a response similar to the following: { ..., "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "1+1 equals 2.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 23, "completion_tokens": 8, "total_tokens": 31, "prompt_tokens_details": { "cached_tokens": 0, "audio_tokens": 0 }, "completion_tokens_details": { "reasoning_tokens": 0, "audio_tokens": 0, "accepted_prediction_tokens": 0, "rejected_prediction_tokens": 0 } }, "service_tier": "default", "system_fingerprint": null} Since the `total_tokens` value exceeds the configured quota of `10`, the next request within the 60-second window is expected to be forwarded to the other instance. Within the same 60-second window, send another POST request to the route: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Explain Newton law" } ] }' You should see a response similar to the following: { ..., "model": "deepseek-chat", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Certainly! Newton's laws of motion are three fundamental principles that describe the relationship between the motion of an object and the forces acting on it. They were formulated by Sir Isaac Newton in the late 17th century and are foundational to classical mechanics.\n\n---\n\n### **1. Newton's First Law (Law of Inertia):**\n- **Statement:** An object at rest will remain at rest, and an object in motion will continue moving at a constant velocity (in a straight line at a constant speed), unless acted upon by an external force.\n- **Key Idea:** This law introduces the concept of **inertia**, which is the tendency of an object to resist changes in its state of motion.\n- **Example:** If you slide a book across a table, it eventually stops because of the force of friction acting on it. Without friction, the book would keep moving indefinitely.\n\n---\n\n### **2. Newton's Second Law (Law of Acceleration):**\n- **Statement:** The acceleration of an object is directly proportional to the net force acting on it and inversely proportional to its mass. Mathematically, this is expressed as:\n \\[\n F = ma\n \\]\n where:\n - \\( F \\) = net force applied (in Newtons),\n -" }, ... } ], ...} ### Load Balance and Rate Limit by Consumers[​](https://docs.api7.ai/hub/ai-rate-limiting/#load-balance-and-rate-limit-by-consumers "Direct link to Load Balance and Rate Limit by Consumers") The following example demonstrates how you can configure two models for load balancing and apply rate limiting by consumer. Create a consumer `johndoe` and a rate limiting quota of 10 tokens in a 60-second window on `openai-instance` instance: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "johndoe", "plugins": { "ai-rate-limiting": { "policy": "local", "instances": [ { "name": "openai-instance", "limit": 10, "time_window": 60 } ], "rejected_code": 429, "limit_strategy": "total_tokens" } } }' Configure `key-auth` credential for `johndoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers/johndoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Create another consumer `janedoe` and a rate limiting quota of 10 tokens in a 60-second window on `deepseek-instance` instance: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "johndoe", "plugins": { "ai-rate-limiting": { "policy": "local", "instances": [ { "name": "deepseek-instance", "limit": 10, "time_window": 60 } ], "rejected_code": 429, "limit_strategy": "total_tokens" } } }' Configure `key-auth` credential for `janedoe`: curl "http://127.0.0.1:9180/apisix/admin/consumers/janedoe/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-key-auth", "plugins": { "key-auth": { "key": "jane-key" } } }' Create a route as such and update with your LLM providers, models, API keys, and endpoints: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-rate-limiting-route", "uri": "/anything", "methods": ["POST"], "plugins": { "key-auth": {}, "ai-proxy-multi": { "fallback_strategy": ["rate_limiting"], "instances": [ { "name": "openai-instance", "provider": "openai", "weight": 0, "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options": { "model": "gpt-4" } }, { "name": "deepseek-instance", "provider": "deepseek", "weight": 0, "auth": { "header": { "Authorization": "Bearer '"$DEEPSEEK_API_KEY"'" } }, "options": { "model": "deepseek-chat" } } ] } } }' ❶ Enable `key-auth` on the route. ❷ Configure an `openai` instance. ❸ Configure a `deepseek` instance. Send a POST request to the route without any consumer key: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should receive an `HTTP/1.1 401 Unauthorized` response. Send a POST request to the route with `johndoe`'s key: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -H 'apikey: john-key' \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should receive a response similar to the following: { ..., "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "1+1 equals 2.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 23, "completion_tokens": 8, "total_tokens": 31, "prompt_tokens_details": { "cached_tokens": 0, "audio_tokens": 0 }, "completion_tokens_details": { "reasoning_tokens": 0, "audio_tokens": 0, "accepted_prediction_tokens": 0, "rejected_prediction_tokens": 0 } }, "service_tier": "default", "system_fingerprint": null} Since the `total_tokens` value exceeds the configured quota of the `openai` instance for `johndoe`, the next request within the 60-second window from `johndoe` is expected to be forwarded to the `deepseek` instance. Within the same 60-second window, send another POST request to the route with `johndoe`'s key: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -H 'apikey: john-key' \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Explain Newtons laws to me" } ] }' You should see a response similar to the following: { ..., "model": "deepseek-chat", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Certainly! Newton's laws of motion are three fundamental principles that describe the relationship between the motion of an object and the forces acting on it. They were formulated by Sir Isaac Newton in the late 17th century and are foundational to classical mechanics.\n\n---\n\n### **1. Newton's First Law (Law of Inertia):**\n- **Statement:** An object at rest will remain at rest, and an object in motion will continue moving at a constant velocity (in a straight line at a constant speed), unless acted upon by an external force.\n- **Key Idea:** This law introduces the concept of **inertia**, which is the tendency of an object to resist changes in its state of motion.\n- **Example:** If you slide a book across a table, it eventually stops because of the force of friction acting on it. Without friction, the book would keep moving indefinitely.\n\n---\n\n### **2. Newton's Second Law (Law of Acceleration):**\n- **Statement:** The acceleration of an object is directly proportional to the net force acting on it and inversely proportional to its mass. Mathematically, this is expressed as:\n \\[\n F = ma\n \\]\n where:\n - \\( F \\) = net force applied (in Newtons),\n -" }, ... } ], ...} Send a POST request to the route with `janedoe`'s key: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -H 'apikey: jane-key' \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should receive a response similar to the following: { ..., "model": "deepseek-chat", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "The sum of 1 and 1 is 2. This is a basic arithmetic operation where you combine two units to get a total of two units." }, "logprobs": null, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 14, "completion_tokens": 31, "total_tokens": 45, "prompt_tokens_details": { "cached_tokens": 0 }, "prompt_cache_hit_tokens": 0, "prompt_cache_miss_tokens": 14 }, "system_fingerprint": "fp_3a5770e1b4_prod0225"} Since the `total_tokens` value exceeds the configured quota of the `deepseek` instance for `janedoe`, the next request within the 60-second window from `janedoe` is expected to be forwarded to the `openai` instance. Within the same 60-second window, send another POST request to the route with `janedoe`'s key: curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -H 'apikey: jane-key' \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "Explain Newtons laws to me" } ] }' You should see a response similar to the following: { ..., "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Sure, here are Newton's three laws of motion:\n\n1) Newton's First Law, also known as the Law of Inertia, states that an object at rest will stay at rest, and an object in motion will stay in motion, unless acted on by an external force. In simple words, this law suggests that an object will keep doing whatever it is doing until something causes it to do otherwise. \n\n2) Newton's Second Law states that the force acting on an object is equal to the mass of that object times its acceleration (F=ma). This means that force is directly proportional to mass and acceleration. The heavier the object and the faster it accelerates, the greater the force.\n\n3) Newton's Third Law, also known as the law of action and reaction, states that for every action, there is an equal and opposite reaction. Essentially, any force exerted onto a body will create a force of equal magnitude but in the opposite direction on the object that exerted the first force.\n\nRemember, these laws become less accurate when considering speeds near the speed of light (where Einstein's theory of relativity becomes more appropriate) or objects very small or very large. However, for everyday situations, they provide a good model of how things move.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], ...} This shows `ai-proxy-multi` load balance the traffic with respect to the rate limiting rules in `ai-rate-limiting` by consumers. ### Rate Limit by Rules[​](https://docs.api7.ai/hub/ai-rate-limiting/#rate-limit-by-rules "Direct link to Rate Limit by Rules") The following example demonstrates how you can configure the plugin to apply different rate-limiting rules (available from API7 Enterprise 3.8.17) based on request attributes. In this example, rate limits are applied based on HTTP header values that represent the caller’s access tier. Note that all rules are applied sequentially. If a configured key does not exist, the corresponding rule will be skipped. tip In addition to HTTP headers, you can also base rules on other [built-in variables](https://docs.api7.ai/enterprise/reference/built-in-variables) to implement more flexible and fine-grained rate-limiting strategies. Create a route with the `ai-rate-limiting` plugin that applies different rate limits based on request headers, allowing requests to be rate limited per subscription (`X-Subscription-ID`) and enforcing a stricter limit for trial users (`X-Trial-ID`): curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-rate-limiting-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-proxy-multi": { "fallback_strategy": ["rate_limiting"], "instances": [ { "name": "openai-instance", "provider": "openai", "priority": 1, "weight": 0, "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options": { "model": "gpt-4" } }, { "name": "deepseek-instance", "provider": "deepseek", "priority": 0, "weight": 0, "auth": { "header": { "Authorization": "Bearer '"$DEEPSEEK_API_KEY"'" } }, "options": { "model": "deepseek-chat" } } ] }, "ai-rate-limiting": { "policy": "local", "rejected_code": 429, "rules": [ { "key": "${http_x_subscription_id}", "count": "${http_x_custom_count ?? 500}", "time_window": 60 }, { "key": "${http_x_trial_id}", "count": 50, "time_window": 60 } ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' ❶ Use the value of the `X-Subscription-ID` request header as the rate-limiting key. ❷ Set the request limit dynamically based on the `X-Custom-Count` header. If the header is not provided, a default count of 500 tokens is applied. ❸ Use the value of the `X-Trial-ID` request header as the rate-limiting key. To verify rate limiting, send several of the following requests to the route with the same subscription ID: curl "http://127.0.0.1:9080/anything" -i -X POST \ -H "X-Subscription-ID: sub-123456789" \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' These requests should match the first rule with a default token count of 500. You should see that requests within the quota return `HTTP/1.1 200 OK`, while those exceeding it return `HTTP/1.1 429 Too Many Requests`: HTTP/1.1 200 OK...X-AI-1-RateLimit-Limit: 500X-AI-1-RateLimit-Remaining: 499X-AI-1-RateLimit-Reset: 60HTTP/1.1 200 OK...X-AI-1-RateLimit-Limit: 500X-AI-1-RateLimit-Remaining: 344X-AI-1-RateLimit-Reset: 57.989000082016HTTP/1.1 429 Too Many Requests...X-AI-1-RateLimit-Limit: 500X-AI-1-RateLimit-Remaining: 0X-AI-1-RateLimit-Reset: 5.871000051498 Wait for the time window to reset. Send several of the following requests to the route with the same subscription ID and set the `X-Custom-Count` header to 10: curl "http://127.0.0.1:9080/anything" -i -X POST \ -H "X-Subscription-ID: sub-123456789" \ -H "X-Custom-Count: 10" \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' These requests should match the first rule with a custom token count of 10. You should see that requests within the quota return `HTTP/1.1 200 OK`, while those exceeding it return `HTTP/1.1 429 Too Many Requests`: HTTP/1.1 200 OK...X-AI-1-RateLimit-Limit: 10X-AI-1-RateLimit-Remaining: 9X-AI-1-RateLimit-Reset: 60HTTP/1.1 429 Too Many Requests...X-AI-1-RateLimit-Limit: 10X-AI-1-RateLimit-Remaining: 0X-AI-1-RateLimit-Reset: 40.422000169754 Finally, send several of the following requests to the route without any header: curl "http://127.0.0.1:9080/anything" -i -X POST \ -H "X-Trial-ID: trial-123456789" \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' These requests should match the second rule with a token count of 50. You should see that requests within the quota return `HTTP/1.1 200 OK`, while those exceeding it return `HTTP/1.1 429 Too Many Requests`: HTTP/1.1 200 OK...X-AI-2-RateLimit-Limit: 50X-AI-2-RateLimit-Remaining: 49X-AI-2-RateLimit-Reset: 60HTTP/1.1 429 Too Many Requests...X-AI-2-RateLimit-Limit: 50X-AI-2-RateLimit-Remaining: 0X-AI-2-RateLimit-Reset: 44 ### Share Quota Among Gateways with a Redis Server[​](https://docs.api7.ai/hub/ai-rate-limiting/#share-quota-among-gateways-with-a-redis-server "Direct link to Share Quota Among Gateways with a Redis Server") The following example demonstrates how to configure distributed rate limiting across multiple gateway instances. This is particularly useful in production environments where you need cluster-wide rate limiting consistency. This example applies to API7 Enterprise version 3.8.19 and later. It is not applicable to APISIX, as the `policy` feature is not yet supported. #### Prerequisites[​](https://docs.api7.ai/hub/ai-rate-limiting/#prerequisites "Direct link to Prerequisites") Before configuring Redis-based rate limiting, start a Redis instance. docker run -d --name redis-standalone \ -p 6379:6379 \ -e REDIS_PASSWORD=p@ssw0rd \ redis:7-alpine redis-server --requirepass p@ssw0rd Then verify the Redis connection. docker exec -it redis-standalone redis-cli -a p@ssw0rd ping You should receive a response `PONG`, which shows successful connection. #### Create Route and Configure Rate Limiting[​](https://docs.api7.ai/hub/ai-rate-limiting/#create-route-and-configure-rate-limiting "Direct link to Create Route and Configure Rate Limiting") Create a route with the following configurations in the gateway group: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-rate-limiting-redis-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-proxy-multi": { "instances": [ { "name": "deepseek-instance", "provider": "deepseek", "weight": 8, "auth": { "header": { "Authorization": "Bearer '"$DEEPSEEK_API_KEY"'" } }, "options": { "model": "deepseek-chat" } }, { "name": "openai-instance", "override": { "endpoint": "https://openrouter.ai/api/v1/chat/completions" }, "provider": "openai-compatible", "weight": 2, "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options": { "model": "gpt-4" } } ] }, "ai-rate-limiting": { "instances": [ { "name": "deepseek-instance", "limit_strategy": "total_tokens", "limit": 100, "time_window": 30 }, { "name": "openai-instance", "limit_strategy": "total_tokens", "limit": 50, "time_window": 30 } ], "policy": "redis", "redis_host": "127.0.0.1", "redis_port": 6379, "redis_password": "p@ssw0rd", "allow_degradation": false, "rejected_code": 429 } } }' ❶ `policy`: Set to `redis` to use a Redis instance for rate limiting. ❷ `redis_host`: Set to the Redis instance IP address. ❸ `redis_port`: Set to Redis instance listening port. ❹ `redis_password`: Set to the password of the Redis instance, if any. ❺ `allow_degradation`: Set to `false` to reject requests if Redis is unavailable. #### Verify[​](https://docs.api7.ai/hub/ai-rate-limiting/#verify "Direct link to Verify") Send a POST request to the route with a system prompt and a sample user question in the request body. curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should see a response similar to the following: { ..., "model": "deepseek-chat", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "In mathematics, under the usual rules of arithmetic, **1 + 1 = 2**.\n\nThis follows from the definition of natural numbers and addition in systems like Peano arithmetic, where:\n\n- 1 is the successor of 0.\n- 2 is the successor of 1.\n- Addition is defined recursively so that 1 + 1 = S(0) + S(0) = S(S(0)) = 2.\n\nIn different contexts, the answer might vary (e.g., in Boolean algebra, 1 + 1 = 1 for logical OR; in modular arithmetic mod 2, 1 + 1 = 0), but in standard arithmetic, the answer is **2**." }, "logprobs": null, "finish_reason": "stop" } ], ...} Generate 3 requests to consume the quota: for i in {1..3}; do curl -i "http://127.0.0.1:9080/anything" \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' & sleep 1donewait You should receive `HTTP/1.1 200 OK` responses. After consuming the rate limit quota, subsequent requests will be rejected. You should receive a `429 Too Many Requests` response when the rate limit is exceeded. ### Share Quota Among Gateway Nodes with a Redis Cluster[​](https://docs.api7.ai/hub/ai-rate-limiting/#share-quota-among-gateway-nodes-with-a-redis-cluster "Direct link to Share Quota Among Gateway Nodes with a Redis Cluster") The following example demonstrates how to enable multiple gateway nodes to share the same rate limiting quota through a Redis cluster. This example applies to API7 Enterprise version 3.8.19 and later. It is not applicable to APISIX, as the `policy` feature is not yet supported. #### Prerequisites[​](https://docs.api7.ai/hub/ai-rate-limiting/#prerequisites-1 "Direct link to Prerequisites") 1. Create a Docker network: docker network create redis-cluster-network Ensure that your gateway instance is running within the same network as your Redis cluster. 2. Start 6 Redis nodes and wait for them to start: for port in $(seq 7000 7005); do docker run -d \ --name redis-node-$port \ --network redis-cluster-network \ -p $port:$port \ redis:7.2-alpine \ redis-server \ --port $port \ --cluster-enabled yes \ --cluster-config-file nodes.conf \ --cluster-node-timeout 5000 \ --appendonly yes \ --requirepass redis-cluster-password \ --masterauth redis-cluster-passworddone && sleep 5 3. Create a cluster: docker run --rm \ --network redis-cluster-network \ redis:7.2-alpine \ sh -c " redis-cli \ --cluster create \ $(for port in $(seq 7000 7005); do echo -n \"redis-node-$port:$port \"; done) \ --cluster-replicas 1 \ --cluster-yes \ -a redis-cluster-password " The expected output should be similar to the following: ...,[OK] All nodes agree about slots configuration.>>> Check for open slots...>>> Check slots coverage...[OK] All 16384 slots covered. 4. Verify cluster nodes: docker exec -it redis-node-7000 redis-cli -c -a redis-cluster-password -p 7000 cluster nodes The expected output should be similar to the following: node-id-1 172.XX.0.2:7000@17000 myself,master - 0 0 1 connected 0-5460node-id-2 172.XX.0.3:7001@17001 master - 0 0 2 connected 5461-10922node-id-3 172.XX.0.4:7002@17002 master - 0 0 3 connected 10923-16383node-id-4 172.XX.0.5:7003@17003 slave node-id-1 0 0 1 connectednode-id-5 172.XX.0.6:7004@17004 slave node-id-2 0 0 2 connectednode-id-6 172.XX.0.7:7005@17005 slave node-id-3 0 0 3 connected 5. Check cluster health (optional): docker exec redis-node-7000 redis-cli -c -a redis-cluster-password -p 7000 cluster info You should see the following response: cluster_state:okcluster_slots_assigned:16384cluster_slots_ok:16384cluster_known_nodes:6cluster_size:3... #### Create Route and Configure Rate Limiting[​](https://docs.api7.ai/hub/ai-rate-limiting/#create-route-and-configure-rate-limiting-1 "Direct link to Create Route and Configure Rate Limiting") Create a route with the following configurations in the gateway group: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-rate-limiting-redis-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-proxy-multi": { "instances": [ { "name": "deepseek-instance", "provider": "deepseek", "weight": 8, "auth": { "header": { "Authorization": "Bearer '"$DEEPSEEK_API_KEY"'" } }, "options": { "model": "deepseek-chat" } }, { "name": "openai-instance", "override": { "endpoint": "https://openrouter.ai/api/v1/chat/completions" }, "provider": "openai-compatible", "weight": 2, "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options": { "model": "gpt-4" } } ] }, "ai-rate-limiting": { "instances": [ { "name": "deepseek-instance", "limit_strategy": "total_tokens", "limit": 200, "time_window": 60 }, { "name": "openai-instance", "limit_strategy": "total_tokens", "limit": 100, "time_window": 60 } ], "policy": "redis-cluster", "redis_cluster_nodes": [ "172.XX.0.2:7000", "172.XX.0.3:7001", "172.XX.0.4:7002", "172.XX.0.5:7003", "172.XX.0.6:7004", "172.XX.0.7:7005" ], "redis_password": "redis-cluster-password", "redis_cluster_name": "redis-cluster-1", "redis_timeout": 1000, "redis_connect_timeout": 1000, "allow_degradation": false, "rejected_code": 429 } } }' ❶ `policy`: Set to `redis-cluster` to use a Redis cluster for rate limiting. ❷ `redis_cluster_nodes`: Set to Redis node addresses in the Redis cluster. ❸ `redis_password`: Set to the password of the Redis cluster, if any. ❹ `redis_cluster_name`: Set to the Redis cluster name. #### Verify[​](https://docs.api7.ai/hub/ai-rate-limiting/#verify-1 "Direct link to Verify") Send a POST request to the route with a system prompt and a sample user question in the request body. curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should see a response similar to the following: { ..., "model": "deepseek-chat", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "In mathematics, under the usual rules of arithmetic, **1 + 1 = 2**.\n\nThis follows from the definition of natural numbers and addition in systems like Peano arithmetic, where:\n\n- 1 is the successor of 0.\n- 2 is the successor of 1.\n- Addition is defined recursively so that 1 + 1 = S(0) + S(0) = S(S(0)) = 2.\n\nIn different contexts, the answer might vary (e.g., in Boolean algebra, 1 + 1 = 1 for logical OR; in modular arithmetic mod 2, 1 + 1 = 0), but in standard arithmetic, the answer is **2**." }, "logprobs": null, "finish_reason": "stop" } ], ...} Generate 3 requests to consume the quota: for i in {1..3}; do curl -i "http://127.0.0.1:9080/anything" \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' & sleep 1donewait You should receive `HTTP/1.1 200 OK` responses for most requests, with the remainder being `HTTP 429 Too Many Requests` responses. This verifies routes configured in different gateway nodes share the same quota. Verify that the rate limiting counters are being stored in the Redis cluster by checking all nodes for rate limiting keys: for port in {7000..7005}; do echo "Checking node redis-node-$port:" docker exec redis-node-$port redis-cli -c -a redis-cluster-password -p $port keys "plugin-ai-rate-limiting*" 2>/dev/null | grep -v "^$" || echo "No related keys found"done You should see output similar to the following: Checking node redis-node-7000:No related keys foundChecking node redis-node-7001:No related keys foundChecking node redis-node-7002:plugin-ai-rate-limitingroute&service&:::Checking node redis-node-7003:plugin-ai-rate-limitingroute&service&:::Checking node redis-node-7004:No related keys foundChecking node redis-node-7005:No related keys found ### Share Quota Among Gateway Nodes with a Redis Sentinel[​](https://docs.api7.ai/hub/ai-rate-limiting/#share-quota-among-gateway-nodes-with-a-redis-sentinel "Direct link to Share Quota Among Gateway Nodes with a Redis Sentinel") This example applies to API7 Enterprise version 3.9.2 and later. It is not applicable to APISIX, as the `policy` feature is not yet supported. Use Redis Sentinel when you need automatic failover and high availability but don't require data sharding. This pattern is simpler to manage and suitable for most high-availability requirements. Ensure that your Redis instances are running in [Sentinel mode](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/) . #### Prerequisites[​](https://docs.api7.ai/hub/ai-rate-limiting/#prerequisites-2 "Direct link to Prerequisites") 1. Create a Docker network: docker network create redis-sentinel-network Ensure that your gateway instance is running within the same network as your Redis Sentinel cluster. 2. Start a Redis master node: docker run -d --name redis-master --network redis-sentinel-network \ -p 6379:6379 \ redis:7.2-alpine \ redis-server --requirepass StrongP@ss123 --appendonly yes 3. Start Sentinel replica nodes: for i in 1 2; do PORT=$((6380 + i - 1)) docker run -d --name redis-slave-$i --network redis-sentinel-network \ -p $PORT:6379 \ redis:7.2-alpine \ redis-server --slaveof redis-master 6379 \ --requirepass StrongP@ss123 \ --masterauth StrongP@ss123 \ --appendonly yesdone 4. Get master node IP address for next step: MASTER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis-master)echo "Redis master node IP: $MASTER_IP" 5. Start Sentinel cluster and replace `$MASTER_IP` with your master node IP: for i in 1 2 3; do docker run -d --name redis-sentinel-$i --network redis-sentinel-network -p $((26378+i-1)):26379 \ redis:7.2-alpine \ sh -c "cat << 'EOF' > /sentinel.confport 26379sentinel monitor mymaster $MASTER_IP 6379 2sentinel auth-pass mymaster StrongP@ss123requirepass admin-passwordsentinel down-after-milliseconds mymaster 5000sentinel failover-timeout mymaster 10000sentinel parallel-syncs mymaster 1protected-mode noEOFredis-sentinel /sentinel.conf"doneecho "✅ Sentinel cluster started successfully." You can see the following response: Starting redis-sentinel-1 (port:26379)...eb9efacb629d0cfdfaa48856f42ba8c67642baa79f1589df5b251c11d3ec6e1aStarting redis-sentinel-2 (port:26380)...7f23f4b6e63c9b6be4c5e1903a244f078d481952a1465a9650c743ea2ee4600fStarting redis-sentinel-3 (port:26381)...1df087502124e3903df7ae665ef597bf735669c5ce3f9d87696c4acd82526626✅ Sentinel cluster started successfully. 6. Confirm the Sentinel environment is running correctly: echo "Waiting for Sentinel cluster establishment (10 seconds)..."sleep 10echo -e "\nVerifying Sentinel cluster status:"for i in 1 2 3; do echo "--- Sentinel $i status ---" if docker ps | grep -q "redis-sentinel-$i"; then echo "Container: ✅ Running" docker exec redis-sentinel-$i redis-cli -p 26379 SENTINEL master mymaster 2>&1 | grep -E "(flags|num-slaves|num-other-sentinels)" else echo "Container: ❌ Not running (run 'docker logs redis-sentinel-$i' to check)" fi echo ""done You can see the following response: Verifying Sentinel cluster status:--- Sentinel 1 status ---Container: ✅ Running--- Sentinel 2 status ---Container: ✅ Running--- Sentinel 3 status ---Container: ✅ Running 7. Get Sentinel IP addresses for plugin configuration: echo -e "Getting Sentinel container IP addresses:"for i in 1 2 3; do IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis-sentinel-$i) echo " redis-sentinel-$i : $IP"done You can see the following response: Getting Sentinel container IP addresses: redis-sentinel-1 : 172.22.0.4 redis-sentinel-2 : 172.22.0.5 redis-sentinel-3 : 172.22.0.6 8. Conduct detailed status check: echo "Checking detailed Sentinel cluster status..."for i in 1 2 3; do echo "=== Sentinel $i details ===" docker exec redis-sentinel-$i redis-cli -p 26379 SENTINEL master mymaster echo ""done You can see the following response: Checking detailed Sentinel cluster status...=== Sentinel 1 Details ===name: mymasterip: 172.22.0.2port: 6379runid: ${YOUR_RUN_ID}flags: masterlink-pending-commands: 0link-refcount: 1last-ping-sent: 0last-ok-ping-reply: 113last-ping-reply: 113down-after-milliseconds: 5000info-refresh: 6979role-reported: masterrole-reported-time: 107360config-epoch: 0num-slaves: 1num-other-sentinels: 2quorum: 2failover-timeout: 10000parallel-syncs: 1... #### Create Route and Configure Rate Limiting[​](https://docs.api7.ai/hub/ai-rate-limiting/#create-route-and-configure-rate-limiting-2 "Direct link to Create Route and Configure Rate Limiting") Create a route with the following configurations in the gateway group: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-rate-limiting-redis-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-proxy-multi": { "instances": [ { "name": "deepseek-instance", "provider": "deepseek", "weight": 8, "auth": { "header": { "Authorization": "Bearer '"$DEEPSEEK_API_KEY"'" } }, "options": { "model": "deepseek-chat" } }, { "name": "openai-instance", "override": { "endpoint": "https://openrouter.ai/api/v1/chat/completions" }, "provider": "openai-compatible", "weight": 2, "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options": { "model": "gpt-4" } } ] }, "ai-rate-limiting": { "instances": [ { "name": "deepseek-instance", "limit_strategy": "total_tokens", "limit": 100, "time_window": 60 }, { "name": "openai-instance", "limit_strategy": "total_tokens", "limit": 100, "time_window": 60 } ], "policy": "redis-sentinel", "redis_sentinels": [ {"host": "172.22.0.4", "port": 26379}, {"host": "172.22.0.5", "port": 26379}, {"host": "172.22.0.6", "port": 26379} ], "redis_master_name": "mymaster", "redis_password": "StrongP@ss123", "redis_role": "master", "sentinel_username": "admin", "sentinel_password": "admin-password", "rejected_code": 429 } } }' ❶ `policy`: Set to `redis-sentinel` to use a Redis in sentinel mode for rate limiting. ❷ `redis_sentinels`: Configure a list of Sentinel node addresses (host and port). ❸ `redis_master_name`: Configure the name of the Redis master group that Sentinels are monitoring. ❹ `redis_password`: Set to the password of the Redis instance, if any. ❺ `redis_role`: Set to `master` to connect to the current Redis master. ❻ `sentinel_username`: Configure the username used to authenticate with Redis Sentinel. ❼ `sentinel_password`: Configure the password used to authenticate with Redis Sentinel. #### Verify[​](https://docs.api7.ai/hub/ai-rate-limiting/#verify-2 "Direct link to Verify") Send a POST request to the route with a system prompt and a sample user question in the request body. curl "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' You should see a response similar to the following: { ..., "model": "deepseek-chat", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "In mathematics, under the usual rules of arithmetic, **1 + 1 = 2**.\n\nThis follows from the definition of natural numbers and addition in systems like Peano arithmetic, where:\n\n- 1 is the successor of 0.\n- 2 is the successor of 1.\n- Addition is defined recursively so that 1 + 1 = S(0) + S(0) = S(S(0)) = 2.\n\nIn different contexts, the answer might vary (e.g., in Boolean algebra, 1 + 1 = 1 for logical OR; in modular arithmetic mod 2, 1 + 1 = 0), but in standard arithmetic, the answer is **2**." }, "logprobs": null, "finish_reason": "stop" } ], ...} Generate 3 requests to consume the quota: for i in {1..3}; do curl -i "http://127.0.0.1:9080/anything" \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are a mathematician" }, { "role": "user", "content": "What is 1+1?" } ] }' & sleep 1donewait You should receive `HTTP/1.1 200 OK` responses. After consuming the rate limit quota, subsequent requests will be rejected. You should receive a `429 Too Many Requests` response when the rate limit is exceeded. * [Local vs Redis Rate Limiting](https://docs.api7.ai/hub/ai-rate-limiting/#local-vs-redis-rate-limiting) * [Demo](https://docs.api7.ai/hub/ai-rate-limiting/#demo) * [Examples](https://docs.api7.ai/hub/ai-rate-limiting/#examples) * [Rate Limit One Instance](https://docs.api7.ai/hub/ai-rate-limiting/#rate-limit-one-instance) * [Apply the Same Quota to All Instances](https://docs.api7.ai/hub/ai-rate-limiting/#apply-the-same-quota-to-all-instances) * [Configure Instance Priority and Rate Limiting](https://docs.api7.ai/hub/ai-rate-limiting/#configure-instance-priority-and-rate-limiting) * [Load Balance and Rate Limit by Consumers](https://docs.api7.ai/hub/ai-rate-limiting/#load-balance-and-rate-limit-by-consumers) * [Rate Limit by Rules](https://docs.api7.ai/hub/ai-rate-limiting/#rate-limit-by-rules) * [Share Quota Among Gateways with a Redis Server](https://docs.api7.ai/hub/ai-rate-limiting/#share-quota-among-gateways-with-a-redis-server) * [Share Quota Among Gateway Nodes with a Redis Cluster](https://docs.api7.ai/hub/ai-rate-limiting/#share-quota-among-gateway-nodes-with-a-redis-cluster) * [Share Quota Among Gateway Nodes with a Redis Sentinel](https://docs.api7.ai/hub/ai-rate-limiting/#share-quota-among-gateway-nodes-with-a-redis-sentinel) --- # Audit and Rollback | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page As the frequency of API calls and system operations increases, the lack of effective monitoring and recording mechanisms can lead to security risks and operational hazards. Many industries face compliance management requirements, necessitating detailed recording and auditing of critical operations. API7 Enterprise provides the audit logging feature to monitor and record user activities within API7 Enterprise, including all operations during user logins to the dashboard and API/ADC calls. API7 Enterprise uses tokens as credentials to authenticate users or applications while the audit logs capture all token-related authentication and authorization actions. How Audit and Rollback Work[​](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#how-audit-and-rollback-work "Direct link to How Audit and Rollback Work") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- All user actions within API7 Enterprise will be recorded. Any additional alteration to audit logs is forbidden, which can ensure data authenticity. All API calls generate corresponding audit logs. Each audit log includes the operation time, operator, event type, resource ID and name, operator's IP address, and source of the operation. Users with permission to view and download audit logs, such as auditors, can access detailed information and export the logs in JSON or CSV format for further analysis. Furthermore, API7 Enterprise has implemented strict data masking mechanisms to protect sensitive information within the logs. Additionally, these audit logs are retained for 180 days by default to meet compliance requirements. Through the audit logging feature, users can promptly identify and address potential security threats, and effectively enhance the platform's security and compliance. Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#key-features "Direct link to Key Features") ------------------------------------------------------------------------------------------------------------------------------- * Monitor and log all activities and user actions that are immutable, ensuring data integrity. * Implement fine-grained audit log access control, ensuring only authorized administrators can view logs or perform rollbacks. * The rollback feature ensures data consistency and transaction integrity, minimizing manual intervention and reducing recovery time. * Audit logs are retained for 180 days for archival and backup purposes, ensuring compliance with auditing and regulatory requirements. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#use-cases "Direct link to Use Cases") ---------------------------------------------------------------------------------------------------------------------- ### Real-Time Detection and Recording[​](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#real-time-detection-and-recording "Direct link to Real-Time Detection and Recording") Consider a scenario where a user mistakenly configures a new API service, leading to service disruption. In this case, the audit logging feature in API7 Enterprise captures the action in real-time, documenting every modification made to the API gateway configuration. When auditors log into the system and review the audit logs, they can quickly identify any anomalies or misconfigurations that contributed to the disruption. The audit logging feature enables them to pinpoint issues promptly and take corrective action before any significant damage occurs. The real-time detection and recording of such events provide a proactive approach to managing security and operational risks. ### Rollback for Swift Restoration[​](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#rollback-for-swift-restoration "Direct link to Rollback for Swift Restoration") In the case of a service disruption caused by a misconfigured API service, internal auditors or system administrators can leverage the audit logging feature within API7 Enterprise to quickly trace and investigate the root cause. Once the issue is localized, the rollback feature in API7 Enterprise allows administrators to restore the system to a stable state by reverting the service to its prior configuration. The rollback capability is particularly useful in scenarios where multiple changes are made in quick succession, and it is difficult to pinpoint exactly which modification caused the problem. In such cases, administrators can perform a rollback based on the resource ID, enabling them to specifically undo changes made to the affected resource. By allowing precise rollbacks, API7 Enterprise facilitates efficient troubleshooting and minimizes the risk of operational disruption. ### Post-Event Audit Logs Analysis[​](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#post-event-audit-logs-analysis "Direct link to Post-Event Audit Logs Analysis") The detailed recording and auditing of critical operations provide a basis for subsequent compliance audits. These logs offer insights into user actions, system changes, and potential security incidents, making them invaluable for assessing adherence to security policies and regulatory requirements. Through post-event analysis, administrators can easily and efficiently conduct targeted security audits on information systems. Moreover, the insights gained from post-event audits not only help in immediate issue resolution but also inform future policy adjustments and improvements. This makes audit logs a cornerstone for continuous security monitoring, risk management, and regulatory compliance. * [How Audit and Rollback Work](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#how-audit-and-rollback-work) * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#key-features) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#use-cases) * [Real-Time Detection and Recording](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#real-time-detection-and-recording) * [Rollback for Swift Restoration](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#rollback-for-swift-restoration) * [Post-Event Audit Logs Analysis](https://docs.api7.ai/apisix/enterprise-feature/audit-and-rollback/#post-event-audit-logs-analysis) --- # Consumer Groups | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/consumer-groups/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of consumer groups in APISIX and their common use cases. You will be introduced to a few relevant concepts, including how to pass consumer group information to upstream and consumer group access restriction. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/consumer-groups/#overview "Direct link to Overview") ---------------------------------------------------------------------------------------------------------- In APISIX, a _consumer group_ corresponds to a group of [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) sharing the same [plugin](https://docs.api7.ai/apisix/key-concepts/plugins) configurations, such as access control and rate limiting policies. Consumers can be added to a consumer group by referring to the consumer group ID in their configurations. This allows for easier management of APIs and helps to reduce redundancies in consumer configurations. A few sample use cases of consumer groups are: * Implementing API monetization with different pricing models * Implementing [role-based access control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) for different roles, such as admins, developers, and guests * Grouping consumers based on other shared functionalities The following diagram illustrates the concept of consumer groups with an API monetization example that has three consumers, `John`, `Jane`, and `Bot`. John and Jane are human consumers in the **basic plan** that share a lower API quota, and the bot is in the **premium plan** with a higher API quota: ![Consumer groups of basic plan and premium plan](https://static.api7.ai/uploads/2024/12/12/VMcONfw0_consumer_groups.svg) By using consumer groups, you do not need to repetitively configure [`limit-count`](https://docs.api7.ai/hub/limit-count) on each consumer. Passing Consumer Group Information to Upstream[​](https://docs.api7.ai/apisix/key-concepts/consumer-groups/#passing-consumer-group-information-to-upstream "Direct link to Passing Consumer Group Information to Upstream") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Similar to [passing consumer information to upstream](https://docs.api7.ai/apisix/key-concepts/consumers#passing-consumer-information-to-upstream) , you can use [`proxy-rewrite`](https://docs.api7.ai/hub/proxy-rewrite) plugin on consumer groups to include the needed information in the header: { "plugins":{ ..., "proxy-rewrite":{ "headers":{ "set":{ "X-Consumer-Group-ID":"$consumer_group_id" } } } }} Consumer Group Access Restriction[​](https://docs.api7.ai/apisix/key-concepts/consumer-groups/#consumer-group-access-restriction "Direct link to Consumer Group Access Restriction") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- You can control request access to your API by imposing restrictions based on consumer group ID, HTTP methods, or other parameters in the `consumer-restriction` plugin. For example, if you want to strictly restrict the HTTP method for a consumer group called `cg-data-uploading` to use PUT, you can update the plugin's configuration as follows: { "plugins":{ ..., "consumer-restriction":{ "type":"consumer_group_id", "allowed_by_methods":[ { "user":"cg-data-uploading", "methods":["PUT"] } ] } }} The [`consumer-restriction`](https://docs.api7.ai/hub/consumer-restriction) plugin can also be used with [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) , [routes](https://docs.api7.ai/apisix/key-concepts/routes) , [services](https://docs.api7.ai/apisix/key-concepts/services) , and [consumer groups](https://docs.api7.ai/apisix/key-concepts/consumer-groups) . Additional Resources[​](https://docs.api7.ai/apisix/key-concepts/consumer-groups/#additional-resources "Direct link to Additional Resources") ---------------------------------------------------------------------------------------------------------------------------------------------- * Key Concepts * [Consumers](https://docs.api7.ai/apisix/key-concepts/consumers) * [Credentials](https://docs.api7.ai/apisix/key-concepts/credentials) * Admin API - [Consumer Groups](https://docs.api7.ai/apisix/reference/admin-api#tag/Consumer-Group) * [Overview](https://docs.api7.ai/apisix/key-concepts/consumer-groups/#overview) * [Passing Consumer Group Information to Upstream](https://docs.api7.ai/apisix/key-concepts/consumer-groups/#passing-consumer-group-information-to-upstream) * [Consumer Group Access Restriction](https://docs.api7.ai/apisix/key-concepts/consumer-groups/#consumer-group-access-restriction) * [Additional Resources](https://docs.api7.ai/apisix/key-concepts/consumer-groups/#additional-resources) --- # Permission Policies and Boundaries | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In API7 Enterprise, permission policies and boundaries are core access control mechanisms to ensure resource security and compliance with user roles. * **Permission Policies** refer to a set of rules and guidelines that clearly define the operations users are authorized to perform and the resource scope they can access, such as gateway groups and published services. A single permission policy can be associated with multiple roles, granting all users in those roles the specified permissions. It can also be applied as a permission boundary for certain users to prevent unauthorized access. * **Permission Boundaries** refer to the limits or permission scope, usually defining the resources a user can access or the operations they can perform. Permission boundaries can be used to set the maximum permissions an entity can have, regardless of their assigned roles or policies. Users cannot exceed the defined access scope, even if broader permissions are granted by their roles. API7 Enterprise includes a built-in `Super Admin` role, which is associated with the built-in permission policy called `super-admin-permission-policy`, granting full access to all operations and resources within API7 Enterprise. Besides, API7 Enterprise follows a "deny overrides allow" principle for permission policies. If any policy for a role or user contains a "deny" statement, it invalidates any "allow" statements for the same resource or action, regardless of other associated policies. Consider John, a development team member, with his restricted permission boundary to `Full Access to Test Gateway Group`. To manage developer permissions in a more granular way, the `Development Team Member` role is assigned the permission policy `Full Access to Test Gateway Group`, while the Test Engineer role is assigned the permission policy `Full Access to Prod Gateway Group`. Since a user's effective permissions are determined by the intersection of permissions attached to roles and permission boundaries, even if John’s role is changed to `Test Engineer`, his permission boundary remains unchanged and John still cannot access the prod gateway group. ![How Permission Policies and Boundaries Work in API7 Enterprise](https://static.api7.ai/uploads/2024/11/27/lhwvAzlP_permission-policy-and-boundary.png) Features of Permission Policies and Boundaries[​](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#features-of-permission-policies-and-boundaries "Direct link to Features of Permission Policies and Boundaries") ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- * Permission policies and permission boundaries must be applied together with roles and users. * Dynamic permission management allows changes to roles as positions shift without affecting the established permission rules. * Setting permission boundaries minimizes the risk of misconfigurations and effectively prevents excessive permissions and potential security vulnerabilities. * Granular access control supports defining precise access permissions for specific resources and users. * Permission policies are flexible and can be combined in various ways to meet complex permission management needs. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#use-cases "Direct link to Use Cases") -------------------------------------------------------------------------------------------------------------------------------------- ### Prevent Unauthorized Access[​](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#prevent-unauthorized-access "Direct link to Prevent Unauthorized Access") Frequent role changes within organizations necessitate robust safeguards. Permission boundaries can effectively prevent users in one department from accessing resources owned by another. For example, users in department A can be restricted from accessing sensitive files and systems belonging to department B to maintain information isolation and security. Furthermore, permission boundaries can prevent users from inadvertently accessing critical system components, such as licenses or organizational settings. Unauthorized access to these critical components could result in severe security risks. By explicitly denying access to these parts, organizations can build a strong defense to protect sensitive configurations from unauthorized modification or breaches. ### Isolate Development Environments[​](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#isolate-development-environments "Direct link to Isolate Development Environments") Before introducing permission policies and boundaries, isolating development environments posed numerous challenges that impacted security, compliance, and productivity. API7 Enterprise addresses these issues by leveraging permission policies and boundaries to logically separate development environments, ensuring that each role only has access to the resources necessary for their responsibilities. For example, associate the `Development Team Member` role with `Full Access to Test Gateway Group`, enabling modifications exclusively in the testing environment. Assign the `Development Team Lead` both `Full Access to Test Gateway Group` and `Full Access to UAT Gateway Group`, granting access to both testing and UAT environments. Grant the Test Engineer both `Full Access to UAT Gateway Group` and `Full Access to Prod Gateway Group`, confining their work to UAT and production environments for API testing and release tasks. ### Granular Permission Management[​](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#granular-permission-management "Direct link to Granular Permission Management") In enterprises with diverse teams and multi-environment operations, implementing fine-grained permission management is essential to maintain security and ensure compliance. API7 Enterprise addresses this need through advanced role assignments and permission policies, enabling tailored access control. For instance, application engineers can be assigned permission policies specific to the test environment, while operations engineers are granted access to production resources through separate policies. Additionally, individual users can be assigned specific permission boundaries, restricting their access to designated gateway group resources. This prevents permission overlaps and mitigates risks caused by misconfigurations. By confining teams to their allocated permissions, API7 Enterprise ensures operational independence across environments while simplifying permission management and configuration, resulting in improved efficiency and security. * [Features of Permission Policies and Boundaries](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#features-of-permission-policies-and-boundaries) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#use-cases) * [Prevent Unauthorized Access](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#prevent-unauthorized-access) * [Isolate Development Environments](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#isolate-development-environments) * [Granular Permission Management](https://docs.api7.ai/apisix/enterprise-feature/permission-policies-and-boundaries/#granular-permission-management) --- # Consumers | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/consumers/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concepts of consumers in APISIX and why you need them. You will be introduced to a few relevant concepts, including how to pass consumer information to upstream, consumer access restriction, as well as consumer authentication and authorization. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/consumers/#overview "Direct link to Overview") ---------------------------------------------------------------------------------------------------- In APISIX, a _consumer_ object represents a user, application, or host that sends requests to the API gateway and consumes backend services. It is used in conjunction with the authentication system; that is, every consumer is associated with at least one authentication plugin. Consumer objects come in handy if you have different consumers sending requests to your system, and you need APISIX to perform certain functions, such as rate limiting, based on consumers. These functionalities are provided by APISIX plugins configured in consumers. The following diagram illustrates an example of APISIX with one [`key-auth`](https://docs.api7.ai/hub/key-auth) enabled route and two consumers, John and Jane. John and Jane each authenticates to the route with their [credentials](https://docs.api7.ai/apisix/key-concepts/credentials) . Only authenticated requests are then allowed to access the upstream resource: ![consumers diagram](https://static.api7.ai/uploads/2024/09/12/nJhSZ7tC_consumers-label-udpated.svg) The configuration above ensures if a request is sent to APISIX: * without any key or with a wrong key, the request is rejected. * with `john-key`, the request is authenticated and forwarded to the upstream service. * with `jane-key`, the request is authenticated and forwarded to the upstream service. The rate limiting plugin [`limit-count`](https://docs.api7.ai/hub/limit-count) on the consumer also takes effect, limiting the number of requests to a maximum of 2 within any 5-second window. If the rate limit threshold is exceeded, the request will be rejected. Note that when a consumer is successfully authenticated, APISIX adds additional headers, such as `X-Consumer-Username`, `X-Credential-Identifier`, `X-Consumer-Custom-ID`, to the request, before proxying it to the upstream service. The upstream service will be able to differentiate between consumers and implement additional logic as needed. If any of these values is not available, the corresponding header will not be added. Passing Consumer Information to Upstream[​](https://docs.api7.ai/apisix/key-concepts/consumers/#passing-consumer-information-to-upstream "Direct link to Passing Consumer Information to Upstream") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- For certain use cases, such as logging, analytics, and auditing, you might want to pass consumer information to upstream services. Consumer information, by default, is not exposed to upstream; however, you can use `proxy-rewrite` plugin to include the needed information in the header: { "plugins":{ ..., "proxy-rewrite":{ "headers":{ "set":{ "X-Consumer-Name":"$consumer_name" } } } }} Consumer Access Restriction[​](https://docs.api7.ai/apisix/key-concepts/consumers/#consumer-access-restriction "Direct link to Consumer Access Restriction") ------------------------------------------------------------------------------------------------------------------------------------------------------------- You can control request access to your API by imposing restrictions based on consumer name, HTTP methods, or other parameters in the `consumer-restriction` plugin. For example, if you want to blacklist `Jane` from accessing your upstream service without changing any consumers configuration in the example from [overview](https://docs.api7.ai/apisix/key-concepts/consumers/#overview) , you can update the plugin's configuration in the route to the following: { "plugins":{ "key-auth":{}, "consumer-restriction":{ "blacklist":["Jane"] } }} Or, if you want to strictly allow `FetchBot`'s access by HTTP GET method, you can update the plugin's configuration (in either the route or the consumer) to the following: { "plugins":{ ..., "consumer-restriction":{ "allowed_by_methods":[ { "user":"FetchBot", "methods":["GET"] } ] } }} The [`consumer-restriction`](https://docs.api7.ai/hub/consumer-restriction) plugin can also be used with [routes](https://docs.api7.ai/apisix/key-concepts/routes) , [services](https://docs.api7.ai/apisix/key-concepts/services) , and [consumer groups](https://docs.api7.ai/apisix/key-concepts/consumer-groups) . Authentication & Authorization[​](https://docs.api7.ai/apisix/key-concepts/consumers/#authentication--authorization "Direct link to Authentication & Authorization") --------------------------------------------------------------------------------------------------------------------------------------------------------------------- There are two main design patterns for building authentication and authorization in an APISIX-based architecture. The first and most commonly adopted approach is to authenticate and authorize requests through a third-party [identity provider (IdP)](https://en.wikipedia.org/wiki/Identity_provider) , such as [Keycloak](https://www.keycloak.org/) : ![APISIX integration with an IdP](https://static.api7.ai/uploads/2023/03/16/N8W31TWC_consumers-auth1.svg) In some environments, a request might need to go through more than one IdP before it can be forwarded to the upstream service. In such cases, you can configure multiple authentication plugins, each corresponding to an IdP, on one consumer; only when all IdPs have granted access to a request will APISIX show a success response. With multiple authentication plugins in place, the [plugins order of execution](https://docs.api7.ai/apisix/key-concepts/plugins#plugins-execution-order) is determined by the plugin's priority, which can be overridden with [`_meta.priority`](https://docs.api7.ai/apisix/reference/plugin-common-configurations#_metapriority) . The second and a more basic approach is to perform authentication and authorization on the API gateway itself, using `key-auth`, `basic-auth`, `jwt-auth`, `hmac-auth` plugins: ![APISIX performs auth without IdP](https://static.api7.ai/uploads/2023/03/16/UGxTDGut_consumers-auth2.svg) For details about how to configure authentication and authorization for your specific needs, please refer to the [authentication section](https://docs.api7.ai/apisix/how-to-guide/authentication/implement-basic-auth) in How-To Guides. Additional Resource(s)[​](https://docs.api7.ai/apisix/key-concepts/consumers/#additional-resources "Direct link to Additional Resource(s)") -------------------------------------------------------------------------------------------------------------------------------------------- * Getting Started - [Configure Key Authentication](https://docs.api7.ai/apisix/getting-started/key-authentication) * Admin API - [Consumer](https://docs.api7.ai/apisix/reference/admin-api#tag/Consumer) * [`consumer-restriction`](https://docs.api7.ai/hub/consumer-restriction) plugin * Key Concepts - [Credentials](https://docs.api7.ai/apisix/key-concepts/credentials) * [Overview](https://docs.api7.ai/apisix/key-concepts/consumers/#overview) * [Passing Consumer Information to Upstream](https://docs.api7.ai/apisix/key-concepts/consumers/#passing-consumer-information-to-upstream) * [Consumer Access Restriction](https://docs.api7.ai/apisix/key-concepts/consumers/#consumer-access-restriction) * [Authentication & Authorization](https://docs.api7.ai/apisix/key-concepts/consumers/#authentication--authorization) * [Additional Resource(s)](https://docs.api7.ai/apisix/key-concepts/consumers/#additional-resources) --- # Secrets | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/secrets/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of secrets in APISIX and why you may need them. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/secrets/#overview "Direct link to Overview") -------------------------------------------------------------------------------------------------- In APISIX, a _secret_ object is used to set up integration with an external secret manager, so that APISIX can establish connections and fetch secrets from the secret manager dynamically at runtime. The following diagram illustrates the concept of a secret object using an example, where [`key-auth`](https://docs.api7.ai/hub/key-auth) is enabled for a user, John, and user credentials are stored in a [HashiCorp Vault](https://www.vaultproject.io/) server: ![secrets diagram example when using Vault as the external secret manager to store key for key authentication](https://static.api7.ai/uploads/2024/09/12/8XIiyG1q_secrets_cred_updated.svg) As demonstrated, when APISIX is used in conjunction with an external secret manager, the field for secret is defined as a variable starting with a fixed prefix `$secret://`, appended with the name of the secret manager, APISIX secret object ID, username, and other details. Specifically, if Vault is used as the secret manager, the APISIX secret object should specify: * `uri`: location where Vault server is hosted * `prefix`: path prefix corresponding to a secret engine that Vault should route traffic to * `token`: token for APISIX to authenticate to Vault and establish connection These configurations ensure that John can send requests to APISIX and access the back-end service with the correct key. Requests from unauthenticated users are rejected by APISIX. In addition to Vault, APISIX also supports the integration with AWS and GCP secret managers. For more details on the secret configurations, please refer to the [Admin API](https://docs.api7.ai/apisix/reference/admin-api#tag/Secret) . Additional Resources[​](https://docs.api7.ai/apisix/key-concepts/secrets/#additional-resources "Direct link to Additional Resources") -------------------------------------------------------------------------------------------------------------------------------------- * Getting Started - [Key Authentication](https://docs.api7.ai/apisix/getting-started/key-authentication) * Key Concepts * [Plugins](https://docs.api7.ai/apisix/key-concepts/plugins) * [Consumers](https://docs.api7.ai/apisix/key-concepts/consumers) * [Credentials](https://docs.api7.ai/apisix/key-concepts/credentials) * [Overview](https://docs.api7.ai/apisix/key-concepts/secrets/#overview) * [Additional Resources](https://docs.api7.ai/apisix/key-concepts/secrets/#additional-resources) --- # Dashboard SSO Options | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page The Single Sign-On (SSO) feature in the API7 Enterprise Dashboard simplifies user authentication by enabling access with a single set of credentials. By integrating with identity providers, such as Keycloak, Microsoft Entra ID (formerly Azure AD), Google, and Okta, enabling SSO eliminates the need for users to manage multiple logins, reducing friction and enhancing security. This centralized approach not only improves user experience but also strengthens compliance by allowing the enforcement of consistent access policies across all integrated systems. The API7 Dashboard supports **LDAP**, **OpenID Connect (OIDC)**, and **SAML** protocols for SSO. LDAP integrates with directory services for centralized user authentication and management, making it a reliable choice for legacy systems and enterprise environments. OIDC, a modern protocol built on OAuth 2.0, provides token-based authentication for secure and user-friendly access to web and mobile applications. SAML, widely used in enterprise SSO setups, facilitates the secure exchange of authentication data between identity providers and service providers. Consider an organization that uses API7 Enterprise to manage its API ecosystem, where administrators and developers regularly access the API7 Dashboard. Administrators handle tasks such as configuring routes, monitoring performance, and enforcing security policies, while developers are granted limited permissions, such as managing routes, to focus on their specific responsibilities. Without SSO, users need to maintain credentials specifically for the API7 Dashboard. To streamline access, the organization integrates the API7 Dashboard with an identity provider supporting SAML, OpenID Connect (OIDC), or LDAP. Users now can log in to the Dashboard using their existing corporate credentials managed in the identity provider. API7 Enterprise validates tokens or assertions from the identity provider during login and redirects users to the Dashboard if successfully authenticated. This approach simplifies login for users, enforces consistent role-based access controls, and strengthens governance across the organization. ![SSO login with API7 dashboard diagram](https://static.api7.ai/uploads/2024/11/27/IoTn3XAt_sso.png) Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#key-features "Direct link to Key Features") -------------------------------------------------------------------------------------------------------------------------- * Integrate with identity providers using OpenID Connect (OIDC), SAML, or LDAP protocols. * Allow customization of the SSO login option text on the API7 Dashboard login page. For instance, the SSO login button can be customized to display `Log in with Employee ID`. * Enable SCIM provisioning to automatically synchronize user identities, roles, and access permissions from the identity provider, preventing configuration discrepancies. * Support the definition of role mapping and permission mapping rules, which assign API7 roles and permissions to imported users by evaluating user attributes they were originally assigned in the identity providers. * Reduce the risk of credential theft by minimizing repeated logins and mitigating password sprawl, where managing multiple credentials increases the chance of insecure storage and potential leaks. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#use-cases "Direct link to Use Cases") ----------------------------------------------------------------------------------------------------------------- ### Simplify Dashboard Access with SSO[​](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#simplify-dashboard-access-with-sso "Direct link to Simplify Dashboard Access with SSO") For organizations managing APIs with API7 Enterprise, the Dashboard serves as a central hub for tasks like route configuration, performance tracking, and policy management. Enabling SSO allows users to access the Dashboard using their existing corporate credentials. This approach simplifies user authentication, reduces the overhead of managing multiple credentials, and ensures access controls align with organizational policies, fostering both security and efficiency. ### Synchronize Roles and Permissions[​](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#synchronize-roles-and-permissions "Direct link to Synchronize Roles and Permissions") API7 Enterprise enables organizations to enforce consistent access controls by using role mapping and permission mapping rules. These mappings ensure that API7 roles and permissions are dynamically assigned to imported users based on attributes defined in the identity provider, such as department, job title, or group membership. The approach eliminates manual configuration errors and ensures that all users are granted access privileges aligned with organizational policies. For example, a user assigned to the `CloudOps` team in the identity provider can automatically receive the role of a `Super Admin` in API7 Dashboard, which comes with escalated administrative privileges to manage API7 Enterprise resources to meet the operational needs. If the user's team changes in the identity provider, the role mapping will be dynamically updated the next time they log into API7 Enterprise. Similarly, the permission boundary mapping is also dynamic, should the relevant attributes change in the identity provider. By leveraging attribute-based evaluations, API7 Enterprise allows organizations to adapt governance policies as their workforce or business structure evolves, ensuring policies remain effective and relevant. ### Centralize Identity Management[​](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#centralize-identity-management "Direct link to Centralize Identity Management") When API7 Enterprise integrates with an identity provider for authentication, it designates the identity provider as the single source of truth for user identities and access control. This means administrators only need to manage the identity provider to handle tasks like provisioning, de-provisioning, and updating user roles or attributes. Changes made in the identity provider will be propagated to API7 Dashboard, ensuring consistent and accurate access control without the risk of configuration drift, effectively simplifying administration and preventing orphaned accounts. ### Bridge Access in Hybrid Cloud Environments[​](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#bridge-access-in-hybrid-cloud-environments "Direct link to Bridge Access in Hybrid Cloud Environments") In organizations deploying a mix of on-premises and cloud-based applications, the SSO option in API7 Dashboard helps facilitate consistent authentication across both environments. Legacy on-premises systems, which could still remain critical to many operations, can utilize LDAP for authentication, ensuring compatibility with established infrastructures. Meanwhile, modern cloud-native platforms are typically designed to integrate with protocols such as OIDC and SAML, offering the scalability and flexibility required for dynamic IT landscapes. By bridging these environments, SSO simplifies identity management and enhances the user experience, eliminating the need for multiple credentials and repetitive logins. * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#key-features) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#use-cases) * [Simplify Dashboard Access with SSO](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#simplify-dashboard-access-with-sso) * [Synchronize Roles and Permissions](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#synchronize-roles-and-permissions) * [Centralize Identity Management](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#centralize-identity-management) * [Bridge Access in Hybrid Cloud Environments](https://docs.api7.ai/apisix/enterprise-feature/dashboard-sso/#bridge-access-in-hybrid-cloud-environments) --- # Stream Routes | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/stream-routes/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of stream routes in APISIX and why you may need them. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/stream-routes/#overview "Direct link to Overview") -------------------------------------------------------------------------------------------------------- In APISIX, a _stream route_ object is used to create a route when APISIX functions as a stream proxy, operating on the [transport layer](https://en.wikipedia.org/wiki/Transport_layer) for TCP and UDP connections. This is analogous to the concept of routes in cases where APISIX functions as an [application-layer](https://en.wikipedia.org/wiki/Application_layer) proxy. The following diagram illustrates the concept of a stream route object. In this example, APISIX functions as a stream proxy for a [MySQL](https://www.mysql.com/) server that uses TCP protocol for communication. APISIX is deployed at IP address `192.168.1.10` and configured to listen for TCP connections on port `9500`: ![APISIX and MySQL Server with one stream route restricting on client IP address](https://static.api7.ai/uploads/2023/04/28/NxlpM4Yf_stream_routes.svg) The `client_addr` field in the stream route filters on the client IP, ensuring that only requests originating from the specified IP address are allowed to pass through this route to the upstream MySQL server. While both clients have used the correct credentials to connect to the MySQL server, only the client with IP address whitelisted in `client_addr` is permitted to establish a connection. In addition to the `client_addr` field, there are other fields such as `server_addr` and `server_port` that can be configured to filter incoming requests before rejecting or forwarding them to upstream services. To learn more about these fields and other configuration options, please refer to the Admin API reference for stream routes (coming soon). For detailed instructions on how to configure APISIX as a stream proxy, see the [Proxy Transport Layer (L4) Traffic](https://docs.api7.ai/apisix/how-to-guide/traffic-management/proxy-transport-layer-l4-traffic) how-to guide. Additional Resources[​](https://docs.api7.ai/apisix/key-concepts/stream-routes/#additional-resources "Direct link to Additional Resources") -------------------------------------------------------------------------------------------------------------------------------------------- * Getting Started - [Configure Routes](https://docs.api7.ai/apisix/getting-started/configure-routes) * Key Concepts - [Routes](https://docs.api7.ai/apisix/key-concepts/routes) * Admin API - [Stream Route](https://docs.api7.ai/apisix/reference/admin-api#tag/Stream-Route) * [Overview](https://docs.api7.ai/apisix/key-concepts/stream-routes/#overview) * [Additional Resources](https://docs.api7.ai/apisix/key-concepts/stream-routes/#additional-resources) --- # Protos | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/protos/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of protos in APISIX and why you may need them. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/protos/#overview "Direct link to Overview") ------------------------------------------------------------------------------------------------- In APISIX, _proto_ objects contain [protocol buffer (protobuf)](https://protobuf.dev/) definitions which define the service interface and message types used in communication with upstream [gRPC](https://grpc.io/) services. The following diagram illustrates the concept of a proto object using an example of APISIX transcoding between HTTP and gRPC. In this example, the route `/grpc-echo` is associated with the plugin `grpc-transcode` and a proto object: ![Diagram of APISIX transcoding between HTTP and gRPC](https://static.api7.ai/uploads/2023/05/06/8X50cIcx_protos.svg) The gRPC server is registered with `EchoService` defined in `echo.proto` file, which echoes back string input from incoming requests. To enable gRPC communication between APISIX and server, the protocol buffer definitions specified in the `echo.proto` file are added to the proto object in APISIX. This ensures that APISIX and the gRPC server agree on the specifications of data exchange, allowing APISIX to effectively communicate with the gRPC server and relay the responses back to the client over HTTP. To learn more about how to use `grpc-transcode` for protocol transcoding, see [Transcode HTTP to gRPC](https://docs.api7.ai/apisix/how-to-guide/transformation/transcode-http-to-grpc) . Additional Resources[​](https://docs.api7.ai/apisix/key-concepts/protos/#additional-resources "Direct link to Additional Resources") ------------------------------------------------------------------------------------------------------------------------------------- * Key Concepts - [Routes](https://docs.api7.ai/apisix/key-concepts/routes) * How-To Guide - [Transcode HTTP to gRPC](https://docs.api7.ai/apisix/how-to-guide/transformation/transcode-http-to-grpc) * Admin API - [Protos](https://docs.api7.ai/apisix/reference/admin-api#tag/Proto) * [Overview](https://docs.api7.ai/apisix/key-concepts/protos/#overview) * [Additional Resources](https://docs.api7.ai/apisix/key-concepts/protos/#additional-resources) --- # Autoscale APISIX Gateway (AWS EC2) | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Autoscaling is a mechanism that automatically adjusts the resources available to the gateway, ensuring consistent performance under varying traffic loads. This guide walks you through deploying APISIX in [decoupled mode](https://docs.api7.ai/apisix/production/deployment-modes#decoupled-mode) on EC2, configuring an Auto Scaling Group (ASG) for the APISIX gateway (also referred to as APISIX Data Plane or DP), defining scaling policies, and validating scaling behavior with a simple load test so your gateway can automatically respond to traffic changes. Prerequisites[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#prerequisites "Direct link to Prerequisites") -------------------------------------------------------------------------------------------------------------------------------------------- * An AWS account. * Sufficient IAM permissions to create and manage the following AWS resources: * EC2 instances * Auto Scaling Groups * Launch Templates * Security Groups * Ensure you have a VPC configured in the target AWS region, with the necessary subnets for your EC2 instances. Configure Security Groups[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#configure-security-groups "Direct link to Configure Security Groups") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- This diagram visualizes traffic between APISIX and its supporting components, highlighting the purpose of each security group and the rules that need to be configured: note The following security group configurations are provided for reference. They follow a least-privilege principle and assume the presence of an ALB for distributing traffic to APISIX DP instances. You should adapt these rules to fit your own network and security requirements. In AWS Console, navigate to **EC2 > Security Groups**. Create the following security groups (SGs) and configure their inbound and outbound rules: **1\. Application Load Balancer (ALB) Security Group** * Inbound * HTTP `80` and HTTPS `443` from `0.0.0.0/0` (anywhere) * Outbound * Custom TCP `9080` to APISIX DP security group **2\. APISIX Control Plane (CP) Security Group** * Inbound * SSH `22` from a trusted source, such as a bastion host or your own IP range * TCP `9180` from a trusted source, such as a bastion host or your own APISIX Admin IP range * Outbound * Custom TCP `2379` to ETCD security group **3\. APISIX Data Plane (DP) Security Group** * Inbound * SSH `22` from a trusted source, such as a bastion host or your own IP range * Custom TCP `9080` from ALB security group * Outbound * Custom TCP `2379` to ETCD security group * HTTP `80` and HTTPS `443` to `0.0.0.0/0` (anywhere) **4\. ETCD Security Group** * Inbound * SSH `22` from a trusted source, such as a bastion host or your own IP range * Custom TCP `2379` from APISIX CP security group * Custom TCP `2379` from APISIX DP security group * Outbound * Custom TCP `2379` to APISIX CP security group * Custom TCP `2379` to APISIX DP security group ![The created security groups in AWS](https://static.api7.ai/uploads/2025/12/19/2SgftzC4_SG.png) Launch EC2 Instances[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#launch-ec2-instances "Direct link to Launch EC2 Instances") ----------------------------------------------------------------------------------------------------------------------------------------------------------------- In this section, you will launch EC2 instances and install etcd and the APISIX CP. note This section walks you through installing a single etcd instance and a single APISIX CP instance for simplicity, as the guide focuses on DP autoscaling. In production, it is recommended to run multiple etcd nodes in a cluster to ensure high availability and fault tolerance. The installation uses Ubuntu EC2 instances on amd64 architecture. If you choose a different Linux distribution or architecture, adjust the installation scripts accordingly. Instead of manually creating APISIX DP instances, you will later define a launch template and use an ASG to automatically create and scale APISIX DP instances. ### etcd[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#etcd "Direct link to etcd") Navigate to **EC2** and launch an EC2 instance to host etcd with the following configurations: * **AMI (Amazon Machine Image):** Select Ubuntu. You may use a different Linux distribution if preferred, but ensure the installation steps are adjusted accordingly. * **Instance type:** Select a size suitable for your workload. * **Key pair:** Choose an existing key pair or create a new one. This key is required to connect to the EC2 instance via SSH. If you proceed without a key pair, you can still connect to the instance using the AWS Console “Connect” option, but SSH access from your local machine will not be available. * **Network settings:** Choose the VPC and select the existing etcd security group. Review your configuration and launch the instance. After the instance is launched, make a note of its private IP, which will be used by etcd and APISIX for communication: # Replace with the private IP of your etcd instanceETCD_INSTANCE_PRIVATE_IP=172.31.19.201 Connect to the instance (using SSH or the AWS Console "Connect" option) and run the following commands to install etcd: # Download and extract etcdETCD_VERSION='3.5.4'wget https://github.com/etcd-io/etcd/releases/download/v${ETCD_VERSION}/etcd-v${ETCD_VERSION}-linux-amd64.tar.gztar -xvf etcd-v${ETCD_VERSION}-linux-amd64.tar.gz# Copy binaries to /usr/bincd etcd-v${ETCD_VERSION}-linux-amd64sudo cp -a etcd etcdctl /usr/bin/# Start etcdnohup etcd \ --name s1 \ --data-dir /tmp/etcd-data \ --listen-client-urls http://0.0.0.0:2379 \ --advertise-client-urls http://${ETCD_INSTANCE_PRIVATE_IP}:2379 \ >/tmp/etcd.log 2>&1 & ### APISIX CP[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#apisix-cp "Direct link to APISIX CP") Navigate to **EC2** and launch an EC2 instance to host APISIX CP with the following configurations: * **AMI (Amazon Machine Image):** Select Ubuntu. You may use a different Linux distribution if preferred, but ensure the installation steps are adjusted accordingly. * **Instance type:** Select a size suitable for your workload. * **Key pair:** Choose an existing key pair or create a new one. This key is required to connect to the EC2 instance via SSH. If you proceed without a key pair, you can still connect to the instance using the AWS Console “Connect” option, but SSH access from your local machine will not be available. * **Network settings:** Choose the VPC and select the existing APISIX CP security group. Review your configuration and launch the instance. Connect to the instance (using SSH or the AWS Console "Connect" option). Run the commands below to install APISIX: # Add key and repo (amd64)wget -O - http://repos.apiseven.com/pubkey.gpg | sudo apt-key add -echo "deb http://repos.apiseven.com/packages/debian bullseye main" | sudo tee /etc/apt/sources.list.d/apisix.list# Update packages and install APISIXsudo apt updatesudo apt install -y apisix=3.14.0-0 Edit the APISIX configuration file: sudo vi /usr/local/apisix/conf/config.yaml Update the configuration to configure this instance as the control plane and connect it to etcd: config.yaml deployment: role: control_plane role_control_plane: config_provider: etcd etcd: host: - http://172.31.19.201:2379 # Replace with the private IP of your etcd instance admin: admin_key: - name: admin key: 'Sup3rs3cretWr1teK3y' # Replace with your admin key role: admin Initialize the configuration and start the APISIX server: sudo apisix initsudo apisix start Create a Launch Template[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#create-a-launch-template "Direct link to Create a Launch Template") ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In this section, a launch template is used to define how APISIX DP instances are created. It contains the instance configuration—such as the AMI, instance type, security groups, and startup scripts—that the ASG uses to launch and manage DP instances. Using a launch template ensures that all DP instances are created consistently and allows the ASG to automatically add or remove instances as traffic changes. Navigate to **EC2 > Launch Templates** and create a launch template with the following configurations: * **Auto Scaling guidance:** Enable this option to indicate the template will be used by an ASG. * **AMI (Amazon Machine Image):** Select Ubuntu. You may use a different Linux distribution if preferred, but ensure that the user data script is adjusted accordingly. * **Instance type:** Select a size suitable for your workload. * **Network settings:** Select the existing APISIX DP security group. * **Advanced details > User data:** Paste in the script below. #!/bin/bashset -e# Replace with the private IP of your etcd instanceETCD_IP="172.31.19.201"APISIX_VERSION="3.14.0-0"# Add APISIX repository (amd64)wget -O - http://repos.apiseven.com/pubkey.gpg | apt-key add -echo "deb http://repos.apiseven.com/packages/debian bullseye main" \ > /etc/apt/sources.list.d/apisix.list# Install APISIXapt updateapt install -y apisix=${APISIX_VERSION}# Configure APISIX as DPcat >/usr/local/apisix/conf/config.yaml < Auto Scaling Groups** and create an ASG with the following configurations: * **Launch Template:** Select the launch template for APISIX DP instances created in the last step. * **Network Settings:** Select the appropriate VPC, Availability Zones, and subnets. * **Load Balancer:** Select **No Load Balancer** if a load balancer is not currently available, or choose the appropriate load balancer and target groups if they have been configured. * **Group size:** Set the **Desired capacity** to 1 (adjust as needed based on your workload). * **Scaling:** * Set the **Min desired capacity** to 1 and the **Max desired capacity** to 5 (adjust as needed based on your workload). * Select **Target tracking scaling policy** * **Scaling policy name:** `Scale on CPU` * **Target value:** `50`. This means ASG will add APISIX DP instances when CPU exceeds 50% and remove instances when CPU drops below 50%. Review your configuration and create the ASG. After the ASG is created, you should see the ASG start a new EC2 instance for APISIX DP to meet the desired capacity. ![The ASG starts one DP instance and the desired capacity is also one](https://static.api7.ai/uploads/2025/12/19/5dpufROI_ASG-init.png) Verify CP and DP Connectivity[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#verify-cp-and-dp-connectivity "Direct link to Verify CP and DP Connectivity") -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Connect to the EC2 instance for APISIX CP (using SSH or the AWS Console "Connect" option). In the instance, create an example route using the [`mocking`](https://docs.api7.ai/hub/mocking) plugin so that requests to `/200` always return a `200` response without forwarding to any upstream: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: Sup3rs3cretWr1teK3y" \ -d '{ "id": "mocking200", "uri": "/200", "plugins": { "mocking": { "response_status": 200, "response_example": "200 OK from mocking plugin" } } }' Next, connect to the EC2 instance for APISIX DP (using SSH or the AWS Console "Connect" option). In the instance, send a request to the `/200` route: curl "http://127.0.0.1:9080/200" -i You should receive an `HTTP/1.1 200 OK` response. About Health Check The `/200` endpoint can later be used for ALB health checks. Alternatively, you can enable APISIX's Status API (default port `7085`) and use the `/status` endpoint for health checks. Configure Application Load Balancer (ALB)[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#configure-application-load-balancer-alb "Direct link to Configure Application Load Balancer (ALB)") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ An Application Load Balancer (ALB) distributes incoming traffic and performs health checks to ensure traffic is only routed to healthy instances. note Although this guide later verifies autoscaling by directly applying CPU load to APISIX DP instances rather than through an ALB, it includes ALB configuration instructions in this section for completeness. Users who deploy APISIX behind an ALB can examine instructions in this section to ensure proper traffic routing to ASG-managed gateway instances and optionally use ALB-based CloudWatch metrics for scaling. ### Create a Target Group[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#create-a-target-group "Direct link to Create a Target Group") Create a target group that the ALB will use to route traffic to APISIX DP instances. Navigate to **EC2 > Target Groups** and create a target group with the following configurations: * **Target type:** Instance * **Protocol:** HTTP * **Port:** `9080` * **VPC:** Select the same VPC as your ASG. * **Health check path:** `/200` (or another valid health check endpoint exposed by APISIX) * **Register targets:** Do not register specific instances manually. The ASG will handle instance registration after the target group is attached. Complete the target group creation and note the target group name, as it will be referenced when attaching the ALB to the ASG. ### Create an ALB[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#create-an-alb "Direct link to Create an ALB") Navigate to **EC2 > Load Balancers** and create an ALB with the following configurations: * **Scheme:** Internet-facing * **IP address type:** IPv4. * **Network mapping:** * Select the same VPC used by the ASG. * Choose at least two Availability Zones and their corresponding subnets. * **Security groups:** Select the existing ALB security group. * **Listeners and routing:** * Create an HTTP listener on port `80` (or HTTPS on port `443` if TLS is configured). * For the default action, forward traffic to the APISIX DP target group created earlier. Review your configuration and create the ALB. ### Attach Target Group to ASG[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#attach-target-group-to-asg "Direct link to Attach Target Group to ASG") Attach the target group to the ASG so that newly started APISIX DP instances will be automatically registered with the target group. * Navigate to **Auto Scaling Groups** and select the APISIX DP ASG. * Edit the **Load balancer target groups** under **Integrations > Load balancing** section. * Select **Attach to an existing load balancer**. * Attach the APISIX DP target group created earlier and save the change. ![Attach target group to ASG](https://static.api7.ai/uploads/2025/12/19/2AkS496u_attach-TG-to-ASG.png) Once attached, the ASG will automatically register new APISIX DP instances with the target group and deregister instances when they are terminated or scaled in. ### Verify ALB Routing[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#verify-alb-routing "Direct link to Verify ALB Routing") The ALB only routes traffic to healthy targets. To ensure your APISIX DP is reachable, check the health status of the target in the ALB target group: ![Target group health check shows one healthy target](https://static.api7.ai/uploads/2025/12/19/jyHEpwhU_TG-healthcheck.png) Next, navigate to the ALB details to find its DNS name, for instance: apisix-alb-xxxxxxxxx.ap-southeast-2.elb.amazonaws.com From your local machine, send a request to the health check route (or any route) to verify that traffic is correctly routed through the ALB: curl apisix-alb-xxxxxxxxx.ap-southeast-2.elb.amazonaws.com/200 -i If everything is configured correctly, you should receive a `200 OK response`. Verify AutoScaling[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#verify-autoscaling "Direct link to Verify AutoScaling") ----------------------------------------------------------------------------------------------------------------------------------------------------------- To confirm that your ASG for APISIX DP works correctly, you can simulate high CPU load on a DP instance using the `stress` utility. This allows you to observe whether new DP instances are automatically launched when CPU usage exceeds the scaling threshold. Connect to the DP instance (using SSH or the AWS Console "Connect" option) and run the following commands to install `stress`: sudo apt updatesudo apt install -y stress Check the number of CPU cores available: nproc Suppose there are 2 CPU cores available. Apply CPU load to both cores for 600 seconds (10 minutes): stress --cpu 2 --timeout 600 & To observe scaling while the `stress` command is running: 1. Navigate to **CloudWatch > Metrics** and select the `CPUUtilization` metric for the APISIX DP ASG. You should see the average CPU utilization gradually increase to above 50%. ![CloudWatch metrics](https://static.api7.ai/uploads/2025/12/19/RO73Gbow_metrics.png) 2. Navigate to the APISIX DP ASG and monitor the **Instances** and **Desired capacity** values. You should see these values gradually increase to the **Max desired capacity** as auto scaling is taking place. ![Scaling up to max number of instances](https://static.api7.ai/uploads/2025/12/19/NZIwrxB4_scaling-done.png) When the `stress` command finishes running, you should see the CPU utilization decreases over time. After the scale-in evaluation and cooldown period, the ASG gradually terminates excess instances. ![Scaling in](https://static.api7.ai/uploads/2025/12/19/YN523LJY_scaling%20in.png) This confirms that APISIX DP instances are automatically scaled by the ASG based on CPU load. Next Steps[​](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#next-steps "Direct link to Next Steps") ----------------------------------------------------------------------------------------------------------------------------------- In this guide, you have learned how to implement autoscaling for APISIX gateway instances based on CPU utilization on EC2. You are encouraged to further explore other metrics and strategies to optimize autoscaling for your workloads. For more information, see [Dynamic scaling for Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scale-based-on-demand.html) . * [Prerequisites](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#prerequisites) * [Configure Security Groups](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#configure-security-groups) * [Launch EC2 Instances](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#launch-ec2-instances) * [etcd](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#etcd) * [APISIX CP](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#apisix-cp) * [Create a Launch Template](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#create-a-launch-template) * [Create an Auto Scaling Group (ASG)](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#create-an-auto-scaling-group-asg) * [Verify CP and DP Connectivity](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#verify-cp-and-dp-connectivity) * [Configure Application Load Balancer (ALB)](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#configure-application-load-balancer-alb) * [Create a Target Group](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#create-a-target-group) * [Create an ALB](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#create-an-alb) * [Attach Target Group to ASG](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#attach-target-group-to-asg) * [Verify ALB Routing](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#verify-alb-routing) * [Verify AutoScaling](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#verify-autoscaling) * [Next Steps](https://docs.api7.ai/apisix/production/scaling/autoscale-apisix-gateway-aws/#next-steps) --- # Workflow | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/workflow/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `workflow` plugin supports the conditional execution of user-defined actions to client traffic based a given set of rules, defined using [APISIX expressions](https://docs.api7.ai/apisix/reference/apisix-expressions) . This provides a granular approach to traffic management. If you would like to apply more complex matching conditions and actions, see the [`traffic-label`](https://docs.api7.ai/hub/traffic-label) plugin. Examples[​](https://docs.api7.ai/hub/workflow/#examples "Direct link to Examples") ----------------------------------------------------------------------------------- The examples below demonstrates how you can use the `workflow` plugin for different scenarios. ### Return Response HTTP Status Code Conditionally[​](https://docs.api7.ai/hub/workflow/#return-response-http-status-code-conditionally "Direct link to Return Response HTTP Status Code Conditionally") The following example demonstrates a simple rule with one matching condition and one associated action to return HTTP status code conditionally. Create a route with the `workflow` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "workflow-route", "uri": "/anything/*", "plugins": { "workflow":{ "rules":[ { "case":[ ["uri", "==", "/anything/rejected"] ], "actions":[ [ "return", {"code": 403} ] ] } ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Trigger the action only when the request's URI path is `/anything/rejected`. ❷ Return HTTP status code 403 when the rule is matched. Send a request that matches none of the rules: curl -i "http://127.0.0.1:9080/anything/anything" You should receive an `HTTP/1.1 200 OK` response. Send a request that matches the configured rule: curl -i "http://127.0.0.1:9080/anything/rejected" You should receive an `HTTP/1.1 403 Forbidden` response of following: {"error_msg":"rejected by workflow"} ### Apply Rate Limiting Conditionally by URI and Query Parameter[​](https://docs.api7.ai/hub/workflow/#apply-rate-limiting-conditionally-by-uri-and-query-parameter "Direct link to Apply Rate Limiting Conditionally by URI and Query Parameter") The following example demonstrates a rule with two matching conditions and one associated action to rate limit requests conditionally. Create a route with the `workflow` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "workflow-route", "uri": "/anything/*", "plugins":{ "workflow":{ "rules":[ { "case":[ ["uri", "==", "/anything/rate-limit"], ["arg_env", "==", "v1"] ], "actions":[ [ "limit-count", { "count":1, "time_window":60, "rejected_code":429 } ] ] } ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Match URI path `/anything/rate-limit`. ❷ Match query parameter `env` whose value being `v1`. See [built-in variables](https://docs.api7.ai/hub/apisix/reference/built-in-variables) for more variables to help construct conditions. ❸ Apply rate limiting when both of the conditions are matched. Generate two consecutive requests that matches the second rule: curl -i "http://127.0.0.1:9080/anything/rate-limit?env=v1" You should receive an `HTTP/1.1 200 OK` response and an `HTTP 429 Too Many Requests` response. Generate requests that do not match the condition: curl -i "http://127.0.0.1:9080/anything/anything?env=v1" You should receive `HTTP/1.1 200 OK` responses for all requests, as they are not rate limited. ### Apply Rate Limiting Conditionally by Consumers[​](https://docs.api7.ai/hub/workflow/#apply-rate-limiting-conditionally-by-consumers "Direct link to Apply Rate Limiting Conditionally by Consumers") The following example demonstrates how to configure the plugin to perform rate limiting based on the following specifications: * consumer `john` should have a quota of 5 requests within a 30-second window * consumer `jane` should have a quota of 3 requests within a 30-second window * all other consumers should have a quota of 2 requests within a 30-second window While this example will be using [`key-auth`](https://docs.api7.ai/hub/key-auth) , you can easily replace it with other authentication plugins. Create a consumer `john`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-key-auth", "plugins": { "key-auth": { "key": "john-key" } } }' Create a second consumer `jane`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jane" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jane-key-auth", "plugins": { "key-auth": { "key": "jane-key" } } }' Create a third consumer `jimmy`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jimmy" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jimmy/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jimmy-key-auth", "plugins": { "key-auth": { "key": "jimmy-key" } } }' Create a route with the `workflow` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "workflow-route", "uri": "/anything", "plugins":{ "key-auth": {}, "workflow":{ "rules":[ { "actions": [ [ "limit-count", { "count": 5, "key": "consumer_john", "key_type": "constant", "rejected_code": 429, "time_window": 30, "policy": "local" } ] ], "case": [ [ "consumer_name", "==", "john" ] ] }, { "actions": [ [ "limit-count", { "count": 3, "key": "consumer_jane", "key_type": "constant", "rejected_code": 429, "time_window": 30, "policy": "local" } ] ], "case": [ [ "consumer_name", "==", "jane" ] ] }, { "actions": [ [ "limit-count", { "count": 2, "key": "$consumer_name", "key_type": "var", "rejected_code": 429, "time_window": 30, "policy": "local" } ] ] } ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Enable `key-auth` on the route. ❷ Match consumer `john` and apply a rate limiting quota of 5 requests within a 30-second window. ❸ Match consumer `jane` and apply a rate limiting quota of 3 requests within a 30-second window. ❹ Match all other consumers and apply a rate limiting quota of 2 requests within a 30-second window, per consumer. To verify, send 6 consecutive requests with `john`'s key: resp=$(seq 6 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'apikey: john-key' -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that out of the 6 requests, 5 requests were successful (status code 200) while the others were rejected (status code 429). 200: 5, 429: 1 Send 6 consecutive requests with `jane`'s key: resp=$(seq 6 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'apikey: jane-key' -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that out of the 6 requests, 3 requests were successful (status code 200) while the others were rejected (status code 429). 200: 3, 429: 3 Send 3 consecutive requests with `jimmy`'s key: resp=$(seq 3 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'apikey: jimmy-key' -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that out of the 3 requests, 2 requests were successful (status code 200) while the others were rejected (status code 429). 200: 2, 429: 1 ### Apply Advanced Rate Limiting with Sliding Window[​](https://docs.api7.ai/hub/workflow/#apply-advanced-rate-limiting-with-sliding-window "Direct link to Apply Advanced Rate Limiting with Sliding Window") The following example demonstrates how to configure `workflow` with the Enterprise `limit-count-advanced` plugin to perform rate limiting conditionally, using the sliding window algorithm. Create a route with the `workflow` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "workflow-route", "uri": "/anything/*", "plugins":{ "workflow":{ "rules":[ { "case": [ ["uri", "==", "/anything/rate-limit-advanced"] ], "actions": [ [ "limit-count-advanced", { "count": 5, "time_window": 10, "rejected_code": 429, "policy": "local", "key_type": "var", "key": "remote_addr", "window_type": "sliding" } ] ] } ] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } } }' ❶ Match URI path `/anything/rate-limit-advanced`. ❷ Apply rate limiting when the condition is matched. ❸ Set the rate limiting algorithm to sliding window. Generate 7 requests to the route that matches the condition every other second: for i in $(seq 7); do (curl -I "http://127.0.0.1:9080/anything/rate-limit-advanced" &) sleep 1done You should receive `HTTP/1.1 200 OK` responses for most requests, with the remainder being `HTTP 429 Too Many Requests responses`. If you send requests to the route with other paths, such as: curl -i "http://127.0.0.1:9080/anything/else" You should not observe any rate limiting in effect as the condition is not matched. * [Examples](https://docs.api7.ai/hub/workflow/#examples) * [Return Response HTTP Status Code Conditionally](https://docs.api7.ai/hub/workflow/#return-response-http-status-code-conditionally) * [Apply Rate Limiting Conditionally by URI and Query Parameter](https://docs.api7.ai/hub/workflow/#apply-rate-limiting-conditionally-by-uri-and-query-parameter) * [Apply Rate Limiting Conditionally by Consumers](https://docs.api7.ai/hub/workflow/#apply-rate-limiting-conditionally-by-consumers) * [Apply Advanced Rate Limiting with Sliding Window](https://docs.api7.ai/hub/workflow/#apply-advanced-rate-limiting-with-sliding-window) --- # AI Prompt Guard | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/ai-prompt-guard/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `ai-prompt-guard` plugin safeguards your LLM endpoints by inspecting and validating incoming prompt messages. It checks the content of requests against user-defined allowed and denied patterns to ensure that only approved inputs are forwarded to upstream LLM. Based on its configuration, the plugin can either examine just the latest message or the entire conversation history, and it can be set to check prompts from all roles or only from end users. When both `allow_patterns` and `deny_patterns` are configured, the plugin first ensures that at least one `allow_patterns` is matched. If none match, the request is rejected. If an allowed pattern is matched, it then checks for any occurrences of denied patterns. Demo[​](https://docs.api7.ai/hub/ai-prompt-guard/#demo "Direct link to Demo") ------------------------------------------------------------------------------ The following demo demonstrates the [implement allow and deny patterns example](https://docs.api7.ai/hub/ai-prompt-guard/#implement-allow-and-deny-patterns) in API7 Enterprise using the Dashboard, where you can validate user prompts by defining both allow and deny patterns and understand how the allow pattern takes precedence. Examples[​](https://docs.api7.ai/hub/ai-prompt-guard/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------ The following examples will be using OpenAI as the upstream service provider. Before proceeding, create an [OpenAI account](https://openai.com/) and an [API key](https://openai.com/blog/openai-api) . You can optionally save the key to an environment variable as such: export OPENAI_API_KEY=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 # replace with your API key If you are working with other LLM providers, please refer to the provider's documentation to obtain an API key. ### Implement Allow and Deny Patterns[​](https://docs.api7.ai/hub/ai-prompt-guard/#implement-allow-and-deny-patterns "Direct link to Implement Allow and Deny Patterns") The following example demonstrates how to use the `ai-prompt-guard` plugin to validate user prompts by defining both allow and deny patterns and understand how the allow pattern takes precedence. Define the allow and deny patterns. You can optionally save them to environment variables for easier escape: # allow US dollar amountexport ALLOW_PATTERN_1='\\$?\\(?\\d{1,3}(,\\d{3})*(\\.\\d{1,2})?\\)?'# deny phone number in US number formatexport DENY_PATTERN_1='(\\([0-9]{3}\\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}' * Admin API * ADC * Ingress Controller Create a route that uses `ai-proxy` to proxy to OpenAI and `ai-prompt-guard` to inspect input prompts: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-prompt-guard-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-proxy": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options":{ "model": "gpt-4" } }, "ai-prompt-guard": { "allow_patterns": [ "'"$ALLOW_PATTERN_1"'" ], "deny_patterns": [ "'"$DENY_PATTERN_1"'" ] } } }' ❶ Specify the provider to be `openai`. ❷ Attach the OpenAI API key in the `Authorization` header as a Bearer token. ❸ Specify the name of the model. ❹ Allow message pattern that matches any dollar amounts. ❺ Deny message patterns that include any US phone number format. Create a route with the `ai-prompt-guard` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: adc.yaml services: - name: prompt-guard-service routes: - name: prompt-guard-route uris: - /anything methods: - POST plugins: ai-proxy: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 ai-prompt-guard: allow_patterns: - '\$?\(?\d{1,3}(,\d{3})*(\.\d{1,2})?\)?' deny_patterns: - '(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}' Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Specify the provider to be `openai`. ❷ Attach the OpenAI API key in the `Authorization` header as a Bearer token. ❸ Specify the name of the model. ❹ Allow message pattern that matches any dollar amounts. ❺ Deny message patterns that include any US phone number format. * Gateway API * APISIX CRD Create a route with the `ai-prompt-guard` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-prompt-guard-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-prompt-guard-plugin-configspec: plugins: - name: ai-prompt-guard config: allow_patterns: - '\$?\(?\d{1,3}(,\d{3})*(\.\d{1,2})?\)?' deny_patterns: - '(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}' - name: ai-proxy config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: prompt-guard-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-prompt-guard-plugin-config Apply the configuration to your cluster: kubectl apply -f ai-prompt-guard-ic.yaml ❶ Allow message pattern that matches any dollar amounts. ❷ Deny message patterns that include any US phone number format. ❸ Attach OpenAI API key in the `Authorization` header. ❹ Specify the name of the model. Create a route with the `ai-prompt-guard` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-prompt-guard-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: prompt-guard-routespec: ingressClassName: apisix http: - name: prompt-guard-route match: paths: - /anything methods: - POST plugins: - name: ai-prompt-guard enable: true config: allow_patterns: - '\$?\(?\d{1,3}(,\d{3})*(\.\d{1,2})?\)?' deny_patterns: - '(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}' - name: ai-proxy enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 Apply the configuration to your cluster: kubectl apply -f ai-prompt-guard-ic.yaml ❶ Allow message pattern that matches any dollar amounts. ❷ Deny message patterns that include any US phone number format. ❸ Attach OpenAI API key in the `Authorization` header. ❹ Specify the name of the model. Send a request to the route to rate the fairness of a purchase: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "Rate if the purchase is at a decent price in USD." }, { "role": "user", "content": "John paid $12.5 for a hot brewed coffee in El Paso." } ] }' You should see receive an `HTTP/1.1 200 OK` response similar to the following: { ... "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "The purchase is not at a decent price. Typically, a hot brewed coffee costs anywhere from $1 to $3 in most places in the US, so $12.5 is quite expensive.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], ...} Send another request to the route without any price in the message: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "Rate if the purchase is at a decent price in USD." }, { "role": "user", "content": "John paid a bit for a hot brewed coffee in El Paso." } ] }' You should receive an `HTTP/1.1 400 Bad Request` response and see the following message: {"message":"Request doesn't match allow patterns"} Send a third request to the route with a phone number in the message: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "Rate if the purchase is at a decent price in USD." }, { "role": "user", "content": "John (647-200-9393) paid $12.5 for a hot brewed coffee in El Paso." } ] }' You should receive an `HTTP/1.1 400 Bad Request` response and see the following message: {"message":"Request contains prohibited content"} By default, the plugin only inspect the input of `user` role and the last message. For instance, if you send a request including the prohibited content in the `system` prompt: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "Rate if the purchase from 647-200-9393 is at a decent price in USD." }, { "role": "user", "content": "John paid $12.5 for a hot brewed coffee in El Paso." } ] }' You will receive an `HTTP/1.1 200 OK` response. If you send a request including the prohibited content in the second last message: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "Rate if the purchase is at a decent price in USD." }, { "role": "user", "content": "Customer John contact: 647-200-9393" }, { "role": "user", "content": "John paid $12.5 for a hot brewed coffee in El Paso." } ] }' You will also receive an `HTTP/1.1 200 OK` response. See the [next example](https://docs.api7.ai/hub/ai-prompt-guard/#validate-messages-from-all-roles-and-conversation-history) to see how to inspect messages from all roles and all messages. ### Validate Messages From All Roles and Conversation History[​](https://docs.api7.ai/hub/ai-prompt-guard/#validate-messages-from-all-roles-and-conversation-history "Direct link to Validate Messages From All Roles and Conversation History") The following example demonstrates how to use the `ai-prompt-guard` plugin to validate prompts from all roles, such as `system` and `user`, and validate the entire conversation history instead of the last message. Define the allow and deny patterns. You can optionally save them to environment variables for easier escape: export ALLOW_PATTERN_1='\\$?\\(?\\d{1,3}(,\\d{3})*(\\.\\d{1,2})?\\)?'export DENY_PATTERN_1='(\\([0-9]{3}\\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}' * Admin API * ADC * Ingress Controller Create a route that uses `ai-proxy` to proxy to OpenAI and `ai-prompt-guard` to inspect input prompts: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "ai-prompt-guard-route", "uri": "/anything", "methods": ["POST"], "plugins": { "ai-proxy": { "provider": "openai", "auth": { "header": { "Authorization": "Bearer '"$OPENAI_API_KEY"'" } }, "options":{ "model": "gpt-4" } }, "ai-prompt-guard": { "match_all_roles": true, "match_all_conversation_history": true, "allow_patterns": [ "'"$ALLOW_PATTERN_1"'" ], "deny_patterns": [ "'"$DENY_PATTERN_1"'" ] } } }' ❶ Validate messages from all roles. ❷ Validate messages of the entire conversation. Create a route with the `ai-prompt-guard` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: adc.yaml services: - name: prompt-guard-service routes: - name: prompt-guard-route uris: - /anything methods: - POST plugins: ai-proxy: provider: openai auth: header: Authorization: "Bearer ${OPENAI_API_KEY}" options: model: gpt-4 ai-prompt-guard: match_all_roles: true match_all_conversation_history: true allow_patterns: - '\$?\(?\d{1,3}(,\d{3})*(\.\d{1,2})?\)?' deny_patterns: - '(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}' Synchronize the configuration to the gateway: adc sync -f adc.yaml ❶ Validate messages from all roles. ❷ Validate messages of the entire conversation. * Gateway API * APISIX CRD Create a route with the `ai-prompt-guard` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-prompt-guard-history-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: ai-prompt-guard-plugin-configspec: plugins: - name: ai-prompt-guard config: match_all_roles: true match_all_conversation_history: true allow_patterns: - '\$?\(?\d{1,3}(,\d{3})*(\.\d{1,2})?\)?' deny_patterns: - '(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}' - name: ai-proxy config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: prompt-guard-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: ai-prompt-guard-plugin-config Apply the configuration to your cluster: kubectl apply -f ai-prompt-guard-history-ic.yaml ❶ Validate messages from all roles. ❷ Validate messages of the entire conversation. Create a route with the `ai-prompt-guard` and [`ai-proxy`](https://docs.api7.ai/hub/ai-proxy) plugins configured as such: ai-prompt-guard-history-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: prompt-guard-routespec: ingressClassName: apisix http: - name: prompt-guard-route match: paths: - /anything methods: - POST plugins: - name: ai-prompt-guard enable: true config: match_all_roles: true match_all_conversation_history: true allow_patterns: - '\$?\(?\d{1,3}(,\d{3})*(\.\d{1,2})?\)?' deny_patterns: - '(\([0-9]{3}\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}' - name: ai-proxy enable: true config: provider: openai auth: header: Authorization: "Bearer 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" options: model: gpt-4 Apply the configuration to your cluster: kubectl apply -f ai-prompt-guard-history-ic.yaml ❶ Validate messages from all roles. ❷ Validate messages of the entire conversation. Send a request including with prohibited content in the `system` prompt: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "Rate if the purchase from 647-200-9393 is at a decent price in USD." }, { "role": "user", "content": "John paid $12.5 for a hot brewed coffee in El Paso." } ] }' You should receive an `HTTP/1.1 400 Bad Request` response and see the following message: {"message":"Request contains prohibited content"} Send a request with multiple messages from the same role with prohibited content: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "Rate if the purchase is at a decent price in USD." }, { "role": "user", "content": "Customer John contact: 647-200-9393" }, { "role": "user", "content": "John paid $12.5 for a hot brewed coffee in El Paso." } ] }' You should receive an `HTTP/1.1 400 Bad Request` response and see the following message: {"message":"Request contains prohibited content"} Send a request that conforms to the patterns: curl -i "http://127.0.0.1:9080/anything" -X POST \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "Rate if the purchase is at a decent price in USD." }, { "role": "system", "content": "The puchase is made in El Paso." }, { "role": "user", "content": "Customer John contact: xxx-xxx-xxxx" }, { "role": "user", "content": "John paid $12.5 for a hot brewed coffee." } ] }' You should see receive an `HTTP/1.1 200 OK` response similar to the following: { ..., "model": "gpt-4-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "$12.5 is generally considered quite expensive for a cup of brew coffee.", "refusal": null }, "logprobs": null, "finish_reason": "stop" } ], ...} * [Demo](https://docs.api7.ai/hub/ai-prompt-guard/#demo) * [Examples](https://docs.api7.ai/hub/ai-prompt-guard/#examples) * [Implement Allow and Deny Patterns](https://docs.api7.ai/hub/ai-prompt-guard/#implement-allow-and-deny-patterns) * [Validate Messages From All Roles and Conversation History](https://docs.api7.ai/hub/ai-prompt-guard/#validate-messages-from-all-roles-and-conversation-history) --- # Key Auth | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/key-auth/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `key-auth` plugin supports the use of an authentication key as a mechanism for clients to authenticate themselves before accessing upstream resources. To use the plugin, you would configure authentication keys on [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) and enable the plugin on routes or services. The key can be included in the request URL query string or request header. APISIX will then verify the key to determine if a request should be allowed or denied to access upstream resources. When a consumer is successfully authenticated, APISIX adds additional headers, such as `X-Consumer-Username`, `X-Credential-Identifier`, and other consumer custom headers if configured, to the request, before proxying it to the upstream service. The upstream service will be able to differentiate between consumers and implement additional logics as needed. If any of these values is not available, the corresponding header will not be added. About X-Consumer-Username When consumers are configured using the Ingress Controller, the consumer name is generated in the format `namespace_consumername`. As a result, the `X-Consumer-Username` header will also follow this format instead of just `consumername`. Examples[​](https://docs.api7.ai/hub/key-auth/#examples "Direct link to Examples") ----------------------------------------------------------------------------------- The examples below demonstrate how you can work with the `key-auth` plugin for different scenarios. ### Implement Key Authentication on Route[​](https://docs.api7.ai/hub/key-auth/#implement-key-authentication-on-route "Direct link to Implement Key Authentication on Route") The following example demonstrates how to implement key authentication on a route and include the key in the request header. * Admin API * ADC * Ingress Controller Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-key-auth", "plugins": { "key-auth": { "key": "jack-key" } } }' Create a route with `key-auth`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "key-auth-route", "uri": "/anything", "plugins": { "key-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create a consumer with `key-auth` credential and a route with `key-auth` plugin configured as such: adc.yaml consumers: - username: jack credentials: - name: key-auth type: key-auth config: key: jack-keyservices: - name: key-auth-service routes: - name: key-auth-route uris: - /anything plugins: key-auth: {} upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Create a consumer with `key-auth` credential and a route with `key-auth` plugin configured as such: * Gateway API * APISIX CRD key-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: jackspec: gatewayRef: name: apisix credentials: - type: key-auth name: primary-cred config: key: jack-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: key-auth-plugin-configspec: plugins: - name: key-auth config: _meta: disable: false---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: key-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: key-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f key-auth-ic.yaml key-auth-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: jackspec: ingressClassName: apisix authParameter: keyAuth: value: key: jack-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: key-auth-routespec: ingressClassName: apisix http: - name: key-auth-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: key-auth enable: true Apply the configuration to your cluster: kubectl apply -f key-auth-ic.yaml #### Verify with a Valid Key[​](https://docs.api7.ai/hub/key-auth/#verify-with-a-valid-key "Direct link to Verify with a Valid Key") Send a request to the route with the valid key: curl -i "http://127.0.0.1:9080/anything" -H 'apikey: jack-key' You should receive an `HTTP/1.1 200 OK` response. #### Verify with an Invalid Key[​](https://docs.api7.ai/hub/key-auth/#verify-with-an-invalid-key "Direct link to Verify with an Invalid Key") Send a request with an invalid key: curl -i "http://127.0.0.1:9080/anything" -H 'apikey: wrong-key' You should see an `HTTP/1.1 401 Unauthorized` response with the following: {"message":"Invalid API key in request"} #### Verify without a Key[​](https://docs.api7.ai/hub/key-auth/#verify-without-a-key "Direct link to Verify without a Key") Send a request to the route without a key: curl -i "http://127.0.0.1:9080/anything" You should see an `HTTP/1.1 401 Unauthorized` response with the following: {"message":"Missing API key found in request"} ### Hide Authentication Information From Upstream[​](https://docs.api7.ai/hub/key-auth/#hide-authentication-information-from-upstream "Direct link to Hide Authentication Information From Upstream") The following example first demonstrates the default behavior, where the authentication key is forwarded to the upstream services, and then shows how to prevent the key from being sent by configuring `hide_credentials`. Forwarding the authentication key to upstream services might lead to security risks in some circumstances. * Admin API * ADC * Ingress Controller Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-key-auth", "plugins": { "key-auth": { "key": "jack-key" } } }' #### Without Hiding Credentials[​](https://docs.api7.ai/hub/key-auth/#without-hiding-credentials "Direct link to Without Hiding Credentials") Create a route with `key-auth` and configure `hide_credentials` to `false`, which is the default configuration: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \-H "X-API-KEY: ${ADMIN_API_KEY}" \-d '{ "id": "key-auth-route", "uri": "/anything", "plugins": { "key-auth": { "hide_credentials": false } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' Create a consumer with `key-auth` credential and a route with `key-auth` plugin configured as such: adc.yaml consumers: - username: jack credentials: - name: key-auth type: key-auth config: key: jack-keyservices: - name: key-auth-service routes: - name: key-auth-route uris: - /anything plugins: key-auth: hide_credentials: false upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Create a consumer with `key-auth` credential and a route with `key-auth` plugin configured as such: * Gateway API * APISIX CRD key-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: jackspec: gatewayRef: name: apisix credentials: - type: key-auth name: primary-cred config: key: jack-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: key-auth-plugin-configspec: plugins: - name: key-auth config: _meta: disable: false hide_credentials: false---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: key-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: key-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f key-auth-ic.yaml key-auth-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: jackspec: ingressClassName: apisix authParameter: keyAuth: value: key: jack-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: key-auth-routespec: ingressClassName: apisix http: - name: key-auth-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: key-auth enable: true config: hide_credentials: false Apply the configuration to your cluster: kubectl apply -f key-auth-ic.yaml Send a request with the valid key: curl -i "http://127.0.0.1:9080/anything?apikey=jack-key" You should see an `HTTP/1.1 200 OK` response with the following: { "args": { "apikey": "jack-key" }, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.2.1", "X-Consumer-Username": "jack", "X-Credential-Identifier": "cred-jack-key-auth", "X-Amzn-Trace-Id": "Root=1-6502d8a5-2194962a67aa21dd33f94bb2", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "GET", "origin": "127.0.0.1, 103.248.35.179", "url": "http://127.0.0.1/anything?apikey=jack-key"} Note that the credential `jack-key` is visible to the upstream service. #### Hide Credentials[​](https://docs.api7.ai/hub/key-auth/#hide-credentials "Direct link to Hide Credentials") * Admin API * ADC * Ingress Controller Update the plugin's `hide_credentials` to `true`: curl "http://127.0.0.1:9180/apisix/admin/routes/key-auth-route" -X PATCH \-H "X-API-KEY: ${ADMIN_API_KEY}" \-d '{ "plugins": { "key-auth": { "hide_credentials": true } }}' Update the route configuration: adc.yaml services: - name: key-auth-service routes: - name: key-auth-route uris: - /anything plugins: key-auth: hide_credentials: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Update the PluginConfig to set `hide_credentials` to `true`: key-auth-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: key-auth-plugin-configspec: plugins: - name: key-auth config: _meta: disable: false hide_credentials: true Apply the configuration to your cluster: kubectl apply -f key-auth-ic.yaml Update the ApisixRoute to set `hide_credentials` to `true`: key-auth-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: key-auth-routespec: ingressClassName: apisix http: - name: key-auth-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: key-auth enable: true config: hide_credentials: true Apply the configuration to your cluster: kubectl apply -f key-auth-ic.yaml Send a request with the valid key: curl -i "http://127.0.0.1:9080/anything?apikey=jack-key" You should see an `HTTP/1.1 200 OK` response with the following: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.2.1", "X-Consumer-Username": "jack", "X-Credential-Identifier": "cred-jack-key-auth", "X-Amzn-Trace-Id": "Root=1-6502d85c-16f34dbb5629a5960183e803", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "GET", "origin": "127.0.0.1, 103.248.35.179", "url": "http://127.0.0.1/anything"} Note that the credential `jack-key` is no longer visible to the upstream service. ### Demonstrate Priority of Keys in Header and Query[​](https://docs.api7.ai/hub/key-auth/#demonstrate-priority-of-keys-in-header-and-query "Direct link to Demonstrate Priority of Keys in Header and Query") The following example demonstrates how to implement key authentication by consumers on a route and customize the URL parameter that should include the key. The example also shows that when the API key is configured in both the header and the query string, the request header has a higher priority. * Admin API * ADC * Ingress Controller Create a consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack" }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-key-auth", "plugins": { "key-auth": { "key": "jack-key" } } }' Create a route with `key-auth`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \-H "X-API-KEY: ${ADMIN_API_KEY}" \-d '{ "id": "key-auth-route", "uri": "/anything", "plugins": { "key-auth": { "query": "auth" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } }}' Create a consumer with `key-auth` credential and a route with `key-auth` plugin configured as such: adc.yaml consumers: - username: jack credentials: - name: key-auth type: key-auth config: key: jack-keyservices: - name: key-auth-service routes: - name: key-auth-route uris: - /anything plugins: key-auth: query: auth upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Create a consumer with `key-auth` credential and a route with `key-auth` plugin configured as such: * Gateway API * APISIX CRD key-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: jackspec: gatewayRef: name: apisix credentials: - type: key-auth name: primary-cred config: key: jack-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: key-auth-plugin-configspec: plugins: - name: key-auth config: _meta: disable: false query: auth---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: key-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: key-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f key-auth-ic.yaml key-auth-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: jackspec: ingressClassName: apisix authParameter: keyAuth: value: key: jack-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: key-auth-routespec: ingressClassName: apisix http: - name: key-auth-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: key-auth enable: true config: query: auth Apply the configuration to your cluster: kubectl apply -f key-auth-ic.yaml #### Verify with a Valid Key[​](https://docs.api7.ai/hub/key-auth/#verify-with-a-valid-key-1 "Direct link to Verify with a Valid Key") Send a request to the route with the valid key: curl -i "http://127.0.0.1:9080/anything?auth=jack-key" You should receive an `HTTP/1.1 200 OK` response. #### Verify with an Invalid Key[​](https://docs.api7.ai/hub/key-auth/#verify-with-an-invalid-key-1 "Direct link to Verify with an Invalid Key") Send a request with an invalid key: curl -i "http://127.0.0.1:9080/anything?auth=wrong-key" You should see an `HTTP/1.1 401 Unauthorized` response with the following: {"message":"Invalid API key in request"} #### Verify with a Valid Key in Query String[​](https://docs.api7.ai/hub/key-auth/#verify-with-a-valid-key-in-query-string "Direct link to Verify with a Valid Key in Query String") However, if you include the valid key in header with the invalid key still in the URL query string: curl -i "http://127.0.0.1:9080/anything?auth=wrong-key" -H 'apikey: jack-key' You should see an `HTTP/1.1 200 OK` response. This shows that the key included in the header always has a higher priority. ### Add Consumer Custom ID to Header[​](https://docs.api7.ai/hub/key-auth/#add-consumer-custom-id-to-header "Direct link to Add Consumer Custom ID to Header") The following example demonstrates how you can attach a consumer custom ID to authenticated request in the `Consumer-Custom-Id` header, which can be used to implement additional logics as needed. * Admin API * ADC * Ingress Controller Create a consumer `jack` with a custom ID label: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack", "labels": { "custom_id": "495aec6a" } }' Create `key-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-key-auth", "plugins": { "key-auth": { "key": "jack-key" } } }' Create a route with `key-auth`: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "key-auth-route", "uri": "/anything", "plugins": { "key-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create a consumer with `key-auth` credential and a route with `key-auth` plugin enabled: adc.yaml consumers: - username: jack labels: custom_id: "495aec6a" credentials: - name: key-auth type: key-auth config: key: jack-keyservices: - name: key-auth-service routes: - name: key-auth-route uris: - /anything plugins: key-auth: {} upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Consumer custom labels are currently not supported when configuring resources through the Ingress Controller, and the `X-Consumer-Custom-Id` header is not included in requests. At the moment, this example cannot be completed with the Ingress Controller. To verify, send a request to the route with the valid key: curl -i "http://127.0.0.1:9080/anything?apikey=jack-key" You should see an `HTTP/1.1 200 OK` response similar to the following: { "args": { "apikey": "jack-key" }, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66ea8d64-33df89052ae198a706e18c2a", "X-Consumer-Username": "jack", "X-Credential-Identifier": "cred-jack-key-auth", "X-Consumer-Custom-Id": "495aec6a", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "GET", "origin": "192.168.65.1, 205.198.122.37", "url": "http://127.0.0.1/anything?apikey=jack-key"} If you would like to attach more consumer custom headers to authenticated requests, see the [`attach-consumer-label`](https://docs.api7.ai/hub/attach-consumer-label) plugin. ### Rate Limit with Anonymous Consumer[​](https://docs.api7.ai/hub/key-auth/#rate-limit-with-anonymous-consumer "Direct link to Rate Limit with Anonymous Consumer") The following example demonstrates how you can configure different rate limiting policies by regular and anonymous consumers, where the anonymous consumer does not need to authenticate and has less quota. * Admin API * ADC * Ingress Controller Create a regular consumer `jack` and configure the [`limit-count`](https://docs.api7.ai/hub/limit-count) plugin to allow for a quota of 3 within a 30-second window: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "jack", "plugins": { "limit-count": { "count": 3, "time_window": 30, "rejected_code": 429, "policy": "local" } } }' Create the `key-auth` credential for the consumer `jack`: curl "http://127.0.0.1:9180/apisix/admin/consumers/jack/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-jack-key-auth", "plugins": { "key-auth": { "key": "jack-key" } } }' Create an anonymous user `anonymous` and configure the `limit-count` plugin to allow for a quota of 1 within a 30-second window: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "anonymous", "plugins": { "limit-count": { "count": 1, "time_window": 30, "rejected_code": 429, "policy": "local" } } }' Create a route and configure the `key-auth` plugin to accept anonymous consumer `anonymous` from bypassing the authentication: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "key-auth-route", "uri": "/anything", "plugins": { "key-auth": { "anonymous_consumer": "anonymous" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Configure consumers with different rate limits and a route that accepts anonymous users: adc.yaml consumers: - username: jack plugins: limit-count: count: 3 time_window: 30 rejected_code: 429 policy: local credentials: - name: key-auth type: key-auth config: key: jack-key - username: anonymous plugins: limit-count: count: 1 time_window: 30 rejected_code: 429 policy: localservices: - name: anonymous-rate-limit-service routes: - name: key-auth-route uris: - /anything plugins: key-auth: anonymous_consumer: anonymous upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Configure consumers with different rate limits and a route that accepts anonymous users: key-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: jackspec: gatewayRef: name: apisix credentials: - type: key-auth name: primary-key config: key: jack-key plugins: - name: limit-count config: count: 3 time_window: 30 rejected_code: 429 policy: local---apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: anonymousspec: gatewayRef: name: apisix plugins: - name: limit-count config: count: 1 time_window: 30 rejected_code: 429 policy: local---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: key-auth-plugin-configspec: plugins: - name: key-auth config: anonymous_consumer: aic_anonymous # namespace_consumername---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: key-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: key-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f key-auth-ic.yaml note The ApisixConsumer CRD currently does not support configuring plugins on consumers, except for the authentication plugins allowed in `authParameter`. This example cannot be completed with APISIX CRDs. To verify, send five consecutive requests with `jack`'s key: resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H 'apikey: jack-key' -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that out of the 5 requests, 3 requests were successful (status code 200) while the others were rejected (status code 429). 200: 3, 429: 2 Send five anonymous requests: resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that only one request was successful: 200: 1, 429: 4 * [Examples](https://docs.api7.ai/hub/key-auth/#examples) * [Implement Key Authentication on Route](https://docs.api7.ai/hub/key-auth/#implement-key-authentication-on-route) * [Hide Authentication Information From Upstream](https://docs.api7.ai/hub/key-auth/#hide-authentication-information-from-upstream) * [Demonstrate Priority of Keys in Header and Query](https://docs.api7.ai/hub/key-auth/#demonstrate-priority-of-keys-in-header-and-query) * [Add Consumer Custom ID to Header](https://docs.api7.ai/hub/key-auth/#add-consumer-custom-id-to-header) * [Rate Limit with Anonymous Consumer](https://docs.api7.ai/hub/key-auth/#rate-limit-with-anonymous-consumer) --- # Credentials | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/key-concepts/credentials/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page In this document, you will learn the basic concept of credentials in APISIX, and how it works with [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) for a number of authentication plugins. Explore additional resources at the end of the document for more information on related topics. Overview[​](https://docs.api7.ai/apisix/key-concepts/credentials/#overview "Direct link to Overview") ------------------------------------------------------------------------------------------------------ In APISIX, a _credential_ object is an entity used to store authentication configurations associated with consumers. A consumer can be associated with one or more credentials from a designated list of authentication plugins, including [`key-auth`](https://docs.api7.ai/hub/key-auth) , [`basic-auth`](https://docs.api7.ai/hub/basic-auth) , [`jwt-auth`](https://docs.api7.ai/hub/jwt-auth) , and [`hmac-auth`](https://docs.api7.ai/hub/hmac-auth) . The decoupling of credentials facilitates credential reuse and rotation as well as enhanced security. You will not see the credential details when examining consumers. The following diagram illustrates an example of credentials using two routes, each with one type of authentication enabled, and one consumer, whose authentication details are configured in credentials. Only requests with the valid credentials will be authenticated and allowed to access the upstream resource: ![credentials diagram](https://static.api7.ai/uploads/2024/09/12/aBnQWmwa_credentials-label-udpated.svg) Note that when a consumer is successfully authenticated, APISIX adds additional headers, such as consumer username and identifier, to the request before proxying it to the upstream service. The upstream service will be able to differentiate between consumers and implement additional logic as needed. Additional Resources[​](https://docs.api7.ai/apisix/key-concepts/credentials/#additional-resources "Direct link to Additional Resources") ------------------------------------------------------------------------------------------------------------------------------------------ * Key Concepts - [Consumers](https://docs.api7.ai/apisix/key-concepts/consumers) * Getting Started - [Key Authentication](https://docs.api7.ai/apisix/getting-started/key-authentication) * Admin API - [Credentials](https://docs.api7.ai/apisix/reference/admin-api#tag/Credentials) * [Overview](https://docs.api7.ai/apisix/key-concepts/credentials/#overview) * [Additional Resources](https://docs.api7.ai/apisix/key-concepts/credentials/#additional-resources) --- # Enterprise Plugins | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page API7 Enterprise offers a robust suite of advanced plugins designed to enhance API gateway capabilities, catering to a wide range of use cases. These enterprise-exclusive plugins provide powerful tools for traffic management, data transformation, authentication, and security, ensuring a secure, efficient, and scalable API ecosystem. By leveraging these plugins, organizations can achieve granular control over API traffic, customize gateway responses, and secure their APIs against potential threats. With continuous innovation, API7 Enterprise plugins are tailored to meet complex business needs. From implementing advanced rate-limiting algorithms to customizing error pages and transforming gateway responses, the plugin library empowers enterprises to optimize API performance, enhance security, and improve the overall user experience. You can start exploring Enterprise plugins using the API7 Enterprise free trial. Enterprise Plugins[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#enterprise-plugins "Direct link to Enterprise Plugins") ------------------------------------------------------------------------------------------------------------------------------------------------- The following is a list of plugins exclusive for API7 Enterprise by category, which is still rapidly growing over time. Visit [Plugin Hub](https://docs.api7.ai/hub) to see all available plugins. ![enterprise plugins](https://static.api7.ai/uploads/2024/12/13/PuKMkitO_enterprise-plugins.png) ### Traffic Management[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#traffic-management "Direct link to Traffic Management") * `graphql-limit-count`: rate limit GraphQL requests based on the depth of the GraphQL queries or mutations. * `graphql-proxy-cache`: provides the capability to cache responses for GraphQL queries. * `oas-validator`: validates requests or responses against a defined Open API schema. * `proxy-buffering`: dynamically disables the NGINX `proxy_buffering` directive to work with SSE (Server-Sent Events) and other upstream services sending stream data. * `traffic-label`: label traffic based on user-defined rules and takes actions based on labels and the associated weights for actions. * `limit-count-advanced`: offers sliding window algorithm in addition to the fixed window algorithm to rate limit requests. ### Transformation[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#transformation "Direct link to Transformation") * `exit-transformer`: supports the customization of gateway responses based on the status codes, headers, and bodies returned from APISIX plugins. * `soap`: provides a convenient approach to transform between RESTful HTTP requests and SOAP requests, as well as their corresponding responses. ### Authentication[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#authentication "Direct link to Authentication") * `saml-auth`: enables API7 to act as the service provider (SP) and authenticate users via SAML 2.0 by interacting with identity providers (IdP). ### Security[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#security "Direct link to Security") * `acl`: allows or denies request access to upstream resources by verifying whether the user initiating the request is in the access control lists. * `data-mask`: provides the capability to remove or replace sensitive information in request headers, request bodies, and URL queries. ### General[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#general "Direct link to General") * `error-page`: allows customizing the error page served when APISIX encounters an exception. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#use-cases "Direct link to Use Cases") ---------------------------------------------------------------------------------------------------------------------- The following are a few use cases using Enterprise plugins. For more information, please see [plugin docs](https://docs.api7.ai/hub) . ### Redact Sensitive Information[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#redact-sensitive-information "Direct link to Redact Sensitive Information") Use `data-mask` plugin to remove or replace sensitive information in the request body before the request is forwarded to upstream services. For example, when forwarding user input to an LLM upstream service, sensitive data such as social insurance numbers, birthdays, or other confidential details might be included in the input prompt. With `data-mask` plugin, the Gateway can automatically detect and mask such information before it reaches upstream services. This ensures compliance with privacy regulations, reduces the risk of data breaches, and maintains user trust. ### Disable Proxy Buffering for SSE[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#disable-proxy-buffering-for-sse "Direct link to Disable Proxy Buffering for SSE") When integrating with Server-Sent Events upstream services, real-time data transmission is critical for applications like live notifications, stock updates, or chat systems. By default, NGINX’s `proxy_buffering` directive can introduce latency, as it buffers data before forwarding it to the client. The `proxy-buffering` plugin allows dynamic disabling of this directive at runtime, ensuring that SSE upstream services stream data to clients without unnecessary delays. ### Customize Error Pages[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#customize-error-pages "Direct link to Customize Error Pages") The `error-page` plugin allows customizing the error page when APISIX throws `404`, `500`, `502`, or `503` errors. For instance, when APISIX encounters an exception, such as an unavailable upstream service or a gateway timeout, the Gateway can return a customized error page, which aligns with the application’s branding or provides actionable steps, to users. You can configure different error pages for the supported error code. The customized error pages help ensure a consistent and professional user experience. ### Apply Rate Limiting with Sliding Window[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#apply-rate-limiting-with-sliding-window "Direct link to Apply Rate Limiting with Sliding Window") The `limit-count-advanced` plugin builds on top of the `limit-count` plugin and supports sliding window algorithm when rate limiting. The sliding window algorithm tracks requests in overlapping intervals, smoothing out the rate limit by counting recent requests within the last configured time period, regardless of when the interval began. This method reduces traffic spikes and is more effective at evenly distributing requests over time. For example, in an e-commerce API handling high-traffic flash sales, using the sliding window algorithm ensures requests are distributed smoothly, preventing sudden traffic spikes from overwhelming the system. ### Implement Access Control List[​](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#implement-access-control-list "Direct link to Implement Access Control List") The `acl` plugin regulates requests to upstream resources by verifying whether the user initiating the request is in the access control lists. The user identities can be obtained from consumer labels or user information from third-party identity providers, such as Keycloak. For instance, in a multi-tenant SaaS platform, the API gateway can use ACLs to verify if a user or service attempting to access specific endpoints is authorized. By allowing or denying requests based on predefined access control lists, the plugin ensures that only permitted tenants or teams can reach their designated APIs. This centralized enforcement at the gateway level enhances security, simplifies management, and reduces the risk of unauthorized access across the entire API ecosystem. * [Enterprise Plugins](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#enterprise-plugins) * [Traffic Management](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#traffic-management) * [Transformation](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#transformation) * [Authentication](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#authentication) * [Security](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#security) * [General](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#general) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#use-cases) * [Redact Sensitive Information](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#redact-sensitive-information) * [Disable Proxy Buffering for SSE](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#disable-proxy-buffering-for-sse) * [Customize Error Pages](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#customize-error-pages) * [Apply Rate Limiting with Sliding Window](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#apply-rate-limiting-with-sliding-window) * [Implement Access Control List](https://docs.api7.ai/apisix/enterprise-feature/enterprise-plugins/#implement-access-control-list) --- # Custom Plugins and Sandbox | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Custom plugins are essential in scenarios where existing plugins cannot fully meet specific requirements. On the one hand, they allow the API gateway to connect to proprietary systems, legacy infrastructure, or non-standard protocols that cannot be addressed with off-the-shelf solutions. On the other hand, using custom plugins, users can enable the customization and extension of the API gateway to address unique business needs accordingly. However, custom plugins have inherent security risks, especially when executing user-written code. Sandbox is used to help mitigate the risks associated with granting full access to system resources. By providing isolated environments in which the plugin runs, sandboxing protects the underlying system and services from potential vulnerabilities or malicious behavior. To use a custom plugin in API7 Enterprise, users simply upload the plugin file, select the appropriate catalog for the organization, provide details such as the plugin's usage description, and author information, and optionally upload a logo. Once configured, the plugin can be added and utilized in the system, and its name must remain unique to avoid conflicts. After the custom plugin is created, it can be easily referenced by all gateway groups and services, enhancing the overall flexibility and efficiency of API management. ![Add Custom Plugin in API7 Enterprise](https://static.api7.ai/uploads/2024/12/13/fXAqAfcq_custom-plugins.jpeg) Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#key-features "Direct link to Key Features") --------------------------------------------------------------------------------------------------------------------------------------- * Support Lua for custom plugin development exclusively to ensure consistency with the core of API7 Enterprise while maintaining lightweight and high-performance operation. * Custom plugins can be used as other built-in plugins, which can be applied directly to routes or services, enabling flexible and targeted API traffic management. * Custom plugins can be activated across all gateways or specifically assigned to particular gateway groups, offering granular control over where and when the plugins run. * Sandboxing offers a secure and controlled environment that limits the execution scope of custom plugins, balancing between safety and the flexibility needed for custom plugin operations. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#use-cases "Direct link to Use Cases") ------------------------------------------------------------------------------------------------------------------------------ ### Integrate with Legacy or Non-Standard Protocols[​](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#integrate-with-legacy-or-non-standard-protocols "Direct link to Integrate with Legacy or Non-Standard Protocols") When enterprises need to integrate their API gateway with internal systems or legacy applications, they may need to tailor plugins suitable for their systems. In such cases, custom plugins are essential for organizations looking to maintain compatibility with their existing infrastructure while adopting modern API management practices. Many legacy systems rely on proprietary protocols or data formats that are not natively supported by modern API management tools. Furthermore, standard plugins may not fully meet the requirements when dealing with non-standard protocols or data formats. To bridge this gap, custom plugins can be developed to handle unique business needs. For instance, custom plugins can perform data transformations between proprietary formats and industry-standard formats. This ensures that the API gateway can effectively manage requests and responses between legacy or non-standard protocol services and new ones. ### Tailor Extensive Features[​](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#tailor-extensive-features "Direct link to Tailor Extensive Features") Custom plugins offer a way to tailor the API gateway to meet the specific needs of an organization, especially when dealing with complex use cases. Custom plugins allow businesses to enhance their API gateways with highly specific functionalities that go beyond the capabilities of built-in features. By extending the API gateway with custom plugins, businesses can not only preserve their existing infrastructure but also modernize and future-proof their systems. This approach allows businesses to stay agile and adapt to evolving technological demands, ensuring long-term scalability and flexibility. ### Balance Flexibility and Security with Sandbox[​](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#balance-flexibility-and-security-with-sandbox "Direct link to Balance Flexibility and Security with Sandbox") In API7 Enterprise, custom plugins can be used in many ways. For example, they can be used as built-in plugins, enabling full access to all functions of the API gateway. While this offers maximum flexibility, it comes with increased security risks. Custom plugins can also be used in a sandbox, which helps mitigate risks by restricting the plugin's access to the system, allowing only safe and controlled interactions with the API gateway. In the future, custom plugin security can be further enhanced by writing configuration files, rather than exposing them on the dashboard. Sandboxes offer an optimal solution for organizations looking to balance flexibility with security, ensuring that the API gateway remains safe and efficient. * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#key-features) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#use-cases) * [Integrate with Legacy or Non-Standard Protocols](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#integrate-with-legacy-or-non-standard-protocols) * [Tailor Extensive Features](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#tailor-extensive-features) * [Balance Flexibility and Security with Sandbox](https://docs.api7.ai/apisix/enterprise-feature/custom-plugins-and-sandbox/#balance-flexibility-and-security-with-sandbox) --- # Alerts and Contact Points | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page When using an API gateway, developers may encounter various exceptions, such as impending SSL certificate expiration or HTTP 5xx response codes exceeding predefined thresholds. To handle these scenarios, API7 Enterprise provides a set of exception event monitoring and alert triggering mechanisms. This mechanism allows real-time tracking of exceptions and, upon detecting issues, sends alert notifications immediately via email and Webhook to preset contacts, ensuring timely attention and resolution. How Do Alerts Work?[​](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#how-do-alerts-work "Direct link to How Do Alerts Work?") ---------------------------------------------------------------------------------------------------------------------------------------------------------- API7 Enterprise is pre-configured with more than 20 exception events, each of which can be customized with specific trigger rules and thresholds. Additionally, users can select gateway groups to which these rules will be applied. Alert policies offer two modes for triggering conditions: **ALL** or **ANY**. The ALL mode requires all conditions to be met for activation, while the ANY mode takes effect when any conditions are met. Note that both modes cannot be used simultaneously in a single alert policy. Users can choose the severity of alerts (High, Medium, Low) and set the check interval for the alert policy. The severity level will be included in the alert notification, but it does not affect the alert strategy or rule settings in API7 Enterprise. The minimum check interval is `1` minute, meaning that alert notifications for an event will be sent to designated contact points every minute after it takes effect. Notifications can be sent via **Email** or **Webhook**. Users can flexibly customize notification content using [variables and templates](https://docs.api7.ai/enterprise/reference/alert-template) and integrate with various systems. It is recommended to add the alert policy name `{{ .AlertPolicyName }}` and description `{{ .Description }}` in the notification to help identify which policy triggered the alert. Multiple contact points can be configured for notifications, such as sending alerts to two emails and three Webhook contact points. API7 Enterprise's control plane runs a background program that checks all alert policies every minute to see if they meet the trigger conditions and thresholds. If they do, notifications will be sent to the corresponding contact point list. API7 Enterprise also provides alert logs for saving, viewing, and searching, with logs retained for up to 30 days. These logs record the alert policy configurations and capture the request and response details when delivering notifications for troubleshooting. Why Use Contact Points?[​](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#why-use-contact-points "Direct link to Why Use Contact Points?") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- A **Contact Point** is an organizational-level concept that abstracts the method of notification (Email or Webhook) and which channels (people, Slack, or operations platforms) receive the notifications. For example, if you configure `tom@acme.com` as a recipient for multiple alert rules in the Alerts module, without contact points, you need to update the email address in every alert policy whenever a change is required. This process is cumbersome and prone to errors. Introducing contact points decouples the alert strategy from the specific notification configuration. For example, you can add an alert policy like `Alert_to_dev_team`, with the contact point `Notify_dev_team_slack` (Webhook type). Even if the URL or authentication token for the contact points changes, the alert policy does not need to be modified. Users need to ensure smooth network connectivity between the control plane server and contact points. Specifically, if the Email-type contact point is used, users need to pre-configure the SMTP server under the "organization" menu to ensure emails can be sent. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#use-cases "Direct link to Use Cases") ----------------------------------------------------------------------------------------------------------------------------- ### Data Plane Node Health[​](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#data-plane-node-health "Direct link to Data Plane Node Health") When the number of healthy gateway instances drops below a specified threshold, for example, 3, indicating a possible failure in the API gateway nodes providing external services. This is a high-priority event that requires sending alerts via Slack using Webhook and a set of designated email addresses. ### 5xx Response Code Ratio[​](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#5xx-response-code-ratio "Direct link to 5xx Response Code Ratio") If the proportion of API requests with 5xx response codes exceeds a specified ratio, e.g., 0.2%, it indicates that the API gateway or upstream services may have encountered a failure. This requires timely attention, and alerts should be sent to operations engineers via SMS configured using Webhook. ### SSL Certificate Expiry[​](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#ssl-certificate-expiry "Direct link to SSL Certificate Expiry") Suppose the SSL certificate of a website is about to expire within a certain period, such as 30 days. This indicates the need to rotate the SSL certificate, which is not an urgent task. A Jira task can be triggered to be created via Webhook for operations engineers. * [How Do Alerts Work?](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#how-do-alerts-work) * [Why Use Contact Points?](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#why-use-contact-points) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#use-cases) * [Data Plane Node Health](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#data-plane-node-health) * [5xx Response Code Ratio](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#5xx-response-code-ratio) * [SSL Certificate Expiry](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points/#ssl-certificate-expiry) --- # API Portal | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page An API portal is a comprehensive platform that serves as a central hub for managing and providing access to APIs. It serves as a bridge connecting API providers and developers, offering necessary API documentation, specifications, code examples, and other essential information. Another key function of an API portal is API monetization. Enterprises can implement pricing models through this platform, including subscription fees, pay-per-use charges, and tiered access levels. This can allow businesses to tailor their offerings to meet the diverse needs of their customers. Besides the basic features of API portals, API7 Portal can be a preferred choice for businesses aiming for agility and precision in API management. It provides another premier feature: online debugging, which not only simplifies the workflow but also ensures faster iteration and deployment of APIs. ![API7 Portal Diagram](https://static.api7.ai/uploads/2024/12/13/g1hLW6mK_api-portal.png) Key Features[​](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#key-features "Direct link to Key Features") ----------------------------------------------------------------------------------------------------------------------- * **Comprehensive Developer Hub**: Provides all necessary resources-API documentation, guidelines, specifications, and code examples, and supports online debugging. * **API Monetization Tools**: Facilitates monetization with subscription models, usage-based billing, and rate-limiting options. * **Full API Lifecycle Management**: Supports the entire API lifecycle from design and development to deployment, versioning, monitoring, and deprecation. * **Data-Driven Insights**: Advanced analytics help track API usage, performance, and customer behavior for informed decision-making and optimization. Use Cases[​](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#use-cases "Direct link to Use Cases") -------------------------------------------------------------------------------------------------------------- ### API Discovery[​](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#api-discovery "Direct link to API Discovery") By serving as a centralized hub, API7 Portal enables API providers to effectively showcase their APIs and the wide array of services they offer. Through an intuitive interface, developers can easily navigate through a catalog of available APIs, each accompanied by detailed descriptions of their functionalities. This clarity allows users to understand the specific capabilities of each API and how they can be leveraged to meet their needs. ### API Documentation[​](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#api-documentation "Direct link to API Documentation") API7 Portal provides extensive and well-structured documentation for each API endpoint, including detailed descriptions, parameters, request/response formats, and examples. Developers can reference this documentation to understand how to integrate product listings and payment processing into their applications, minimizing confusion and reducing the time to launch. ### API Monetization[​](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#api-monetization "Direct link to API Monetization") Consider a financial services company providing data analytics and transaction processing APIs for businesses. This company must monetize its APIs to sustain operations and drive growth. API7 Portal can exert its functions in the following aspects: * Showcasing the company's API offerings, categorizing services * Enabling various pricing models, such as tiered subscriptions * Maintaining rate-limiting and services based on API call quota * Integrating billing systems to allow seamless payment processing ### Online Debugging[​](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#online-debugging "Direct link to Online Debugging") API7 Portal seamlessly integrates with API7 Enterprise to allow users to publish and manage services with ease. For instance, if a service named GitHub v3 REST API is configured on API7 Enterprise and then published via API7 Portal, developers can directly test its functionality on the API7 Developer Portal. With the intuitive interface, developers can swiftly interact with the dashboard and troubleshoot the services. Such an integrated approach eliminates the need for external debugging tools and reduces the technical complexity for users. Furthermore, this feature also makes API lifecycle management more efficient, especially when debugging or fine-tuning service configurations. ![Online Debugging of API7 Portal](https://static.api7.ai/uploads/2024/12/13/oWDxTtto_online-debugging.jpeg) * [Key Features](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#key-features) * [Use Cases](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#use-cases) * [API Discovery](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#api-discovery) * [API Documentation](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#api-documentation) * [API Monetization](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#api-monetization) * [Online Debugging](https://docs.api7.ai/apisix/enterprise-feature/api-portal/#online-debugging) --- # Security Hardening | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Security Hardening is the process of implementing configurations, controls, and best practices to minimize vulnerabilities and protect systems from unauthorized access or attacks. In the context of an API gateway, security hardening is crucial, as the gateway often serves as a single point of entry to numerous backend services, data sources, and sensitive business logic. By hardening security on the gateway, organizations reduce potential attack surfaces and ensure that only authorized, properly authenticated requests reach critical resources. In addition, understanding where and how sensitive information is stored is important when planning to implement robust security measures and safeguard against unauthorized access, data breaches, or malicious attacks in your organization. Security hardening for API gateways is essential not only to protect sensitive data but also to maintain performance, regulatory compliance, and trust in the system. The following is an architecture diagram illustrating various components of API7 Enterprise and how they interact: ![architecture diagram of various API7 Enterprise components](https://static.api7.ai/uploads/2024/11/28/IVzIOALY_security-hardening.png) Security Hardening Features[​](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#security-hardening-features "Direct link to Security Hardening Features") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- * The communication between the dashboard and the DP manager uses HTTPS by default. * Sensitive data are masked in audit logs before the logs are saved to the database. Any additional alteration to audit logs is forbidden. * User credentials, including username and password, as well as access token, are salted and PBKDF2 encrypted before being saved to the database. * Sensitive plugin configurations, such as Redis password, are specified in `encrypted_fields` in the plugin schema. Information in these fields is encrypted with AES256 before being saved to the database. * Keyrings, which can be used to encrypt sensitive information, differ by gateway group. They are also encrypted before being saved to the database. Security Hardening Measures[​](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#security-hardening-measures "Direct link to Security Hardening Measures") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The following are a few additional security measures you can optionally choose to harden API7 Enterprise security. ### Secure Data Plane Communication[​](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#secure-data-plane-communication "Direct link to Secure Data Plane Communication") To ensure the confidentiality and integrity of data exchanged between clients and API7 Enterprise, as well as between API7 Enterprise and upstream services, TLS or mTLS (mutual TLS) can be configured for communication, using the SSL configuration in API7 Enterprise. By enabling TLS, data transmitted between the client and API7 Enterprise, or between API7 Enterprise and upstream services, is encrypted, protecting against interception and tampering. This setup ensures that unauthorized parties cannot read or alter the data in transit, providing a secure channel over potentially insecure networks. With mTLS, both parties—API7 Enterprise and its client or upstream service—authenticate each other. This bidirectional authentication is particularly valuable for high-security environments, where verifying the identity of both communicating parties prevents unauthorized access. mTLS is commonly used to secure communication with sensitive back-end systems or between internal services where data protection is critical. ### Encrypt Data with Keyring[​](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#encrypt-data-with-keyring "Direct link to Encrypt Data with Keyring") `data_encryption` is a configurable option in the `config.yaml`, which is set to false by default. When set to true, API7 Enterprise will encrypt sensitive plugin fields specified in `encrypt_fields` and TLS certificate private key before saving them to the database. A `keyring` is a collection of encryption keys used for securing the specified sensitive data. Regularly rotating keyrings is a recommended security practice to minimize the risk of key compromise, maintain data integrity, and align with best practices for cryptographic key management. config.yaml data_encryption: enable: false keyring: - qeddd145sfvddff3 When performing key rotation, beware that if your API7 Enterprise is already running and has data encrypted, do not remove the old keys. Add the new keys at the top of the array, so that the encrypted data can be correctly decrypted. Removing the old keys directly can render the encrypted data irreversible. ### Manage Secrets in Secrets Manager[​](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#manage-secrets-in-secrets-manager "Direct link to Manage Secrets in Secrets Manager") To enhance security and simplify the management of sensitive data, API7 Enterprise allows you to store keys in secrets managers such as HashiCorp Vault and AWS Secrets Manager. Secrets managers provide a centralized, encrypted repository for sensitive information, such as API tokens, credentials, and encryption keys. For example, in API7 Enterprise, sensitive plugin fields—such as Redis passwords or private keys for TLS certificates—can be securely stored in a secrets manager. At runtime, API7 retrieves these secrets via integration with the secrets manager, ensuring that sensitive data remains protected throughout its lifecycle. One key advantage of using a secrets manager is enhanced security. This reduces the data risk associated with embedding sensitive information directly into configuration files or exposing it through environment variables. Another advantage is the support for automatic key rotation. Regularly rotating keys is a security best practice, as it minimizes the risk of compromised credentials being exploited over time. Many secrets managers automate this process, updating keys and ensuring that dependent services can access the latest versions without manual intervention. This automation reduces operational overhead while helping organizations comply with regulatory requirements and industry standards. ### Incorporate Continuous Monitoring[​](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#incorporate-continuous-monitoring "Direct link to Incorporate Continuous Monitoring") Incorporating continuous monitoring into your stack helps harden your API infrastructure security by collecting and analyzing real-time metrics. This approach provides early detection of threats, such as unauthorized access or unusual traffic patterns, and helps optimize API performance by identifying bottlenecks. API7 Enterprise offers a built-in event-based alerting system with a series of pre-configured conditions that can trigger alerts, such as Control Plane certificate nearing expiration, gateway instances going offline, or CPU quota exceeded. The alert events can be configured on the Dashboard, each of which can be further customized with severity, check intervals, thresholds, and notification content. See [Alerts and Contact Points](https://docs.api7.ai/apisix/enterprise-feature/alerts-and-contact-points) for more information. API7 Enterprise can also integrate with external tools like Prometheus and Datadog. The Gateway supports exposing a comprehensive set of metrics to the monitoring tools with minimal delay. These integrations allow administrators to visualize metrics, configure alerts, and quickly respond to critical events. Additionally, monitoring helps optimize API performance by pinpointing resource inefficiencies, ensuring smooth operation even under high traffic loads. For organizations subject to regulatory requirements, continuous monitoring also supports compliance by offering detailed metrics and logs necessary for audits and reporting. * [Security Hardening Features](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#security-hardening-features) * [Security Hardening Measures](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#security-hardening-measures) * [Secure Data Plane Communication](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#secure-data-plane-communication) * [Encrypt Data with Keyring](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#encrypt-data-with-keyring) * [Manage Secrets in Secrets Manager](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#manage-secrets-in-secrets-manager) * [Incorporate Continuous Monitoring](https://docs.api7.ai/apisix/enterprise-feature/security-hardening/#incorporate-continuous-monitoring) --- # HMAC Auth | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/hmac-auth/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `hmac-auth` plugin supports HMAC (Hash-based Message Authentication Code) authentication as a mechanism to ensure the integrity of requests, preventing them from being modified during transmissions. To use the plugin, you would configure HMAC secret keys on [consumers](https://docs.api7.ai/apisix/key-concepts/consumers) and enable the plugin on routes or services. When a consumer is successfully authenticated, APISIX adds additional headers, such as `X-Consumer-Username`, `X-Credential-Identifier`, and other consumer custom headers if configured, to the request, before proxying it to the upstream service. The upstream service will be able to differentiate between consumers and implement additional logic as needed. If any of these values is not available, the corresponding header will not be added. About X-Consumer-Username When consumers are configured using the Ingress Controller, the consumer name is generated in the format `namespace_consumername`. As a result, the `X-Consumer-Username` header will also follow this format instead of just `consumername`. Implementation[​](https://docs.api7.ai/hub/hmac-auth/#implementation "Direct link to Implementation") ------------------------------------------------------------------------------------------------------ Once enabled, the plugin verifies the HMAC signature in the request's `Authorization` header and checks that incoming requests are from trusted sources. Specifically, when APISIX receives an HMAC-signed request, the key ID is extracted from the `Authorization` header. APISIX then retrieves the corresponding consumer configuration, including the secret key. If the key ID is valid and exists, APISIX generates an HMAC signature using the request's `Date` header and the secret key. If this generated signature matches the signature provided in the `Authorization` header, the request is authenticated and forwarded to upstream services. The plugin implementation is based on [draft-cavage-http-signatures](https://www.ietf.org/archive/id/draft-cavage-http-signatures-12.txt) . Examples[​](https://docs.api7.ai/hub/hmac-auth/#examples "Direct link to Examples") ------------------------------------------------------------------------------------ The examples below demonstrate how you can work with the `hmac-auth` plugin for different scenarios. ### Implement HMAC Authentication on a Route[​](https://docs.api7.ai/hub/hmac-auth/#implement-hmac-authentication-on-a-route "Direct link to Implement HMAC Authentication on a Route") The following example demonstrates how to implement HMAC authentication on a route. You will also attach a consumer custom ID to authenticated requests in the `X-Consumer-Custom-Id` header, which can be used to implement additional logics as needed. * Admin API * ADC * Ingress Controller Create a consumer `john` with a custom ID label: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john", "labels": { "custom_id": "495aec6a" } }' Create `hmac-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-hmac-auth", "plugins": { "hmac-auth": { "key_id": "john-key", "secret_key": "john-secret-key" } } }' Create a route with the `hmac-auth` plugin using its default configurations: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "hmac-auth-route", "uri": "/get", "methods": ["GET"], "plugins": { "hmac-auth": {} }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create a consumer with `hmac-auth` credential and a route with `hmac-auth` plugin configured as such: adc.yaml consumers: - username: john labels: custom_id: "495aec6a" credentials: - name: hmac-auth type: hmac-auth config: key_id: john-key secret_key: john-secret-keyservices: - name: hmac-auth-service routes: - name: hmac-auth-route uris: - /get methods: - GET plugins: hmac-auth: {} upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Create a consumer with `hmac-auth` credential and a route with `hmac-auth` plugin configured as such: info Consumer custom labels are currently not supported when configuring resources through the Ingress Controller. As a result, the `X-Consumer-Custom-Id` header will not be included in requests. * Gateway API * APISIX CRD hmac-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: johnspec: gatewayRef: name: apisix credentials: - type: hmac-auth name: primary-cred config: key_id: john-key secret_key: john-secret-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: hmac-auth-plugin-configspec: plugins: - name: hmac-auth config: _meta: disable: false---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: hmac-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /get method: GET filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: hmac-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f hmac-auth-ic.yaml hmac-auth-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: johnspec: ingressClassName: apisix authParameter: hmacAuth: value: key_id: john-key secret_key: john-secret-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: hmac-auth-routespec: ingressClassName: apisix http: - name: hmac-auth-route match: paths: - /get methods: - GET upstreams: - name: httpbin-external-domain plugins: - name: hmac-auth enable: true Apply the configuration to your cluster: kubectl apply -f hmac-auth-ic.yaml Generate a signature. You can use the below Python snippet or other stack of your choice: hmac-sig-header-gen.py import hmacimport hashlibimport base64from datetime import datetime, timezonekey_id = "john-key" # key idsecret_key = b"john-secret-key" # secret keyrequest_method = "GET" # HTTP methodrequest_path = "/get" # route URIalgorithm= "hmac-sha256" # can use other algorithms in allowed_algorithms# get current datetime in GMT# note: the signature will become invalid after the clock skew (default 300s)# you can regenerate the signature after it becomes invalid, or increase the clock# skew to prolong the validity within the advised security boundarygmt_time = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT')# construct the signing string (ordered)# the date and any subsequent custom headers should be lowercased and separated by a# single space character, i.e. `:`# https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#section-2.1.6signing_string = ( f"{key_id}\n" f"{request_method} {request_path}\n" f"date: {gmt_time}\n")# create signaturesignature = hmac.new(secret_key, signing_string.encode('utf-8'), hashlib.sha256).digest()signature_base64 = base64.b64encode(signature).decode('utf-8')# construct the request headersheaders = { "Date": gmt_time, "Authorization": ( f'Signature keyId="{key_id}",algorithm="{algorithm}",' f'headers="@request-target date",' f'signature="{signature_base64}"' )}# print headersprint(headers) Run the script: python3 hmac-sig-header-gen.py You should see the request headers printed: {'Date': 'Fri, 06 Sep 2024 06:41:29 GMT', 'Authorization': 'Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date",signature="wWfKQvPDr0wHQ4IHdluB4IzeNZcj0bGJs2wvoCOT5rM="'} Using the headers generated, send a request to the route: curl -X GET "http://127.0.0.1:9080/get" \ -H "Date: Fri, 06 Sep 2024 06:41:29 GMT" \ -H 'Authorization: Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date",signature="wWfKQvPDr0wHQ4IHdluB4IzeNZcj0bGJs2wvoCOT5rM="' You should see an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "headers": { "Accept": "*/*", "Authorization": "Signature keyId=\"john-key\",algorithm=\"hmac-sha256\",headers=\"@request-target date\",signature=\"wWfKQvPDr0wHQ4IHdluB4IzeNZcj0bGJs2wvoCOT5rM=\"", "Date": "Fri, 06 Sep 2024 06:41:29 GMT", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66d96513-2e52d4f35c9b6a2772d667ea", "X-Consumer-Username": "john", "X-Credential-Identifier": "cred-john-hmac-auth", "X-Consumer-Custom-Id": "495aec6a", "X-Forwarded-Host": "127.0.0.1" }, "origin": "192.168.65.1, 34.0.34.160", "url": "http://127.0.0.1/get"} If you would like to attach more consumer custom headers to authenticated requests, see the [`attach-consumer-label`](https://docs.api7.ai/hub/attach-consumer-label) plugin. ### Hide Authorization Information From Upstream[​](https://docs.api7.ai/hub/hmac-auth/#hide-authorization-information-from-upstream "Direct link to Hide Authorization Information From Upstream") As seen in the previous example, the `Authorization` header passed to the upstream includes the signature and all other details. This could potentially introduce security risks. This example continues from the [previous example](https://docs.api7.ai/hub/hmac-auth/#implement-hmac-authentication-on-a-route) to demonstrate how to prevent this information from being sent to the upstream service. * Admin API * ADC * Ingress Controller Update the plugin configuration to set `hide_credentials` to `true`: curl "http://127.0.0.1:9180/apisix/admin/routes/hmac-auth-route" -X PATCH \-H "X-API-KEY: ${ADMIN_API_KEY}" \-d '{ "plugins": { "hmac-auth": { "hide_credentials": true } }}' Update the plugin configuration as such: adc.yaml consumers: - username: john labels: custom_id: "495aec6a" credentials: - name: hmac-auth type: hmac-auth config: key_id: john-key secret_key: john-secret-keyservices: - name: hmac-auth-service routes: - name: hmac-auth-route uris: - /get methods: - GET plugins: hmac-auth: hide_credentials: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Update the PluginConfig to set `hide_credentials` to `true`: hmac-auth-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: hmac-auth-plugin-configspec: plugins: - name: hmac-auth config: _meta: disable: false hide_credentials: true Apply the configuration to your cluster: kubectl apply -f hmac-auth-ic.yaml Update the ApisixRoute to set `hide_credentials` to `true`: hmac-auth-ic.yaml # other configs# ---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: hmac-auth-routespec: ingressClassName: apisix http: - name: hmac-auth-route match: paths: - /get methods: - GET upstreams: - name: httpbin-external-domain plugins: - name: hmac-auth enable: true config: hide_credentials: true Apply the configuration to your cluster: kubectl apply -f hmac-auth-ic.yaml Send a request to the route: curl -X GET "http://127.0.0.1:9080/get" \ -H "Date: Fri, 06 Sep 2024 06:41:29 GMT" \ -H 'Authorization: Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date",signature="wWfKQvPDr0wHQ4IHdluB4IzeNZcj0bGJs2wvoCOT5rM="' You should see an `HTTP/1.1 200 OK` response and notice the `Authorization` header is entirely removed: { "args": {}, "headers": { "Accept": "*/*", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66d96513-2e52d4f35c9b6a2772d667ea", "X-Consumer-Username": "john", "X-Credential-Identifier": "cred-john-hmac-auth", "X-Forwarded-Host": "127.0.0.1" }, "origin": "192.168.65.1, 34.0.34.160", "url": "http://127.0.0.1/get"} ### Enable Body Validation[​](https://docs.api7.ai/hub/hmac-auth/#enable-body-validation "Direct link to Enable Body Validation") The following example demonstrates how to enable body validation to ensure the integrity of the request body. * Admin API * ADC * Ingress Controller Create a consumer `john`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john" }' Create `hmac-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-hmac-auth", "plugins": { "hmac-auth": { "key_id": "john-key", "secret_key": "john-secret-key" } } }' Create a route with the `hmac-auth` plugin as such: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "hmac-auth-route", "uri": "/post", "methods": ["POST"], "plugins": { "hmac-auth": { "validate_request_body": true } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create a consumer with `hmac-auth` credential and a route with `hmac-auth` plugin configured as such: adc.yaml consumers: - username: john credentials: - name: hmac-auth type: hmac-auth config: key_id: john-key secret_key: john-secret-keyservices: - name: hmac-auth-service routes: - name: hmac-auth-route uris: - /post methods: - POST plugins: hmac-auth: validate_request_body: true upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Create a consumer with `hmac-auth` credential and a route with `hmac-auth` plugin configured as such: * Gateway API * APISIX CRD hmac-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: johnspec: gatewayRef: name: apisix credentials: - type: hmac-auth name: primary-cred config: key_id: john-key secret_key: john-secret-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: hmac-auth-plugin-configspec: plugins: - name: hmac-auth config: _meta: disable: false validate_request_body: true---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: hmac-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /post method: POST filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: hmac-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f hmac-auth-ic.yaml hmac-auth-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: johnspec: ingressClassName: apisix authParameter: hmacAuth: value: key_id: john-key secret_key: john-secret-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: hmac-auth-routespec: ingressClassName: apisix http: - name: hmac-auth-route match: paths: - /post methods: - POST upstreams: - name: httpbin-external-domain plugins: - name: hmac-auth enable: true config: validate_request_body: true Apply the configuration to your cluster: kubectl apply -f hmac-auth-ic.yaml Generate a signature. You can use the below Python snippet or other stack of your choice: hmac-sig-digest-header-gen.py import hmacimport hashlibimport base64from datetime import datetime, timezonekey_id = "john-key" # key idsecret_key = b"john-secret-key" # secret keyrequest_method = "POST" # HTTP methodrequest_path = "/post" # route URIalgorithm= "hmac-sha256" # can use other algorithms in allowed_algorithmsbody = '{"name": "world"}' # example request body# get current datetime in GMT# note: the signature will become invalid after the clock skew (default 300s).# you can regenerate the signature after it becomes invalid, or increase the clock# skew to prolong the validity within the advised security boundarygmt_time = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT')# construct the signing string (ordered)# the date and any subsequent custom headers should be lowercased and separated by a# single space character, i.e. `:`# https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#section-2.1.6signing_string = ( f"{key_id}\n" f"{request_method} {request_path}\n" f"date: {gmt_time}\n")# create signaturesignature = hmac.new(secret_key, signing_string.encode('utf-8'), hashlib.sha256).digest()signature_base64 = base64.b64encode(signature).decode('utf-8')# create the SHA-256 digest of the request body and base64 encode itbody_digest = hashlib.sha256(body.encode('utf-8')).digest()body_digest_base64 = base64.b64encode(body_digest).decode('utf-8')# construct the request headersheaders = { "Date": gmt_time, "Digest": f"SHA-256={body_digest_base64}", "Authorization": ( f'Signature keyId="{key_id}",algorithm="hmac-sha256",' f'headers="@request-target date",' f'signature="{signature_base64}"' )}# print headersprint(headers) Run the script: python3 hmac-sig-digest-header-gen.py You should see the request headers printed: {'Date': 'Fri, 06 Sep 2024 09:16:16 GMT', 'Digest': 'SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=', 'Authorization': 'Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date",signature="rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE="'} Using the headers generated, send a request to the route: curl "http://127.0.0.1:9080/post" -X POST \ -H "Date: Fri, 06 Sep 2024 09:16:16 GMT" \ -H "Digest: SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=" \ -H 'Authorization: Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date",signature="rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE="' \ -d '{"name": "world"}' You should see an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "data": "", "files": {}, "form": { "{\"name\": \"world\"}": "" }, "headers": { "Accept": "*/*", "Authorization": "Signature keyId=\"john-key\",algorithm=\"hmac-sha256\",headers=\"@request-target date\",signature=\"rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE=\"", "Content-Length": "17", "Content-Type": "application/x-www-form-urlencoded", "Date": "Fri, 06 Sep 2024 09:16:16 GMT", "Digest": "SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66d978c3-49f929ad5237da5340bbbeb4", "X-Consumer-Username": "john", "X-Credential-Identifier": "cred-john-hmac-auth", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "origin": "192.168.65.1, 34.0.34.160", "url": "http://127.0.0.1/post"} If you send a request without the digest or with an invalid digest: curl "http://127.0.0.1:9080/post" -X POST \ -H "Date: Fri, 06 Sep 2024 09:16:16 GMT" \ -H "Digest: SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=" \ -H 'Authorization: Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date",signature="rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE="' \ -d '{"name": "world"}' You should see an `HTTP/1.1 401 Unauthorized` response with the following message: {"message":"client request can't be validated"} ### Mandate Signed Headers[​](https://docs.api7.ai/hub/hmac-auth/#mandate-signed-headers "Direct link to Mandate Signed Headers") The following example demonstrates how you can mandate certain headers to be signed in the request's HMAC signature. * Admin API * ADC * Ingress Controller Create a consumer `john`: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john" }' Create `hmac-auth` credential for the consumer: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-hmac-auth", "plugins": { "hmac-auth": { "key_id": "john-key", "secret_key": "john-secret-key" } } }' Create a route with the `hmac-auth` plugin which requires three headers to be present in the HMAC signature: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "hmac-auth-route", "uri": "/get", "methods": ["GET"], "plugins": { "hmac-auth": { "signed_headers": ["date","x-custom-header-a", "x-custom-header-b"] } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Create a consumer with `hmac-auth` credential and a route with `hmac-auth` plugin configured as such: adc.yaml consumers: - username: john credentials: - name: hmac-auth type: hmac-auth config: key_id: john-key secret_key: john-secret-keyservices: - name: hmac-auth-service routes: - name: hmac-auth-route uris: - /get methods: - GET plugins: hmac-auth: signed_headers: - date - x-custom-header-a - x-custom-header-b upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml Create a consumer with `hmac-auth` credential and a route with `hmac-auth` plugin configured as such: * Gateway API * APISIX CRD hmac-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: johnspec: gatewayRef: name: apisix credentials: - type: hmac-auth name: primary-cred config: key_id: john-key secret_key: john-secret-key---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: hmac-auth-plugin-configspec: plugins: - name: hmac-auth config: _meta: disable: false signed_headers: - date - x-custom-header-a - x-custom-header-b---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: hmac-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /get method: GET filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: hmac-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f hmac-auth-ic.yaml hmac-auth-ic.yaml apiVersion: apisix.apache.org/v2kind: ApisixConsumermetadata: namespace: aic name: johnspec: ingressClassName: apisix authParameter: hmacAuth: value: key_id: john-key secret_key: john-secret-key---apiVersion: apisix.apache.org/v2kind: ApisixUpstreammetadata: namespace: aic name: httpbin-external-domainspec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org---apiVersion: apisix.apache.org/v2kind: ApisixRoutemetadata: namespace: aic name: hmac-auth-routespec: ingressClassName: apisix http: - name: hmac-auth-route match: paths: - /get methods: - GET upstreams: - name: httpbin-external-domain plugins: - name: hmac-auth enable: true config: signed_headers: - date - x-custom-header-a - x-custom-header-b Apply the configuration to your cluster: kubectl apply -f hmac-auth-ic.yaml Generate a signature. You can use the below Python snippet or other stack of your choice: hmac-sig-req-header-gen.py import hmacimport hashlibimport base64from datetime import datetime, timezonekey_id = "john-key" # key idsecret_key = b"john-secret-key" # secret keyrequest_method = "GET" # HTTP methodrequest_path = "/get" # route URIalgorithm= "hmac-sha256" # can use other algorithms in allowed_algorithmscustom_header_a = "hello123" # required custom headercustom_header_b = "world456" # required custom header# get current datetime in GMT# note: the signature will become invalid after the clock skew (default 300s)# you can regenerate the signature after it becomes invalid, or increase the clock# skew to prolong the validity within the advised security boundarygmt_time = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT')# construct the signing string (ordered)# the date and any subsequent custom headers should be lowercased and separated by a# single space character, i.e. `:`# https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#section-2.1.6signing_string = ( f"{key_id}\n" f"{request_method} {request_path}\n" f"date: {gmt_time}\n" f"x-custom-header-a: {custom_header_a}\n" f"x-custom-header-b: {custom_header_b}\n")# create signaturesignature = hmac.new(secret_key, signing_string.encode('utf-8'), hashlib.sha256).digest()signature_base64 = base64.b64encode(signature).decode('utf-8')# construct the request headersheaders = { "Date": gmt_time, "Authorization": ( f'Signature keyId="{key_id}",algorithm="hmac-sha256",' f'headers="@request-target date x-custom-header-a x-custom-header-b",' f'signature="{signature_base64}"' ), "x-custom-header-a": custom_header_a, "x-custom-header-b": custom_header_b}# print headersprint(headers) Run the script: python3 hmac-sig-req-header-gen.py You should see the request headers printed: {'Date': 'Fri, 06 Sep 2024 09:58:49 GMT', 'Authorization': 'Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="MwJR8JOhhRLIyaHlJ3Snbrf5hv0XwdeeRiijvX3A3yE="', 'x-custom-header-a': 'hello123', 'x-custom-header-b': 'world456'} Using the headers generated, send a request to the route: curl -X GET "http://127.0.0.1:9080/get" \ -H "Date: Fri, 06 Sep 2024 09:58:49 GMT" \ -H 'Authorization: Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date x-custom-header-a x-custom-header-b",signature="MwJR8JOhhRLIyaHlJ3Snbrf5hv0XwdeeRiijvX3A3yE="' \ -H "x-custom-header-a: hello123" \ -H "x-custom-header-b: world456" You should see an `HTTP/1.1 200 OK` response similar to the following: { "args": {}, "headers": { "Accept": "*/*", "Authorization": "Signature keyId=\"john-key\",algorithm=\"hmac-sha256\",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"MwJR8JOhhRLIyaHlJ3Snbrf5hv0XwdeeRiijvX3A3yE=\"", "Date": "Fri, 06 Sep 2024 09:58:49 GMT", "Host": "127.0.0.1", "User-Agent": "curl/8.6.0", "X-Amzn-Trace-Id": "Root=1-66d98196-64a58db25ece71c077999ecd", "X-Consumer-Username": "john", "X-Credential-Identifier": "cred-john-hmac-auth", "X-Custom-Header-A": "hello123", "X-Custom-Header-B": "world456", "X-Forwarded-Host": "127.0.0.1" }, "origin": "192.168.65.1, 103.97.2.206", "url": "http://127.0.0.1/get"} ### Rate Limit with Anonymous Consumer[​](https://docs.api7.ai/hub/hmac-auth/#rate-limit-with-anonymous-consumer "Direct link to Rate Limit with Anonymous Consumer") The following example demonstrates how you can configure different rate limiting policies by regular and anonymous consumers, where the anonymous consumer does not need to authenticate and has less quota. * Admin API * ADC * Ingress Controller Create a regular consumer `john` and configure the `limit-count` plugin to allow for a quota of 3 within a 30-second window: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "john", "plugins": { "limit-count": { "count": 3, "time_window": 30, "rejected_code": 429, "policy": "local" } } }' Create the `hmac-auth` credential for the consumer `john`: curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "cred-john-hmac-auth", "plugins": { "hmac-auth": { "key_id": "john-key", "secret_key": "john-secret-key" } } }' Create an anonymous user `anonymous` and configure the `limit-count` plugin to allow for a quota of 1 within a 30-second window: curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "username": "anonymous", "plugins": { "limit-count": { "count": 1, "time_window": 30, "rejected_code": 429, "policy": "local" } } }' Create a route and configure the `hmac-auth` plugin to accept anonymous consumer `anonymous` from bypassing the authentication: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "hmac-auth-route", "uri": "/get", "methods": ["GET"], "plugins": { "hmac-auth": { "anonymous_consumer": "anonymous" } }, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' Configure consumers with different rate limits and a route that accepts anonymous users: adc.yaml consumers: - username: john plugins: limit-count: count: 3 time_window: 30 rejected_code: 429 policy: local credentials: - name: hmac-auth type: hmac-auth config: key_id: john-key secret_key: john-secret-key - username: anonymous plugins: limit-count: count: 1 time_window: 30 rejected_code: 429 policy: localservices: - name: anonymous-rate-limit-service routes: - name: hmac-auth-route uris: - /get methods: - GET plugins: hmac-auth: anonymous_consumer: anonymous upstream: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1 Synchronize the configuration to the gateway: adc sync -f adc.yaml * Gateway API * APISIX CRD Configure consumers with different rate limits and a route that accepts anonymous users: hmac-auth-ic.yaml apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: johnspec: gatewayRef: name: apisix credentials: - type: hmac-auth name: primary-cred config: key_id: john-key secret_key: john-secret-key plugins: - name: limit-count config: count: 3 time_window: 30 rejected_code: 429 policy: local---apiVersion: apisix.apache.org/v1alpha1kind: Consumermetadata: namespace: aic name: anonymousspec: gatewayRef: name: apisix plugins: - name: limit-count config: count: 1 time_window: 30 rejected_code: 429 policy: local---apiVersion: v1kind: Servicemetadata: namespace: aic name: httpbin-external-domainspec: type: ExternalName externalName: httpbin.org---apiVersion: apisix.apache.org/v1alpha1kind: PluginConfigmetadata: namespace: aic name: hmac-auth-plugin-configspec: plugins: - name: hmac-auth config: anonymous_consumer: aic_anonymous # namespace_consumername---apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: namespace: aic name: hmac-auth-routespec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /get method: GET filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: hmac-auth-plugin-config backendRefs: - name: httpbin-external-domain port: 80 Apply the configuration to your cluster: kubectl apply -f hmac-auth-ic.yaml note The ApisixConsumer CRD currently does not support configuring plugins on consumers, except for the authentication plugins allowed in `authParameter`. This example cannot be completed with APISIX CRDs. Generate a signature. You can use the below Python snippet or other stack of your choice: hmac-sig-header-gen.py import hmacimport hashlibimport base64from datetime import datetime, timezonekey_id = "john-key" # key idsecret_key = b"john-secret-key" # secret keyrequest_method = "GET" # HTTP methodrequest_path = "/get" # route URIalgorithm= "hmac-sha256" # can use other algorithms in allowed_algorithms# get current datetime in GMT# note: the signature will become invalid after the clock skew (default 300s)# you can regenerate the signature after it becomes invalid, or increase the clock# skew to prolong the validity within the advised security boundarygmt_time = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT')# construct the signing string (ordered)# the date and any subsequent custom headers should be lowercased and separated by a# single space character, i.e. `:`# https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#section-2.1.6signing_string = ( f"{key_id}\n" f"{request_method} {request_path}\n" f"date: {gmt_time}\n")# create signaturesignature = hmac.new(secret_key, signing_string.encode('utf-8'), hashlib.sha256).digest()signature_base64 = base64.b64encode(signature).decode('utf-8')# construct the request headersheaders = { "Date": gmt_time, "Authorization": ( f'Signature keyId="{key_id}",algorithm="{algorithm}",' f'headers="@request-target date",' f'signature="{signature_base64}"' )}# print headersprint(headers) Run the script: python3 hmac-sig-header-gen.py You should see the request headers printed: {'Date': 'Mon, 21 Oct 2024 17:31:18 GMT', 'Authorization': 'Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date",signature="ztFfl9w7LmCrIuPjRC/DWSF4gN6Bt8dBBz4y+u1pzt8="'} To verify, send five consecutive requests with the generated headers: resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -H "Date: Mon, 21 Oct 2024 17:31:18 GMT" -H 'Authorization: Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target date",signature="ztFfl9w7LmCrIuPjRC/DWSF4gN6Bt8dBBz4y+u1pzt8="' -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that out of the 5 requests, 3 requests were successful (status code 200) while the others were rejected (status code 429). 200: 3, 429: 2 Send five anonymous requests: resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/anything" -o /dev/null -s -w "%{http_code}\n") && \ count_200=$(echo "$resp" | grep "200" | wc -l) && \ count_429=$(echo "$resp" | grep "429" | wc -l) && \ echo "200": $count_200, "429": $count_429 You should see the following response, showing that only one request was successful: 200: 1, 429: 4 * [Implementation](https://docs.api7.ai/hub/hmac-auth/#implementation) * [Examples](https://docs.api7.ai/hub/hmac-auth/#examples) * [Implement HMAC Authentication on a Route](https://docs.api7.ai/hub/hmac-auth/#implement-hmac-authentication-on-a-route) * [Hide Authorization Information From Upstream](https://docs.api7.ai/hub/hmac-auth/#hide-authorization-information-from-upstream) * [Enable Body Validation](https://docs.api7.ai/hub/hmac-auth/#enable-body-validation) * [Mandate Signed Headers](https://docs.api7.ai/hub/hmac-auth/#mandate-signed-headers) * [Rate Limit with Anonymous Consumer](https://docs.api7.ai/hub/hmac-auth/#rate-limit-with-anonymous-consumer) --- # Set Up Ingress Controller and Gateway | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#__docusaurus_skipToContent_fallback) * * * Version: latest On this page This tutorial focuses on setting up APISIX Ingress Controller and APISIX gateway on a Kubernetes cluster for local learning and testing. for API7 Enterprise users If you are using API7 Enterprise, the API7 Dashboard will guide you through the setup of API7 Ingress Controller and Gateway, including generating the necessary deployment scripts. Make sure that you: * Create a new gateway group of type **Ingress Controller**. The Dashboard will then generate deployment steps for the following, based on the **namespace** and **name** you specified: * Install the Ingress Controller. * Deploy the GatewayProxy configuration. Select the **Gateway API** tab if you plan to use Gateway API, or the **Ingress** tab if you plan to use Ingress or APISIX CRDs. * Deploy a Gateway instance on Kubernetes. Once completed, skip to the [next tutorial](https://docs.api7.ai/ingress-controller/proxy-requests-to-a-service) where you will learn how to create a route that proxies requests to an upstream service on the same cluster. Prerequisites[​](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#prerequisites "Direct link to Prerequisites") ---------------------------------------------------------------------------------------------------------------------------------------------- * Install [Docker](https://docs.docker.com/get-docker/) as a dependency of [kind](https://kind.sigs.k8s.io/) . * Install [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) and create a local cluster, or use any existing Kubernetes cluster (version 1.26+). * Install [Helm](https://helm.sh/docs/intro/install/) (version 3.8+). * Install [kubectl](https://kubernetes.io/docs/tasks/tools/) . Create a Namespace[​](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#create-a-namespace "Direct link to Create a Namespace") ------------------------------------------------------------------------------------------------------------------------------------------------------------- Create a new namespace `aic` (or any name of your choice): kubectl create namespace aic Optionally, you can set the namespace as the preferred namespace: kubectl config set-context --current --namespace=aic Install APISIX and APISIX Ingress Controller[​](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#install-apisix-and-apisix-ingress-controller "Direct link to Install APISIX and APISIX Ingress Controller") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In this section, you will install APISIX and APISIX Ingress Controller in standalone mode on your Kubernetes cluster. Deployment Mode The standalone mode is recommended over the traditional etcd-based deployment, as it avoids the stability issues that can occur when running APISIX and etcd together inside Kubernetes. Install APISIX and APISIX Ingress Controller: helm repo add apisix https://apache.github.io/apisix-helm-charthelm repo updatehelm install apisix \ --namespace aic \ --create-namespace \ --set apisix.deployment.role=traditional \ --set apisix.deployment.role_traditional.config_provider=yaml \ --set etcd.enabled=false \ --set ingress-controller.enabled=true \ --set ingress-controller.config.provider.type=apisix-standalone \ --set ingress-controller.apisix.adminService.namespace=aic \ --set ingress-controller.gatewayProxy.createDefault=true \ apisix/apisix ❶&❷ Start APISIX in [API-driven standalone mode](https://docs.api7.ai/apisix/production/deployment-modes#api-driven) . ❸ Disable the etcd deployment. ❹ Install APISIX Ingress Controller (along with APISIX). ❺ Configure the controller to operate in standalone mode, where configuration is pushed directly to the gateway instead of using etcd. ➏ Configure the namespace of the APISIX Admin Service that the controller will connect to for pushing configurations. ➐ Automatically creates a GatewayProxy resource that defines the connection information with APISIX. See the GatewayProxy resource Find the name of the GatewayProxy: kubectl get gatewayproxy You should see name of the GatewayProxy: NAME AGEapisix-config 12s Show the configuration of the GatewayProxy resource in YAML format: kubectl get gatewayproxy apisix-config -o yaml You should see the configuration of the GatewayProxy resource similar to the following: apiVersion: apisix.apache.org/v1alpha1kind: GatewayProxymetadata: annotations: meta.helm.sh/release-name: apisix meta.helm.sh/release-namespace: aic creationTimestamp: "2025-11-25T10:07:40Z" generation: 1 labels: app.kubernetes.io/managed-by: Helm name: apisix-config namespace: aic resourceVersion: "3073" uid: 6b0a30db-530c-4f14-b127-024d0507b18espec: provider: controlPlane: auth: adminKey: value: edd1c9f034335f136f87ad84b625c8f1 type: AdminKey service: name: apisix-admin port: 9180 type: ControlPlane Verify Installation[​](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#verify-installation "Direct link to Verify Installation") ---------------------------------------------------------------------------------------------------------------------------------------------------------------- To verify that APISIX Ingress Controller is installed and running, check the status of the pods: kubectl get pods If everything is ok, you should see that all pods are in the `Running` status: NAME READY STATUS RESTARTS AGEapisix-bffb459b8-rcqz7 1/1 Running 0 2m53sapisix-ingress-controller-ff66c9585-wlxfh 2/2 Running 0 2m53s You can also check the status of the services: kubectl get services If everything is ok, you should see a response similar to the following: NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEapisix-admin ClusterIP 10.96.61.223 9180/TCP 2m54sapisix-gateway NodePort 10.96.108.211 80:30834/TCP 2m54sapisix-ingress-controller ClusterIP 10.96.249.157 8080/TCP 2m54sapisix-ingress-controller-webhook-svc ClusterIP 10.96.161.96 443/TCP 2m54s To check the installed APISIX version, first map the gateway service port to the local machine's port: kubectl port-forward svc/apisix-gateway 9080:80 & Then send a request to the gateway: curl -sI "http://127.0.0.1:9080" | grep Server If everything is ok, you should see the APISIX version: Server: APISIX/3.15.0 info If you would like to use a load balancer to expose the service on the kind cluster as an alternative to port forwarding, see [load balancer kind documentation](https://kind.sigs.k8s.io/docs/user/loadbalancer/) . Define Controller and Gateway[​](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#define-controller-and-gateway "Direct link to Define Controller and Gateway") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- See the Gateway API tab if you plan to use Gateway API, or the Ingress tab if you plan to use Ingress or APISIX CRDs. * Gateway API * Ingress If you will be using Gateway API, define the GatewayClass and Gateway resources: gatewayclass-gateway.yaml apiVersion: gateway.networking.k8s.io/v1kind: GatewayClassmetadata: name: apisixspec: controllerName: apisix.apache.org/apisix-ingress-controller---apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: namespace: aic name: apisixspec: gatewayClassName: apisix listeners: - name: http protocol: HTTP port: 80 infrastructure: parametersRef: group: apisix.apache.org kind: GatewayProxy name: apisix-config Note that the `port` in the Gateway listener is required but ignored. This is due to limitations in the data plane: it cannot dynamically open new ports. Since the Ingress Controller does not manage the data plane deployment, it cannot automatically update the configuration or restart the data plane to apply port changes. Apply the configuration to your cluster: kubectl apply -f gatewayclass-gateway.yaml If you will be using Ingress or APISIX CRDs, the following IngressClass resource is already applied during installation. You may proceed without any additional configuration. apiVersion: networking.k8s.io/v1kind: IngressClassmetadata: name: apisixspec: controller: apisix.apache.org/apisix-ingress-controller parameters: apiGroup: apisix.apache.org kind: GatewayProxy name: apisix-config namespace: aic scope: Namespace To learn more about these parameters and how to adjust them as needed, see [Define Controller and Gateway](https://docs.api7.ai/ingress-controller/reference/examples#define-controller-and-gateway) . Next Step[​](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#next-step "Direct link to Next Step") ---------------------------------------------------------------------------------------------------------------------------------- In the [next tutorial](https://docs.api7.ai/ingress-controller/proxy-requests-to-a-service) , you will learn how to create a route that proxies requests to an upstream service on the same cluster. * [Prerequisites](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#prerequisites) * [Create a Namespace](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#create-a-namespace) * [Install APISIX and APISIX Ingress Controller](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#install-apisix-and-apisix-ingress-controller) * [Verify Installation](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#verify-installation) * [Define Controller and Gateway](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#define-controller-and-gateway) * [Next Step](https://docs.api7.ai/ingress-controller/set-up-ingress-controller-and-gateway/#next-step) --- # Admin API Key | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/apisix/production/security/admin-api-key/#__docusaurus_skipToContent_fallback) * * * Version: 3.15.0 On this page Admin API keys are used to control access to the APISIX Admin API endpoints, allowing only authorized users to manage and administer APISIX resources via the Admin API. Key Requirement and Permissions[​](https://docs.api7.ai/apisix/production/security/admin-api-key/#key-requirement-and-permissions "Direct link to Key Requirement and Permissions") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ It is strongly recommended that you switch on the mandatory requirement of Admin API keys in production and configure a set of complex keys to harden your APISIX instances. The example [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) documents the following details, where Admin API key is required by default and set to an empty string: config.yaml.example deployment: admin: admin_key_required: true admin_key: - name: admin key: '' role: admin # read and write access - name: viewer key: 4054f7cf07e344346cd3f287985e76a2 role: viewer # read-only access If you do not configure a custom Admin API key, APISIX will automatically generate a key at runtime. To customize these configurations for your deployment, add the custom configurations to the `config.yaml` [configuration file](https://docs.api7.ai/apisix/reference/configuration-files#configyaml-and-configyamlexample) and [reload APISIX](https://docs.api7.ai/apisix/reference/apisix-cli#apisix-reload) for changes to take effect. Other Admin API Security Options[​](https://docs.api7.ai/apisix/production/security/admin-api-key/#other-admin-api-security-options "Direct link to Other Admin API Security Options") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In addition to setting the Admin API keys, you can also customize other configurations to further secure the Admin API, such as: * Admin API CORS * [Admin API Access IP whitelist](https://docs.api7.ai/apisix/production/security/ip-restriction#restrict-admin-api-access-by-ip) * [Admin API mTLS](https://docs.api7.ai/apisix/production/security/mtls/configure-mtls-between-client-and-admin-api) For a complete list of configuration options, see [`config.yaml.example`](https://github.com/apache/apisix/blob/master/conf/config.yaml.example) . * [Key Requirement and Permissions](https://docs.api7.ai/apisix/production/security/admin-api-key/#key-requirement-and-permissions) * [Other Admin API Security Options](https://docs.api7.ai/apisix/production/security/admin-api-key/#other-admin-api-security-options) --- # RocketMQ Logger | APISIX & API7 API Gateway Docs [Skip to main content](https://docs.api7.ai/hub/rocketmq-logger/#__docusaurus_skipToContent_fallback) * * * Copy for LLM Copy Page as Markdown Copy page as Markdown for LLMs ![](https://static.api7.ai/uploads/2025/08/06/4ilIE22W_markdown.svg) View as Markdown View this page as Markdown ![](https://static.api7.ai/uploads/2025/08/06/Grz9ppCq_chatgpt.svg) Open in ChatGPT Ask GPT about this page ![](https://static.api7.ai/uploads/2025/08/06/dMWgh2cy_claude.svg) Open in Claude Ask Claude about this page The `rocketmq-logger` plugin pushes request and response logs as JSON objects to your RocketMQ clusters in batches and supports the customization of log formats. Examples[​](https://docs.api7.ai/hub/rocketmq-logger/#examples "Direct link to Examples") ------------------------------------------------------------------------------------------ The examples below demonstrate how you can configure `rocketmq-logger` plugin for different scenarios. To follow along the examples, start a sample RocketMQ cluster using the below Docker compose file: docker-compose.yml version: "3"services: rocketmq_namesrv: image: apacherocketmq/rocketmq:4.6.0 container_name: rmqnamesrv restart: unless-stopped ports: - "9876:9876" command: sh mqnamesrv networks: rocketmq_net: rocketmq_broker: image: apacherocketmq/rocketmq:4.6.0 container_name: rmqbroker restart: unless-stopped ports: - "10909:10909" - "10911:10911" - "10912:10912" depends_on: - rocketmq_namesrv command: sh mqbroker -n rmqnamesrv:9876 -c ../conf/broker.conf networks: rocketmq_net:networks: rocketmq_net: Start containers: docker compose up -d In a few seconds, the name server and broker should start. Create the `TopicTest` topic: docker exec -i rmqnamesrv rm /home/rocketmq/rocketmq-4.6.0/conf/tools.ymldocker exec -i rmqnamesrv /home/rocketmq/rocketmq-4.6.0/bin/mqadmin updateTopic -n rmqnamesrv:9876 -t TopicTest -c DefaultCluster Wait for message in the configured RockerMQ topic: docker run -it --name rockemq_consumer -e NAMESRV_ADDR=localhost:9876 --net host apacherocketmq/rocketmq:4.6.0 sh tools.sh org.apache.rocketmq.example.quickstart.Consumer In a few seconds, the consumer should start and listen for messages from APISIX: 01:32:17.823 [main] DEBUG i.n.u.i.l.InternalLoggerFactory - Using SLF4J as the default logging frameworkConsumer Started. Open a new terminal session for the following steps working with APISIX. ### Log in Different Meta Log Formats[​](https://docs.api7.ai/hub/rocketmq-logger/#log-in-different-meta-log-formats "Direct link to Log in Different Meta Log Formats") The following example demonstrates how you can enable the `rocketmq-logger` plugin on a route, which logs client requests to the route and pushes logs to RocketMQ. You will also understand the differences between the `default` and `origin` meta log formats. Create a route with `rocketmq-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "rocketmq-logger-route", "uri": "/anything", "plugins": { "rocketmq-logger": { "nameserver_list": [ "127.0.0.1:9876" ], "topic": "TopicTest", "key": "key1", "timeout": 30, "meta_format": "default", "batch_max_size": 1 } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `meta_format`: set to the `default` log format. ❷ `batch_max_size`: set to 1 to send the log entry immediately. Send a request to the route to generate a log entry: curl -i "http://127.0.0.1:9080/anything" You should see a log entry in the other terminal running the consumer: ConsumeMessageThread_1 Receive New Messages: [MessageExt [queueId=0, storeSize=857, queueOffset=0, sysFlag=0, bornTimestamp=1703661585406, bornHost=/172.18.0.1:38362, storeTimestamp=1703661585486, storeHost=/172.18.0.3:10911, msgId=AC12000300002A9F0000000000000000, commitLogOffset=0, bodyCRC=1383911373, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=1, KEYS=key1, CONSUME_START_TIME=1703661585635, UNIQ_KEY=7F000001A7045E46ABB3892F67FD0002, CLUSTER=DefaultCluster, WAIT=true}, body=[123, 34, 99, 108, 105, 101, 110, 116, 95, 105, 112, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 34, 58, 34, 49, 57, 56, 46, 49, 56, 46, 49, 46, 57, 50, 58, 56, 48, 34, 44, 34, 115, 116, 97, 114, 116, 95, 116, 105, 109, 101, 34, 58, 49, 55, 48, 51, 54, 54, 49, 53, 56, 52, 56, 57, 52, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 104, 111, 115, 116, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 34, 44, 34, 97, 99, 99, 101, 112, 116, 34, 58, 34, 42, 47, 42, 34, 44, 34, 117, 115, 101, 114, 45, 97, 103, 101, 110, 116, 34, 58, 34, 99, 117, 114, 108, 47, 55, 46, 56, 49, 46, 48, 34, 125, 44, 34, 113, 117, 101, 114, 121, 115, 116, 114, 105, 110, 103, 34, 58, 123, 125, 44, 34, 115, 105, 122, 101, 34, 58, 56, 54, 44, 34, 117, 114, 105, 34, 58, 34, 47, 97, 110, 121, 116, 104, 105, 110, 103, 34, 44, 34, 117, 114, 108, 34, 58, 34, 104, 116, 116, 112, 58, 47, 47, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 47, 97, 110, 121, 116, 104, 105, 110, 103, 34, 44, 34, 109, 101, 116, 104, 111, 100, 34, 58, 34, 71, 69, 84, 34, 125, 44, 34, 114, 111, 117, 116, 101, 95, 105, 100, 34, 58, 34, 114, 111, 99, 107, 101, 116, 109, 113, 45, 108, 111, 103, 103, 101, 114, 45, 114, 111, 117, 116, 101, 34, 44, 34, 97, 112, 105, 115, 105, 120, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 56, 46, 57, 57, 57, 56, 52, 53, 53, 48, 52, 55, 54, 48, 55, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 53, 48, 51, 44, 34, 108, 97, 116, 101, 110, 99, 121, 34, 58, 53, 49, 49, 46, 57, 57, 57, 56, 52, 53, 53, 48, 52, 55, 54, 44, 34, 114, 101, 115, 112, 111, 110, 115, 101, 34, 58, 123, 34, 115, 105, 122, 101, 34, 58, 51, 51, 50, 44, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 99, 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 34, 58, 34, 49, 54, 50, 34, 44, 34, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 34, 58, 34, 99, 108, 111, 115, 101, 34, 44, 34, 100, 97, 116, 101, 34, 58, 34, 87, 101, 100, 44, 32, 50, 55, 32, 68, 101, 99, 32, 50, 48, 50, 51, 32, 48, 55, 58, 49, 57, 58, 52, 53, 32, 71, 77, 84, 34, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 34, 65, 80, 73, 83, 73, 88, 47, 51, 46, 55, 46, 48, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 34, 58, 34, 116, 101, 120, 116, 47, 104, 116, 109, 108, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 34, 125, 44, 34, 115, 116, 97, 116, 117, 115, 34, 58, 52, 48, 52, 125, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 123, 34, 104, 111, 115, 116, 110, 97, 109, 101, 34, 58, 34, 117, 98, 117, 110, 116, 117, 45, 108, 105, 110, 117, 120, 45, 50, 50, 45, 48, 52, 45, 48, 50, 45, 100, 101, 115, 107, 116, 111, 112, 34, 44, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 34, 51, 46, 55, 46, 48, 34, 125, 44, 34, 115, 101, 114, 118, 105, 99, 101, 95, 105, 100, 34, 58, 34, 34, 125], transactionId='null'}]] You can use JavaScript to translate the body: const data = [123, 34, 99, 108, 105, 101, 110, 116, 95, 105, 112, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 34, 58, 34, 49, 57, 56, 46, 49, 56, 46, 49, 46, 57, 50, 58, 56, 48, 34, 44, 34, 115, 116, 97, 114, 116, 95, 116, 105, 109, 101, 34, 58, 49, 55, 48, 51, 54, 54, 49, 53, 56, 52, 56, 57, 52, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 104, 111, 115, 116, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 34, 44, 34, 97, 99, 99, 101, 112, 116, 34, 58, 34, 42, 47, 42, 34, 44, 34, 117, 115, 101, 114, 45, 97, 103, 101, 110, 116, 34, 58, 34, 99, 117, 114, 108, 47, 55, 46, 56, 49, 46, 48, 34, 125, 44, 34, 113, 117, 101, 114, 121, 115, 116, 114, 105, 110, 103, 34, 58, 123, 125, 44, 34, 115, 105, 122, 101, 34, 58, 56, 54, 44, 34, 117, 114, 105, 34, 58, 34, 47, 97, 110, 121, 116, 104, 105, 110, 103, 34, 44, 34, 117, 114, 108, 34, 58, 34, 104, 116, 116, 112, 58, 47, 47, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 47, 97, 110, 121, 116, 104, 105, 110, 103, 34, 44, 34, 109, 101, 116, 104, 111, 100, 34, 58, 34, 71, 69, 84, 34, 125, 44, 34, 114, 111, 117, 116, 101, 95, 105, 100, 34, 58, 34, 114, 111, 99, 107, 101, 116, 109, 113, 45, 108, 111, 103, 103, 101, 114, 45, 114, 111, 117, 116, 101, 34, 44, 34, 97, 112, 105, 115, 105, 120, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 56, 46, 57, 57, 57, 56, 52, 53, 53, 48, 52, 55, 54, 48, 55, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 53, 48, 51, 44, 34, 108, 97, 116, 101, 110, 99, 121, 34, 58, 53, 49, 49, 46, 57, 57, 57, 56, 52, 53, 53, 48, 52, 55, 54, 44, 34, 114, 101, 115, 112, 111, 110, 115, 101, 34, 58, 123, 34, 115, 105, 122, 101, 34, 58, 51, 51, 50, 44, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 99, 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 34, 58, 34, 49, 54, 50, 34, 44, 34, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 34, 58, 34, 99, 108, 111, 115, 101, 34, 44, 34, 100, 97, 116, 101, 34, 58, 34, 87, 101, 100, 44, 32, 50, 55, 32, 68, 101, 99, 32, 50, 48, 50, 51, 32, 48, 55, 58, 49, 57, 58, 52, 53, 32, 71, 77, 84, 34, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 34, 65, 80, 73, 83, 73, 88, 47, 51, 46, 55, 46, 48, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 34, 58, 34, 116, 101, 120, 116, 47, 104, 116, 109, 108, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 34, 125, 44, 34, 115, 116, 97, 116, 117, 115, 34, 58, 52, 48, 52, 125, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 123, 34, 104, 111, 115, 116, 110, 97, 109, 101, 34, 58, 34, 117, 98, 117, 110, 116, 117, 45, 108, 105, 110, 117, 120, 45, 50, 50, 45, 48, 52, 45, 48, 50, 45, 100, 101, 115, 107, 116, 111, 112, 34, 44, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 34, 51, 46, 55, 46, 48, 34, 125, 44, 34, 115, 101, 114, 118, 105, 99, 101, 95, 105, 100, 34, 58, 34, 34, 125]const charArray = data.map(code => String.fromCharCode(code))const resultString = charArray.join('')console.log(resultString) The result will be: { "client_ip": "127.0.0.1", "upstream": "198.18.1.92:80", "start_time": 1703661584894, "request": { "headers": { "host": "127.0.0.1:9080", "accept": "*/*", "user-agent": "curl/7.81.0" }, "querystring": {}, "size": 86, "uri": "/anything", "url": "http://127.0.0.1:9080/anything", "method": "GET" }, "route_id": "rocketmq-logger-route", "apisix_latency": 8.9998455047607, "upstream_latency": 503, "latency": 511.99984550476, "response": { "size": 332, "headers": { "content-length": "162", "connection": "close", "date": "Wed, 27 Dec 2023 07:19:45 GMT", "server": "APISIX/3.8.0", "content-type": "text/html; charset=utf-8" }, "status": 404 }, "server": { "hostname": "ubuntu-linux-22-04-02-desktop", "version": "3.8.0" }, "service_id": ""} Update the `rocketmq-logger` meta log format to `origin`: curl "http://127.0.0.1:9180/apisix/admin/routes/rocketmq-logger-route" -X PATCH \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "rocketmq-logger": { "meta_format": "origin" } } }' Send a request to the route again to generate a new log entry: curl -i "http://127.0.0.1:9080/anything" You should see a log entry in the other terminal running the consumer: ConsumeMessageThread_1 Receive New Messages: [MessageExt [queueId=0, storeSize=857, queueOffset=0, sysFlag=0, bornTimestamp=1703661585406, bornHost=/172.18.0.1:38362, storeTimestamp=1703661585486, storeHost=/172.18.0.3:10911, msgId=AC12000300002A9F0000000000000000, commitLogOffset=0, bodyCRC=1383911373, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=1, KEYS=key1, CONSUME_START_TIME=1703661585635, UNIQ_KEY=7F000001A7045E46ABB3892F67FD0002, CLUSTER=DefaultCluster, WAIT=true}, body=[123, 34, 99, 108, 105, 101, 110, 116, 95, 105, 112, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 34, 58, 34, 49, 57, 56, 46, 49, 56, 46, 49, 46, 57, 50, 58, 56, 48, 34, 44, 34, 115, 116, 97, 114, 116, 95, 116, 105, 109, 101, 34, 58, 49, 55, 48, 51, 54, 54, 49, 53, 56, 52, 56, 57, 52, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 104, 111, 115, 116, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 34, 44, 34, 97, 99, 99, 101, 112, 116, 34, 58, 34, 42, 47, 42, 34, 44, 34, 117, 115, 101, 114, 45, 97, 103, 101, 110, 116, 34, 58, 34, 99, 117, 114, 108, 47, 55, 46, 56, 49, 46, 48, 34, 125, 44, 34, 113, 117, 101, 114, 121, 115, 116, 114, 105, 110, 103, 34, 58, 123, 125, 44, 34, 115, 105, 122, 101, 34, 58, 56, 54, 44, 34, 117, 114, 105, 34, 58, 34, 47, 97, 110, 121, 116, 104, 105, 110, 103, 34, 44, 34, 117, 114, 108, 34, 58, 34, 104, 116, 116, 112, 58, 47, 47, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 47, 97, 110, 121, 116, 104, 105, 110, 103, 34, 44, 34, 109, 101, 116, 104, 111, 100, 34, 58, 34, 71, 69, 84, 34, 125, 44, 34, 114, 111, 117, 116, 101, 95, 105, 100, 34, 58, 34, 114, 111, 99, 107, 101, 116, 109, 113, 45, 108, 111, 103, 103, 101, 114, 45, 114, 111, 117, 116, 101, 34, 44, 34, 97, 112, 105, 115, 105, 120, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 56, 46, 57, 57, 57, 56, 52, 53, 53, 48, 52, 55, 54, 48, 55, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 53, 48, 51, 44, 34, 108, 97, 116, 101, 110, 99, 121, 34, 58, 53, 49, 49, 46, 57, 57, 57, 56, 52, 53, 53, 48, 52, 55, 54, 44, 34, 114, 101, 115, 112, 111, 110, 115, 101, 34, 58, 123, 34, 115, 105, 122, 101, 34, 58, 51, 51, 50, 44, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 99, 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 34, 58, 34, 49, 54, 50, 34, 44, 34, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 34, 58, 34, 99, 108, 111, 115, 101, 34, 44, 34, 100, 97, 116, 101, 34, 58, 34, 87, 101, 100, 44, 32, 50, 55, 32, 68, 101, 99, 32, 50, 48, 50, 51, 32, 48, 55, 58, 49, 57, 58, 52, 53, 32, 71, 77, 84, 34, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 34, 65, 80, 73, 83, 73, 88, 47, 51, 46, 55, 46, 48, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 34, 58, 34, 116, 101, 120, 116, 47, 104, 116, 109, 108, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 34, 125, 44, 34, 115, 116, 97, 116, 117, 115, 34, 58, 52, 48, 52, 125, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 123, 34, 104, 111, 115, 116, 110, 97, 109, 101, 34, 58, 34, 117, 98, 117, 110, 116, 117, 45, 108, 105, 110, 117, 120, 45, 50, 50, 45, 48, 52, 45, 48, 50, 45, 100, 101, 115, 107, 116, 111, 112, 34, 44, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 34, 51, 46, 55, 46, 48, 34, 125, 44, 34, 115, 101, 114, 118, 105, 99, 101, 95, 105, 100, 34, 58, 34, 34, 125], transactionId='null'}]] You can use JavaScript to translate the body: const data = [71, 69, 84, 32, 47, 97, 110, 121, 116, 104, 105, 110, 103, 32, 72, 84, 84, 80, 47, 49, 46, 49, 13, 10, 104, 111, 115, 116, 58, 32, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 13, 10, 117, 115, 101, 114, 45, 97, 103, 101, 110, 116, 58, 32, 99, 117, 114, 108, 47, 55, 46, 56, 49, 46, 48, 13, 10, 97, 99, 99, 101, 112, 116, 58, 32, 42, 47, 42, 13, 10, 13, 10]const charArray = data.map(code => String.fromCharCode(code))const resultString = charArray.join('')console.log(resultString) The result will be: GET /anything HTTP/1.1host: 127.0.0.1:9080user-agent: curl/7.81.0accept: */* ### Log Request and Response Headers With Plugin Metadata[​](https://docs.api7.ai/hub/rocketmq-logger/#log-request-and-response-headers-with-plugin-metadata "Direct link to Log Request and Response Headers With Plugin Metadata") The following example demonstrates how you can customize log format using [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) and [built-in variables](https://docs.api7.ai/apisix/reference/built-in-variables) to log specific headers from request and response. In APISIX, [plugin metadata](https://docs.api7.ai/apisix/key-concepts/plugin-metadata) is used to configure the common metadata fields of all plugin instances of the same plugin. It is useful when a plugin is enabled across multiple resources and requires a universal update to their metadata fields. First, create a route with `rocketmq-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "id": "rocketmq-logger-route", "uri": "/anything", "plugins": { "rocketmq-logger": { "nameserver_list": [ "127.0.0.1:9876" ], "topic": "TopicTest", "key": "key1", "timeout": 30, "meta_format": "default", "batch_max_size": 1 } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }' ❶ `meta_format`: set to the `default` log format. It is important to note that this is mandatory if you would like to customize log format with plugin metadata. If `meta_format` is set to `origin`, the log entries will remain in `origin` format. ❷ `batch_max_size`: set to 1 to send the log entry immediately. Next, configure the plugin metadata for `rocketmq-logger`: curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/rocketmq-logger" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "log_format": { "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr", "env": "$http_env", "resp_content_type": "$sent_http_Content_Type" } }' ❶ log the custom request header `env`. ❷ log the response header `Content-Type`. Send a request to the route with the `env` header: curl -i "http://127.0.0.1:9080/anything" -H "env: dev" You should see a log entry in the other terminal running the consumer: ConsumeMessageThread_1 Receive New Messages: [MessageExt [queueId=0, storeSize=364, queueOffset=3, sysFlag=0, bornTimestamp=1703662284683, bornHost=/172.18.0.1:34628, storeTimestamp=1703662284755, storeHost=/172.18.0.3:10911, msgId=AC12000300002A9F0000000000000577, commitLogOffset=1399, bodyCRC=39663622, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=4, KEYS=key1, CONSUME_START_TIME=1703662284829, UNIQ_KEY=7F000001A703880B19C0893A138B0002, CLUSTER=DefaultCluster, WAIT=true}, body=[123, 34, 104, 111, 115, 116, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 34, 99, 108, 105, 101, 110, 116, 95, 105, 112, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 34, 114, 101, 115, 112, 95, 99, 111, 110, 116, 101, 110, 116, 95, 116, 121, 112, 101, 34, 58, 34, 116, 101, 120, 116, 47, 104, 116, 109, 108, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 34, 44, 34, 114, 111, 117, 116, 101, 95, 105, 100, 34, 58, 34, 114, 111, 99, 107, 101, 116, 109, 113, 45, 108, 111, 103, 103, 101, 114, 45, 114, 111, 117, 116, 101, 34, 44, 34, 101, 110, 118, 34, 58, 34, 100, 101, 118, 34, 44, 34, 64, 116, 105, 109, 101, 115, 116, 97, 109, 112, 34, 58, 34, 50, 48, 50, 51, 45, 49, 50, 45, 50, 55, 84, 49, 53, 58, 51, 49, 58, 50, 52, 43, 48, 56, 58, 48, 48, 34, 125], transactionId='null'}]] You can use JavaScript to translate the body: const data = [123, 34, 104, 111, 115, 116, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 34, 99, 108, 105, 101, 110, 116, 95, 105, 112, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 34, 114, 101, 115, 112, 95, 99, 111, 110, 116, 101, 110, 116, 95, 116, 121, 112, 101, 34, 58, 34, 116, 101, 120, 116, 47, 104, 116, 109, 108, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 34, 44, 34, 114, 111, 117, 116, 101, 95, 105, 100, 34, 58, 34, 114, 111, 99, 107, 101, 116, 109, 113, 45, 108, 111, 103, 103, 101, 114, 45, 114, 111, 117, 116, 101, 34, 44, 34, 101, 110, 118, 34, 58, 34, 100, 101, 118, 34, 44, 34, 64, 116, 105, 109, 101, 115, 116, 97, 109, 112, 34, 58, 34, 50, 48, 50, 51, 45, 49, 50, 45, 50, 55, 84, 49, 53, 58, 51, 49, 58, 50, 52, 43, 48, 56, 58, 48, 48, 34, 125]const charArray = data.map(code => String.fromCharCode(code))const resultString = charArray.join('')console.log(resultString) The result will be: { "host": "127.0.0.1", "client_ip": "127.0.0.1", "resp_content_type": "text/html; charset=utf-8", "route_id": "rocketmq-logger-route", "env": "dev", "@timestamp": "2023-12-27T15:31:24+08:00"} ### Log Request Bodies Conditionally[​](https://docs.api7.ai/hub/rocketmq-logger/#log-request-bodies-conditionally "Direct link to Log Request Bodies Conditionally") The following example demonstrates how you can conditionally log request body. Create a route with `rocketmq-logger` as follows: curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${ADMIN_API_KEY}" \ -d '{ "plugins": { "rocketmq-logger": { "nameserver_list": [ "127.0.0.1:9876" ], "topic": "TopicTest", "key": "key1", "timeout": 30, "meta_format": "default", "batch_max_size": 1, "include_req_body": true, "include_req_body_expr": [["arg_log_body", "==", "yes"]] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" }, "uri": "/anything", "id": "rocketmq-logger-route"}' ❶ `include_req_body`: set to true to include request body. ❷ `include_req_body_expr`: only include request body if the URL query string `log_body` is `yes`. Send a request to the route with an URL query string satisfying the condition: curl -i "http://127.0.0.1:9080/anything?log_body=yes" -X POST -d '{"env": "dev"}' You should see a log entry in the other terminal running the consumer: ConsumeMessageThread_1 Receive New Messages: [MessageExt [queueId=0, storeSize=1002, queueOffset=5, sysFlag=0, bornTimestamp=1703662942878, bornHost=/172.18.0.1:36804, storeTimestamp=1703662942946, storeHost=/172.18.0.3:10911, msgId=AC12000300002A9F0000000000000843, commitLogOffset=2115, bodyCRC=441488425, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=6, KEYS=key1, CONSUME_START_TIME=1703662942961, UNIQ_KEY=7F000001A702F30B5FB389441E9E0003, CLUSTER=DefaultCluster, WAIT=true}, body=[123, 34, 99, 108, 105, 101, 110, 116, 95, 105, 112, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 34, 58, 34, 49, 57, 56, 46, 49, 56, 46, 49, 46, 57, 50, 58, 56, 48, 34, 44, 34, 115, 116, 97, 114, 116, 95, 116, 105, 109, 101, 34, 58, 49, 55, 48, 51, 54, 54, 50, 57, 52, 50, 52, 55, 53, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 98, 111, 100, 121, 34, 58, 34, 123, 92, 34, 101, 110, 118, 92, 34, 58, 32, 92, 34, 100, 101, 118, 92, 34, 125, 34, 44, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 104, 111, 115, 116, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 34, 44, 34, 117, 115, 101, 114, 45, 97, 103, 101, 110, 116, 34, 58, 34, 99, 117, 114, 108, 47, 55, 46, 56, 49, 46, 48, 34, 44, 34, 97, 99, 99, 101, 112, 116, 34, 58, 34, 42, 47, 42, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 34, 58, 34, 49, 52, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 34, 58, 34, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120, 45, 119, 119, 119, 45, 102, 111, 114, 109, 45, 117, 114, 108, 101, 110, 99, 111, 100, 101, 100, 34, 125, 44, 34, 113, 117, 101, 114, 121, 115, 116, 114, 105, 110, 103, 34, 58, 123, 34, 108, 111, 103, 95, 98, 111, 100, 121, 34, 58, 34, 121, 101, 115, 34, 125, 44, 34, 115, 105, 122, 101, 34, 58, 49, 56, 51, 44, 34, 117, 114, 105, 34, 58, 34, 47, 97, 110, 121, 116, 104, 105, 110, 103, 63, 108, 111, 103, 95, 98, 111, 100, 121, 61, 121, 101, 115, 34, 44, 34, 117, 114, 108, 34, 58, 34, 104, 116, 116, 112, 58, 47, 47, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 47, 97, 110, 121, 116, 104, 105, 110, 103, 63, 108, 111, 103, 95, 98, 111, 100, 121, 61, 121, 101, 115, 34, 44, 34, 109, 101, 116, 104, 111, 100, 34, 58, 34, 80, 79, 83, 84, 34, 125, 44, 34, 114, 111, 117, 116, 101, 95, 105, 100, 34, 58, 34, 114, 111, 99, 107, 101, 116, 109, 113, 45, 108, 111, 103, 103, 101, 114, 45, 114, 111, 117, 116, 101, 34, 44, 34, 97, 112, 105, 115, 105, 120, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 53, 46, 48, 48, 48, 49, 49, 54, 51, 52, 56, 50, 54, 54, 54, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 51, 57, 56, 44, 34, 108, 97, 116, 101, 110, 99, 121, 34, 58, 52, 48, 51, 46, 48, 48, 48, 49, 49, 54, 51, 52, 56, 50, 55, 44, 34, 114, 101, 115, 112, 111, 110, 115, 101, 34, 58, 123, 34, 115, 105, 122, 101, 34, 58, 51, 51, 50, 44, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 99, 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 34, 58, 34, 49, 54, 50, 34, 44, 34, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 34, 58, 34, 99, 108, 111, 115, 101, 34, 44, 34, 100, 97, 116, 101, 34, 58, 34, 87, 101, 100, 44, 32, 50, 55, 32, 68, 101, 99, 32, 50, 48, 50, 51, 32, 48, 55, 58, 52, 50, 58, 50, 50, 32, 71, 77, 84, 34, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 34, 65, 80, 73, 83, 73, 88, 47, 51, 46, 55, 46, 48, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 34, 58, 34, 116, 101, 120, 116, 47, 104, 116, 109, 108, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 34, 125, 44, 34, 115, 116, 97, 116, 117, 115, 34, 58, 52, 48, 52, 125, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 123, 34, 104, 111, 115, 116, 110, 97, 109, 101, 34, 58, 34, 117, 98, 117, 110, 116, 117, 45, 108, 105, 110, 117, 120, 45, 50, 50, 45, 48, 52, 45, 48, 50, 45, 100, 101, 115, 107, 116, 111, 112, 34, 44, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 34, 51, 46, 55, 46, 48, 34, 125, 44, 34, 115, 101, 114, 118, 105, 99, 101, 95, 105, 100, 34, 58, 34, 34, 125], transactionId='null'}]] You can use JavaScript to translate the body: const data = [123, 34, 99, 108, 105, 101, 110, 116, 95, 105, 112, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 34, 58, 34, 49, 57, 56, 46, 49, 56, 46, 49, 46, 57, 50, 58, 56, 48, 34, 44, 34, 115, 116, 97, 114, 116, 95, 116, 105, 109, 101, 34, 58, 49, 55, 48, 51, 54, 54, 50, 57, 52, 50, 52, 55, 53, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 98, 111, 100, 121, 34, 58, 34, 123, 92, 34, 101, 110, 118, 92, 34, 58, 32, 92, 34, 100, 101, 118, 92, 34, 125, 34, 44, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 104, 111, 115, 116, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 34, 44, 34, 117, 115, 101, 114, 45, 97, 103, 101, 110, 116, 34, 58, 34, 99, 117, 114, 108, 47, 55, 46, 56, 49, 46, 48, 34, 44, 34, 97, 99, 99, 101, 112, 116, 34, 58, 34, 42, 47, 42, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 34, 58, 34, 49, 52, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 34, 58, 34, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120, 45, 119, 119, 119, 45, 102, 111, 114, 109, 45, 117, 114, 108, 101, 110, 99, 111, 100, 101, 100, 34, 125, 44, 34, 113, 117, 101, 114, 121, 115, 116, 114, 105, 110, 103, 34, 58, 123, 34, 108, 111, 103, 95, 98, 111, 100, 121, 34, 58, 34, 121, 101, 115, 34, 125, 44, 34, 115, 105, 122, 101, 34, 58, 49, 56, 51, 44, 34, 117, 114, 105, 34, 58, 34, 47, 97, 110, 121, 116, 104, 105, 110, 103, 63, 108, 111, 103, 95, 98, 111, 100, 121, 61, 121, 101, 115, 34, 44, 34, 117, 114, 108, 34, 58, 34, 104, 116, 116, 112, 58, 47, 47, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 57, 48, 56, 48, 47, 97, 110, 121, 116, 104, 105, 110, 103, 63, 108, 111, 103, 95, 98, 111, 100, 121, 61, 121, 101, 115, 34, 44, 34, 109, 101, 116, 104, 111, 100, 34, 58, 34, 80, 79, 83, 84, 34, 125, 44, 34, 114, 111, 117, 116, 101, 95, 105, 100, 34, 58, 34, 114, 111, 99, 107, 101, 116, 109, 113, 45, 108, 111, 103, 103, 101, 114, 45, 114, 111, 117, 116, 101, 34, 44, 34, 97, 112, 105, 115, 105, 120, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 53, 46, 48, 48, 48, 49, 49, 54, 51, 52, 56, 50, 54, 54, 54, 44, 34, 117, 112, 115, 116, 114, 101, 97, 109, 95, 108, 97, 116, 101, 110, 99, 121, 34, 58, 51, 57, 56, 44, 34, 108, 97, 116, 101, 110, 99, 121, 34, 58, 52, 48, 51, 46, 48, 48, 48, 49, 49, 54, 51, 52, 56, 50, 55, 44, 34, 114, 101, 115, 112, 111, 110, 115, 101, 34, 58, 123, 34, 115, 105, 122, 101, 34, 58, 51, 51, 50, 44, 34, 104, 101, 97, 100, 101, 114, 115, 34, 58, 123, 34, 99, 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 34, 58, 34, 49, 54, 50, 34, 44, 34, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 34, 58, 34, 99, 108, 111, 115, 101, 34, 44, 34, 100, 97, 116, 101, 34, 58, 34, 87, 101, 100, 44, 32, 50, 55, 32, 68, 101, 99, 32, 50, 48, 50, 51, 32, 48, 55, 58, 52, 50, 58, 50, 50, 32, 71, 77, 84, 34, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 34, 65, 80, 73, 83, 73, 88, 47, 51, 46, 55, 46, 48, 34, 44, 34, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 34, 58, 34, 116, 101, 120, 116, 47, 104, 116, 109, 108, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 34, 125, 44, 34, 115, 116, 97, 116, 117, 115, 34, 58, 52, 48, 52, 125, 44, 34, 115, 101, 114, 118, 101, 114, 34, 58, 123, 34, 104, 111, 115, 116, 110, 97, 109, 101, 34, 58, 34, 117, 98, 117, 110, 116, 117, 45, 108, 105, 110, 117, 120, 45, 50, 50, 45, 48, 52, 45, 48, 50, 45, 100, 101, 115, 107, 116, 111, 112, 34, 44, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 34, 51, 46, 55, 46, 48, 34, 125, 44, 34, 115, 101, 114, 118, 105, 99, 101, 95, 105, 100, 34, 58, 34, 34, 125]const charArray = data.map(code => String.fromCharCode(code))const resultString = charArray.join('')console.log(resultString) The result will be: { ..., "method": "POST", "body": "{\"env\": \"dev\"}", "size": 183 }} Send a request to the route without any URL query string: curl -i "http://127.0.0.1:9080/anything" -X POST -d '{"env": "dev"}' You should not observe the request body in the log. info If you have customized the `log_format` in addition to setting `include_req_body` or `include_resp_body` to `true`, the plugin would not include the bodies in the logs. As a workaround, you may be able to use the NGINX variable `$request_body` in the log format, such as: { "rocketmq-logger": { ..., "log_format": {"body": "$request_body"} }} * [Examples](https://docs.api7.ai/hub/rocketmq-logger/#examples) * [Log in Different Meta Log Formats](https://docs.api7.ai/hub/rocketmq-logger/#log-in-different-meta-log-formats) * [Log Request and Response Headers With Plugin Metadata](https://docs.api7.ai/hub/rocketmq-logger/#log-request-and-response-headers-with-plugin-metadata) * [Log Request Bodies Conditionally](https://docs.api7.ai/hub/rocketmq-logger/#log-request-bodies-conditionally) ---