title: cors keywords:
import Tabs from ‘@theme/Tabs’; import TabItem from ‘@theme/TabItem’;
The cors Plugin allows you to enable Cross-Origin Resource Sharing (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.
| Name | Type | Required | Default | Valid values | Description |
|---|---|---|---|---|---|
| allow_origins | string | False | "*" | Comma-separated string of origins to allow CORS, in the format scheme://host:port, for example https://somedomain.com:8081. If you have multiple origins, use , to list them. If allow_credential is set to false, you can use * to allow all origins. If allow_credential is set to true, you can use ** to forcefully allow all origins, but this poses security risks. | |
| allow_methods | string | False | "*" | Comma-separated string of HTTP request methods to allow CORS, for example GET, POST. If allow_credential is set to false, you can use * to allow all methods. If allow_credential is set to true, you can use ** to forcefully allow all methods, but this poses security risks. | |
| allow_headers | string | False | "*" | Comma-separated string of HTTP headers allowed in requests when accessing a cross-origin resource. If allow_credential is set to false, you can use * to allow all request headers. If allow_credential is set to true, you can use ** to forcefully allow all request headers, but this poses security risks. | |
| expose_headers | string | False | Comma-separated string of HTTP headers that should be made available in response to a cross-origin request. If allow_credential is set to false, you can use * to allow all response headers. If not specified, the Plugin will not modify the Access-Control-Expose-Headers header. See Access-Control-Expose-Headers - MDN for more details. | ||
| max_age | integer | False | 5 | Maximum time in seconds for which the results of a preflight request can be cached. If the time is within this limit, the browser will check the cached result. Set to -1 to disable caching. Note that the maximum value is browser-dependent. See Access-Control-Max-Age for more details. | |
| allow_credential | boolean | False | false | When set to true, allows requests to include credentials such as cookies. According to CORS specification, if you set this to true, you cannot use * for other CORS attributes. | |
| allow_origins_by_regex | array | False | Regex to match origins that allow CORS. For example, [".*\.test.com$"] can match all subdomains of test.com. When configured, only domains matching the RegEx will be allowed and allow_origins will be ignored. | ||
| allow_origins_by_metadata | array | False | Origins to enable CORS referenced from allow_origins set in the Plugin metadata. For example, if "allow_origins": {"EXAMPLE": "https://example.com"} is set in the Plugin metadata, then ["EXAMPLE"] can be used to allow CORS on the origin https://example.com. | ||
| timing_allow_origins | string | False | Comma-separated string of origins to allow access to the resource timing information. See Timing-Allow-Origin for more details. | ||
| timing_allow_origins_by_regex | array | False | Regex to match origins for enabling access to the resource timing information. For example, [".*\.test.com"] can match all subdomains of test.com. When configured, only domains matching the RegEx will be allowed and timing_allow_origins will be ignored. |
:::info IMPORTANT
allow_credential attribute is sensitive and must be used carefully. If set to true, the default value * of the other attributes will be invalid and they should be specified explicitly.** you are vulnerable to security risks like CSRF. Make sure that this meets your security levels before using it.:::
| Name | Type | Required | Default | Valid values | Description |
|---|---|---|---|---|---|
| allow_origins | object | False | A map of keys and allowed origins. The keys are used in the allow_origins_by_metadata attribute and the values are equivalent to the allow_origins attribute of the Plugin. |
The examples below demonstrate how you can configure the cors Plugin for different scenarios.
:::note
You can fetch the admin_key from config.yaml and save to an environment variable with the following command:
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')
:::
The following example demonstrates how to enable CORS on a Route to allow resource loading from a list of origins.
<Tabs groupId=“api” defaultValue=“admin-api” values={[ {label: ‘Admin API’, value: ‘admin-api’}, {label: ‘ADC’, value: ‘adc’}, {label: ‘Ingress Controller’, value: ‘aic’} ]}>
Create a Route with the cors Plugin:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${admin_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" } }'
services: - name: cors-service routes: - name: cors-route uris: - /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: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1
Synchronize the configuration to APISIX:
adc sync -f adc.yaml
<Tabs groupId=“k8s-api” defaultValue=“gateway-api” values={[ {label: ‘Gateway API’, value: ‘gateway-api’}, {label: ‘APISIX Ingress Controller’, value: ‘apisix-ingress-controller’} ]}>
apiVersion: v1 kind: Service metadata: namespace: aic name: httpbin-external-domain spec: type: ExternalName externalName: httpbin.org --- apiVersion: apisix.apache.org/v1alpha1 kind: PluginConfig metadata: namespace: aic name: cors-plugin-config spec: plugins: - name: cors config: 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 --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: aic name: cors-route spec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: cors-plugin-config backendRefs: - name: httpbin-external-domain port: 80
Apply the configuration to your cluster:
kubectl apply -f cors-ic.yaml
apiVersion: apisix.apache.org/v2 kind: ApisixUpstream metadata: namespace: aic name: httpbin-external-domain spec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org --- apiVersion: apisix.apache.org/v2 kind: ApisixRoute metadata: namespace: aic name: cors-route spec: ingressClassName: apisix http: - name: cors-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: cors enable: true config: 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
Apply the configuration to your cluster:
kubectl apply -f cors-ic.yaml
Send a 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.com Access-Control-Allow-Credentials: true Server: APISIX/3.8.0 Vary: Origin Access-Control-Allow-Methods: GET,POST Access-Control-Max-Age: 50 Access-Control-Expose-Headers: ex-headr1,ex-headr2 Access-Control-Allow-Headers: headr1,headr2
Send a 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 headers:
... Server: APISIX/3.8.0 Vary: Origin
The following example demonstrates how to use a regular expression to match allowed origins using the allow_origins_by_regex attribute.
<Tabs groupId=“api” defaultValue=“admin-api” values={[ {label: ‘Admin API’, value: ‘admin-api’}, {label: ‘ADC’, value: ‘adc’}, {label: ‘Ingress Controller’, value: ‘aic’} ]}>
Create a Route with the cors Plugin:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \ -H "X-API-KEY: ${admin_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" } }'
services: - name: cors-service routes: - name: cors-route uris: - /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: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1
Synchronize the configuration to APISIX:
adc sync -f adc.yaml
<Tabs groupId=“k8s-api” defaultValue=“gateway-api” values={[ {label: ‘Gateway API’, value: ‘gateway-api’}, {label: ‘APISIX Ingress Controller’, value: ‘apisix-ingress-controller’} ]}>
apiVersion: v1 kind: Service metadata: namespace: aic name: httpbin-external-domain spec: type: ExternalName externalName: httpbin.org --- apiVersion: apisix.apache.org/v1alpha1 kind: PluginConfig metadata: namespace: aic name: cors-regex-plugin-config spec: plugins: - name: cors config: allow_methods: "GET,POST" allow_headers: "headr1,headr2" expose_headers: "ex-headr1,ex-headr2" max_age: 50 allow_origins_by_regex: - ".*\\.test.com$" --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: aic name: cors-route spec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: cors-regex-plugin-config backendRefs: - name: httpbin-external-domain port: 80
Apply the configuration to your cluster:
kubectl apply -f cors-ic.yaml
apiVersion: apisix.apache.org/v2 kind: ApisixUpstream metadata: namespace: aic name: httpbin-external-domain spec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org --- apiVersion: apisix.apache.org/v2 kind: ApisixRoute metadata: namespace: aic name: cors-route spec: ingressClassName: apisix http: - name: cors-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: cors enable: true config: allow_methods: "GET,POST" allow_headers: "headr1,headr2" expose_headers: "ex-headr1,ex-headr2" max_age: 50 allow_origins_by_regex: - ".*\\.test.com$"
Apply the configuration to your cluster:
kubectl apply -f cors-ic.yaml
Send a 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.com Server: APISIX/3.8.0 Vary: Origin Access-Control-Allow-Methods: GET,POST Access-Control-Max-Age: 50 Access-Control-Expose-Headers: ex-headr1,ex-headr2 Access-Control-Allow-Headers: headr1,headr2
Send a request with an origin that does not match the pattern:
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 headers:
... Server: APISIX/3.8.0 Vary: Origin
The following example demonstrates how to configure allowed origins in Plugin metadata and reference them in the cors Plugin using allow_origins_by_metadata.
<Tabs groupId=“api” defaultValue=“admin-api” values={[ {label: ‘Admin API’, value: ‘admin-api’}, {label: ‘ADC’, value: ‘adc’}, {label: ‘Ingress Controller’, value: ‘aic’} ]}>
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_key}" \ -d '{ "allow_origins": { "key_1": "https://domain.com", "key_2": "https://sub.domain.com,https://sub2.domain.com", "key_3": "*" } }'
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_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" } }'
plugin_metadata: cors: allow_origins: key_1: "https://domain.com" key_2: "https://sub.domain.com,https://sub2.domain.com" key_3: "*" services: - name: cors-service routes: - name: cors-route uris: - /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: type: roundrobin nodes: - host: httpbin.org port: 80 weight: 1
Synchronize the configuration to APISIX:
adc sync -f adc.yaml
Update your GatewayProxy manifest to configure the Plugin metadata:
apiVersion: apisix.apache.org/v1alpha1 kind: GatewayProxy metadata: namespace: aic name: apisix-config spec: provider: type: ControlPlane controlPlane: # your control plane connection configuration pluginMetadata: cors: allow_origins: key_1: "https://domain.com" key_2: "https://sub.domain.com,https://sub2.domain.com" key_3: "*"
<Tabs groupId=“k8s-api” defaultValue=“gateway-api” values={[ {label: ‘Gateway API’, value: ‘gateway-api’}, {label: ‘APISIX Ingress Controller’, value: ‘apisix-ingress-controller’} ]}>
Create the Route with allow_origins_by_metadata:
apiVersion: v1 kind: Service metadata: namespace: aic name: httpbin-external-domain spec: type: ExternalName externalName: httpbin.org --- apiVersion: apisix.apache.org/v1alpha1 kind: PluginConfig metadata: namespace: aic name: cors-metadata-plugin-config spec: plugins: - name: cors config: allow_methods: "GET,POST" allow_headers: "headr1,headr2" expose_headers: "ex-headr1,ex-headr2" max_age: 50 allow_origins_by_metadata: - "key_1" --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: namespace: aic name: cors-route spec: parentRefs: - name: apisix rules: - matches: - path: type: Exact value: /anything filters: - type: ExtensionRef extensionRef: group: apisix.apache.org kind: PluginConfig name: cors-metadata-plugin-config backendRefs: - name: httpbin-external-domain port: 80
Apply the configuration to your cluster:
kubectl apply -f gatewayproxy.yaml -f cors-ic.yaml
Create the Route with allow_origins_by_metadata:
apiVersion: apisix.apache.org/v2 kind: ApisixUpstream metadata: namespace: aic name: httpbin-external-domain spec: ingressClassName: apisix externalNodes: - type: Domain name: httpbin.org --- apiVersion: apisix.apache.org/v2 kind: ApisixRoute metadata: namespace: aic name: cors-route spec: ingressClassName: apisix http: - name: cors-route match: paths: - /anything upstreams: - name: httpbin-external-domain plugins: - name: cors enable: true config: allow_methods: "GET,POST" allow_headers: "headr1,headr2" expose_headers: "ex-headr1,ex-headr2" max_age: 50 allow_origins_by_metadata: - "key_1"
Apply the configuration to your cluster:
kubectl apply -f gatewayproxy.yaml -f cors-ic.yaml
Send a 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.com Server: APISIX/3.8.0 Vary: Origin Access-Control-Allow-Methods: GET,POST Access-Control-Max-Age: 50 Access-Control-Expose-Headers: ex-headr1,ex-headr2 Access-Control-Allow-Headers: headr1,headr2
Send a request with an origin not in the metadata:
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 headers:
... Server: APISIX/3.8.0 Vary: Origin