You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kubernetes Ingress Controller中正确配置Rewrite Target

NGINX Ingress重写规则不生效问题排查

环境与需求

我们有一个Blazor应用,对应两个域名:

  • domain.com:面向普通用户的前端,已配置完成,domain.com/auth提供用户UI,domain.com/auth/admin直接返回404
  • admin.domain.com:管理面板前端,期望实现:访问https://admin.domain.com/auth/userlist时,后端实际收到的请求路径为/auth/admin/userlist,但当前配置的重写规则未生效

现有完整Ingress配置

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Values.label.name }}-service-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    nginx.ingress.kubernetes.io/client-max-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      location ~* "^/auth/admin(/|$)(.*)" {
        return 404;
      }
spec:
  tls:
  - hosts:
    - domain.com
    secretName: wildcard-domain-com-ingress
  rules:
  - host: domain.com
    http:
      paths:
        - path: /auth(/|$)(.*)
          pathType: Prefix
          backend:
            service:
              name: {{ .Values.label.name }}-service
              port:
                number: {{ .Values.service.port }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Values.label.name }}-service-admin-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    nginx.ingress.kubernetes.io/client-max-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /auth/admin/$1
spec:
  tls:
    - hosts:
        - admin.domain.com
      secretName: wildcard-domain-com-ingress
  rules:
    - host: admin.domain.com
      http:
        paths:
          - path: /auth/(.*)
            pathType: Prefix
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Values.label.name }}-service-admin-styles-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    nginx.ingress.kubernetes.io/client-max-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
  tls:
    - hosts:
        - admin.domain.com
      secretName: wildcard-domain-com-ingress
  rules:
    - host: admin.domain.com
      http:
        paths:
          - path: /auth/icons(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}
          - path: /auth/img(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}
          - path: /auth/js(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}
          - path: /auth/styles(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}
          - path: /auth/favicon.ico
            pathType: Prefix
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}
          - path: /auth/_framework(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}
          - path: /auth/_blazor(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}

问题根源

  1. 多Ingress优先级冲突:第三个负责静态资源的Ingress里,路径规则(比如/auth/icons(/|$)(.*))比第二个Ingress的/auth/(.*)更具体,NGINX Ingress的匹配逻辑是最长前缀优先,所以所有静态资源请求、甚至部分业务请求都会先命中第三个Ingress的规则,跳过了第二个Ingress的重写配置,导致重写根本没触发。

  2. 静态资源未重写:就算静态资源请求命中了第三个Ingress,当前配置也不会重写路径,后端收到的是/auth/icons/logo.png,但管理面板的静态资源实际应该在/auth/admin/icons/logo.png,这还会导致页面静态资源加载失败。

  3. PathType与正则不兼容:使用正则匹配时,pathType: Prefix不是最佳选择,NGINX Ingress推荐用ImplementationSpecific来配合正则规则,否则可能出现匹配不符合预期的情况。

修复方案

把管理面板的业务路径和静态资源路径合并到同一个Ingress中,统一应用重写规则,调整匹配顺序:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Values.label.name }}-service-admin-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    nginx.ingress.kubernetes.io/client-max-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/use-regex: "true"
    # 统一重写路径,$2对应捕获的具体资源/业务路径
    nginx.ingress.kubernetes.io/rewrite-target: /auth/admin/$2
spec:
  tls:
    - hosts:
        - admin.domain.com
      secretName: wildcard-domain-com-ingress
  rules:
    - host: admin.domain.com
      http:
        paths:
          # 先匹配所有静态资源的具体路径,正则分组捕获目录和后续路径
          - path: /auth/(icons|img|js|styles|_framework|_blazor)(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}
          # 单独匹配favicon.ico
          - path: /auth/favicon.ico
            pathType: ImplementationSpecific
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}
          # 最后匹配其他所有/auth下的业务路径
          - path: /auth(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: {{ .Values.label.name }}-service
                port:
                  number: {{ .Values.service.port }}

调整说明

  • 合并管理面板的所有Ingress规则,避免多Ingress的优先级冲突,确保重写规则能覆盖所有admin.domain.com下的/auth路径。
  • 修改重写规则的分组引用:因为静态资源路径的正则有多个分组,$2才是我们需要传递给后端的实际路径部分,重写后静态资源路径会变成/auth/admin/icons/logo.png,业务路径变成/auth/admin/userlist。
  • 路径按具体到通用的顺序排列,确保静态资源优先匹配,同时统一应用重写。
  • 将pathType改为ImplementationSpecific,适配正则匹配的场景,避免Prefix类型带来的匹配异常。

原普通用户的Ingress配置可以保留,它只作用于domain.com,不会影响admin.domain.com的请求。

内容的提问来源于stack exchange,提问作者Marcin Gadomski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 19:31:07