带有 HTTPS 重定向的 Nginx 入口控制器尾部斜杠

Posted

技术标签:

【中文标题】带有 HTTPS 重定向的 Nginx 入口控制器尾部斜杠【英文标题】:Nginx Ingress Controller trailling slash with HTTPS redirect 【发布时间】:2021-07-28 07:10:14 【问题描述】:

带有 HTTPS 重定向的 nginx Ingress Controller 尾部斜杠

我正在尝试使用带有 Nginx 入口控制器的入口将请求从 HTTP 重定向到 HTTPS。我的应用是用 Django v3.0.7 编写的,我的 Nginx 控制器是 v0.46.0 和 k8s v1.19.8。

我有以下入口:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: INGRESS-NAME
  namespace: INGRESS-NS
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /api/v1/$1/
    cert-manager.io/cluster-issuer: "ISSUER-NAME"
    nginx.ingress.kubernetes.io/permanent-redirect-code: '308'
spec:
  tls:
  ...
  rules:
  - host: MY-DOMAIN
    http:
      paths:
      - path: /api/v1/?(.*)
        pathType: Prefix
        backend:
          service:
            name: SVC-NAME
            port:
              number: SVC-PORT

https://.../api/v1/get-token/ 的请求,引发此错误:

[05/May/2021:20:39:49 +0000] "POST /api/v1/get-token// HTTP/1.1" 404 => POST 在最后得到一个额外的/。但是使用 HTTP 或 https://.../api/v1/get-token(没有尾随 /)的相同请求很好。

如果我删除

annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /api/v1/$1/

重定向会删除尾随 / 并导致 POST 在所有 HTTP POST 请求中变成 GET,从而导致 403 - 方法不允许,如 Nginx 日志中所示:

[05/May/2021:20:54:52 +0000] "POST /api/v1/get-token HTTP/1.1" 308 164
[05/May/2021:20:54:53 +0000] "POST /api/v1/get-token HTTP/1.1" 301 0 
[05/May/2021:20:54:53 +0000] "GET /api/v1/get-token/ HTTP/1.1" 405

但 HTTP POST 请求与 http://.../api/v1/get-token//(两个尾随 /)正常工作。

有没有办法解决这个问题? 308 HTTP -> HTTPS 重定向很重要,所以我不能删除它,但是有没有办法强制请求有一个,并且只有一个,尾随 /?谢谢。

【问题讨论】:

您是否尝试过从nginx.ingress.kubernetes.io/rewrite-target: /api/v1/$1/ 中仅删除一个尾随/ 是的,但发生的是第二种情况,只有http://.../api/v1/get-token// 有效。 【参考方案1】:

这里有两个问题

问题 #1

annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /api/v1/$1/

导致发送到 https://.../api/v1/get-token/ 的请求以 HTTP 404 Not Found 结束,但 https://.../api/v1/get-token 可以正常工作。

为什么? 因为nginx.ingress.kubernetes.io/rewrite-target: /api/v1/$1/ rewrite 末尾的/ 被添加到URL,而/api/v1/get-token// 导致资源不存在。

该怎么办?path 键更改为/api/v1/?(.*\b)/。我不是 100% 确定它会起作用,但值得一试。 从重写中删除尾随 /。 现在,这样做会导致问题 #2。


问题 #2

https://.../api/v1/get-token 的请求以405 Method Not Allowed结束。

为什么? 第一次重定向工作正常(HTTP 308),但是请求再次使用 HTTP 301 重定向。

关于 HTTP 301 的 MDN 文章指出:

即使规范要求在执行重定向时不要更改方法(和主体),但并非所有用户代理都与它保持一致 - 您仍然可以在那里找到这种类型的错误软件。因此,建议仅将 301 代码用作 GET 或 HEAD 方法的响应,而将 308 永久重定向用于 POST 方法,因为此状态明确禁止更改方法。

基本上HTTP 301 会导致POST 变为GET,而GET 是不允许的,因此HTTP 405

该怎么办? 确保不要将请求重定向两次,尤其是使用 HTTP 301

【讨论】:

我会尝试第一个想法并告诉你,但关于第二个:我怎么能不重定向两次?似乎这是 Nginx Ingress Controller 的默认行为。事实上,我真的不想重定向两次,因为它使 POST 转向 GET,如上面的文章所述。出于某种原因,Nginx 使用 308 重定向,然后再次使用 301。 如果我不从重写中删除/ ,第一个选项(路径键/api/v1/?(.*\b)/)属于第二个问题。如果删除,则不再有效。我现在使用的是没有重写,路径/api/v1/?(.*) 和这个配置:nginx.ingress.kubernetes.io/configuration-snippet: | if ($request_uri ~ "^[^?]*?//") rewrite "^" $scheme://$host$uri permanent; 它只允许带有/ 的https,这并不理想,因为我想要所有的可能性,但我现在可以使用它。 嗨@ToledoNeto,你的问题解决了吗? 不,到目前为止,除了我上一条评论中的“解决方案”之外,没有什么新鲜事。它有效,但如果有更好的东西会很好。

以上是关于带有 HTTPS 重定向的 Nginx 入口控制器尾部斜杠的主要内容,如果未能解决你的问题,请参考以下文章

nginx入口子路径重定向

带有 nginx 的 Gatsby - 重定向被破坏 - 尾随 /(斜杠)

NGINX 将 http 重定向到 https,将非 www 重定向到 ww

使用 nginx 的 Keycloak 重定向 url 将转到 http 而不是 https

Nginx 重定向 伪静态 rewrite index.php

如何做nginx的重定向