diff --git a/plugins/wasm-go/extensions/cache-control/READMD.md b/plugins/wasm-go/extensions/cache-control/READMD.md
new file mode 100644
index 000000000..ea386bac4
--- /dev/null
+++ b/plugins/wasm-go/extensions/cache-control/READMD.md
@@ -0,0 +1,28 @@
+# 功能说明
+`cache-control`插件实现了基于 URL 文件后缀来为请求的响应头部添加 `Expires` 和 `Cache-Control` 头部,从而方便浏览器对特定后缀的文件进行缓存,例如 `jpg`、`png` 等图片文件。
+
+# 配置字段
+
+| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
+|---------|--------|-----------------------------------------------------------------------------------------------------|-|--------------------------|
+| suffix | string | 选填,表示匹配的文件后缀名,例如 `jpg`、`png` 等。
如果需要匹配多种后缀,需要用 `\|` 进行分割,例如 `png\|jpg`。
如果不填写,表示匹配所有后缀 | - | 配置用于匹配的请求文件后缀 |
+| expires | string | 必填,表示缓存的最长时间。
当填入的字符串为数字时,单位为秒,例如需要缓存1小时,需填写 3600。
另外,还可以填写 epoch 或 max
,与 nginx 中语义相同。 | - | 配置缓存的最大时间 |
+
+# 配置示例
+1. 缓存后缀为 `jpg`, `png`, `jpeg` 的文件,缓存时间为一小时
+```yaml
+suffix: jpg|png|jpeg
+expires: 3600
+```
+
+根据该配置,下列请求在访问时,将会在响应头中添加 `Expires` 和 `Cache-Control` 字段,且过期时间为 1 小时后。
+
+```bash
+curl http://example.com/test.png
+curl http://exmaple.com/test.jpg
+```
+2. 缓存所有文件,且缓存至最大时间 `“Thu, 31 Dec 2037 23:55:55 GMT”`
+```yaml
+expires: max
+```
+
diff --git a/plugins/wasm-go/extensions/cache-control/VERSION b/plugins/wasm-go/extensions/cache-control/VERSION
new file mode 100644
index 000000000..afaf360d3
--- /dev/null
+++ b/plugins/wasm-go/extensions/cache-control/VERSION
@@ -0,0 +1 @@
+1.0.0
\ No newline at end of file
diff --git a/plugins/wasm-go/extensions/cache-control/go.mod b/plugins/wasm-go/extensions/cache-control/go.mod
new file mode 100644
index 000000000..9f37a0444
--- /dev/null
+++ b/plugins/wasm-go/extensions/cache-control/go.mod
@@ -0,0 +1,20 @@
+module github.com/alibaba/higress/plugins/wasm-go/extensions/cache-control
+
+go 1.18
+
+replace github.com/alibaba/higress/plugins/wasm-go => ../..
+
+require (
+ github.com/alibaba/higress/plugins/wasm-go v0.0.0
+ github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a
+ github.com/tidwall/gjson v1.14.3
+)
+
+require (
+ github.com/google/uuid v1.3.0 // indirect
+ github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 // indirect
+ github.com/magefile/mage v1.14.0 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.0 // indirect
+ github.com/tidwall/resp v0.1.1 // indirect
+)
diff --git a/plugins/wasm-go/extensions/cache-control/main.go b/plugins/wasm-go/extensions/cache-control/main.go
new file mode 100644
index 000000000..e5b7d16cf
--- /dev/null
+++ b/plugins/wasm-go/extensions/cache-control/main.go
@@ -0,0 +1,86 @@
+package main
+
+import (
+ "github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
+ "github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
+ "github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
+ "github.com/tidwall/gjson"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+)
+
+func main() {
+ wrapper.SetCtx(
+ "cache-control",
+ wrapper.ParseConfigBy(parseConfig),
+ wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
+ wrapper.ProcessResponseHeadersBy(onHttpResponseHeaders),
+ )
+}
+
+type CacheControlConfig struct {
+ suffix []string
+ expires string
+}
+
+func parseConfig(json gjson.Result, config *CacheControlConfig, log wrapper.Log) error {
+ suffix := json.Get("suffix").String()
+ if suffix != "" {
+ parts := strings.Split(suffix, "|")
+ config.suffix = parts
+ }
+
+ config.expires = json.Get("expires").String()
+
+ log.Infof("suffix: %q, expires: %s", config.suffix, config.expires)
+ return nil
+}
+
+func onHttpRequestHeaders(ctx wrapper.HttpContext, config CacheControlConfig, log wrapper.Log) types.Action {
+ path := ctx.Path()
+ if strings.Contains(path, "?") {
+ path = strings.Split(path, "?")[0]
+ }
+ ctx.SetContext("path", path)
+ log.Debugf("path: %s", path)
+
+ return types.ActionContinue
+}
+
+func onHttpResponseHeaders(ctx wrapper.HttpContext, config CacheControlConfig, log wrapper.Log) types.Action {
+ hit := false
+ if len(config.suffix) == 0 {
+ hit = true
+ } else {
+ path, ok := ctx.GetContext("path").(string)
+ if !ok {
+ log.Error("failed to get request path")
+ return types.ActionContinue
+ }
+
+ for _, part := range config.suffix {
+ if strings.HasSuffix(path, "."+part) {
+ hit = true
+ break
+ }
+ }
+ }
+ if hit {
+ if config.expires == "max" {
+ proxywasm.AddHttpResponseHeader("Expires", "Thu, 31 Dec 2037 23:55:55 GMT")
+ proxywasm.AddHttpResponseHeader("Cache-Control", "maxAge=315360000")
+ } else if config.expires == "epoch" {
+ proxywasm.AddHttpResponseHeader("Expires", "Thu, 01 Jan 1970 00:00:01 GMT")
+ proxywasm.AddHttpResponseHeader("Cache-Control", "no-cache")
+ } else {
+ maxAge, _ := strconv.ParseInt(config.expires, 10, 64)
+ currentTime := time.Now()
+ expireTime := currentTime.Add(time.Duration(maxAge) * time.Second)
+ proxywasm.AddHttpResponseHeader("Expires", expireTime.UTC().Format(http.TimeFormat))
+ proxywasm.AddHttpResponseHeader("Cache-Control", "maxAge="+strconv.FormatInt(maxAge, 10))
+ }
+ }
+ return types.ActionContinue
+}
diff --git a/test/e2e/conformance/tests/go-wasm-cache-control.go b/test/e2e/conformance/tests/go-wasm-cache-control.go
new file mode 100644
index 000000000..a36b2c47a
--- /dev/null
+++ b/test/e2e/conformance/tests/go-wasm-cache-control.go
@@ -0,0 +1,63 @@
+// Copyright (c) 2022 Alibaba Group Holding Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tests
+
+import (
+ "github.com/alibaba/higress/test/e2e/conformance/utils/http"
+ "github.com/alibaba/higress/test/e2e/conformance/utils/suite"
+ "testing"
+)
+
+func init() {
+ Register(WasmPluginCacheControl)
+}
+
+var WasmPluginCacheControl = suite.ConformanceTest{
+ ShortName: "WasmPluginCacheControl",
+ Description: "The Ingress in the higress-conformance-infra namespace test the cache control WASM Plugin",
+ Manifests: []string{"tests/go-wasm-cache-control.yaml"},
+ Features: []suite.SupportedFeature{suite.WASMGoConformanceFeature},
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ testcases := []http.Assertion{
+ {
+ Meta: http.AssertionMeta{
+ TestCaseName: "case 1: Test hit",
+ TargetBackend: "infra-backend-v1",
+ TargetNamespace: "higress-conformance-infra",
+ },
+ Request: http.AssertionRequest{
+ ActualRequest: http.Request{
+ Host: "foo.com",
+ Path: "/foo",
+ Headers: map[string]string{"User-Agent": "BaiduMobaider/1.1.0"},
+ },
+ },
+ Response: http.AssertionResponse{
+ ExpectedResponse: http.Response{
+ StatusCode: 200,
+ Headers: map[string]string{
+ "Cache-Control": "maxAge=3600",
+ },
+ },
+ },
+ },
+ }
+ t.Run("WasmPlugins cache-control", func(t *testing.T) {
+ for _, testcase := range testcases {
+ http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
+ }
+ })
+ },
+}
diff --git a/test/e2e/conformance/tests/go-wasm-cache-control.yaml b/test/e2e/conformance/tests/go-wasm-cache-control.yaml
new file mode 100644
index 000000000..4f98f4862
--- /dev/null
+++ b/test/e2e/conformance/tests/go-wasm-cache-control.yaml
@@ -0,0 +1,48 @@
+# Copyright (c) 2022 Alibaba Group Holding Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ annotations:
+ name: wasmplugin-cache-control
+ namespace: higress-conformance-infra
+spec:
+ ingressClassName: higress
+ rules:
+ - host: "foo.com"
+ http:
+ paths:
+ - pathType: Prefix
+ path: "/foo"
+ backend:
+ service:
+ name: infra-backend-v1
+ port:
+ number: 8080
+---
+apiVersion: extensions.higress.io/v1alpha1
+kind: WasmPlugin
+metadata:
+ name: cache-control
+ namespace: higress-system
+spec:
+ defaultConfigDisable: true
+ matchRules:
+ - config:
+ expires: 3600
+ configDisable: false
+ ingress:
+ - higress-conformance-infra/wasmplugin-cache-control
+ url: file:///opt/plugins/wasm-go/extensions/cache-control/plugin.wasm
\ No newline at end of file