【问题标题】:Strategies for dealing with URIs when building an application that sits behind a reverse proxy构建位于反向代理后面的应用程序时处理 URI 的策略
【发布时间】:2010-12-27 17:54:38
【问题描述】:

我正在构建一个带有独立 HTTP 服务器的应用程序,该服务器可以直接访问,也可以放在反向代理后面(如 Apache mod_proxy)。

所以,假设我的应用程序在端口 8080 上运行,而您这样设置 Apache:

ProxyPass /myapp http://localhost:8080
ProxyPassReverse /myapp http://localhost:8080

这将导致进入主 Apache 服务器并转到 /myapp/* 的 HTTP 请求被代理到我的应用程序。如果像GET /myapp/bar 这样的请求进来,我的应用程序将看到GET /bar。这是应该的。

出现的问题是生成的 URI 必须从我的应用程序的 URI 空间转换才能通过代理正常工作(即预先添加 /myapp/)。

ProxyPassReverse 指令负责处理 HTTP 标头中的 URI(重定向等)。但这不处理我的应用程序生成的 HTML 或静态文件和模板中的 URI。

我知道像 mod_proxy_html 这样的过滤器,但这是一个非标准的 Apache 模块,无论如何,这些过滤器可能不适用于其他能够充当反向的前端 Web 服务器代理。

所以我想出了一些可能的策略:

  1. 要求在某处设置包含代理路径的环境变量,并将其添加到所有生成的 URI。这似乎不优雅;它破坏了反向代理提供的封装。

  2. 将代理路径放入我的应用程序的配置文件中。反对同上。

  3. 在我的应用程序中仅使用相对 URI。这可能会有些棘手。我必须计算当前资源与链接所在位置之间的路径差异,并添加适当数量的../'es。看起来很乱另一个问题是有些东西必须生成绝对 URI,例如 RSS 提要和生成的电子邮件。

  4. 在前端使用一些 hacky Javascript 来混合文档文本中的 URI。从互操作性的角度来看,这似乎是一个非常可怕的想法。

  5. 在我的代码中使用单一的 URI 生成函数,并要求 Javascript、CSS 等“静态”文件在我的模板系统中运行。这是我现在倾向于的想法。

这一定是一个相当普遍的问题。你过去是如何处理它的?什么有效,什么让事情变得更加困难?

【问题讨论】:

    标签: apache http proxy


    【解决方案1】:

    是的,常见问题。如何解决这个问题取决于您拥有的应用程序类型以及您正在使用的服务器平台和 Web 框架。但是我有一个通用的方法来解决这些问题,到目前为止效果很好。

    我的偏好是在应用程序代码中处理这样的问题,而不是依赖像 mod_proxy_html 这样的 web 服务器模块来处理,因为通常有太多的特殊情况(例如,客户端-javascript 即时组装 URL)服务器模块没有捕获。也就是说,我在少数情况下采用了服务器模块方法,但我决定自己修改模块代码以处理极端情况。还要牢记性能;在生成代码时修复代码中的 URL 通常比将整个 HTML 推送到另一个服务器模块要快。

    以下是我对如何在代码中处理此问题的建议:

    首先,您需要弄清楚要生成什么样的 URL。我的偏好是相对 URL。您在上面说“添加适当数量的../'es”是正确的,但至少这是您(程序员)的混乱。如果您使用配置文件/环境变量方法,那么您将依赖于部署您的应用程序的人(例如,薪水过低且脾气暴躁的 IT 运营工程师)来始终正确设置。即使您自己进行部署,它也会使您的代码发布变得复杂,因为您不能简单地将开发文件复制到生产中,而是需要添加每个部署环境的自定义步骤。我过去发现消除潜在的部署问题值得大量先发制人的编码。

    接下来,您需要将这些 URL 放入您的代码中。您执行此操作的方式因内容/代码的类型而异:

    对于服务器端代码(例如 PHP、RoR 等),您需要确保服务器端 URL 生成发生在代码中尽可能少的位置(理想情况下,一种方法!)。如果您使用任何主流 MVC Web 框架(例如 RoR、Django 等),这应该是微不足道的,因为使用 MVC 框架生成 URL 通常已经通过您可以覆盖的单个代码路径。如果您没有使用这些框架之一,那么您的代码中可能会出现 URL 生成问题。但是您要采用的方法是通过代码生成所有 URL,然后覆盖该方法以支持将非相对 URL 转换为相对 URL。您通常可以在代码中搜索模式(例如"/'/"http://'http://)并进行手动搜索和替换(或者如果您真的很书呆子并且比我更有耐心,制作一个正则表达式来替换源代码中的每个常见情况)。

    使这项工作可靠地工作的关键是,与其在服务器端代码中手动将所有绝对 URL 替换为相对 URL(即使您将每个 URL 都正确,但如果文件被移动,这些绝对 URL 也很脆弱),您可以将绝对 URL 保留在适当的位置,只需调用您的“relativizer”方法即可将它们包装起来。这更加可靠和不易碎。

    对于 Javascript,我通常喜欢做与服务器代码相同的事情——将所有 URL 生成转移到一个方法中,并确保任何 URL 生成都调用此方法。这对于具有大量预先存在的 javascript 的应用程序来说可能很难,但上面的搜索和替换方法似乎在 JS 中也能很好地工作。

    对于 CSS,CSS 中的 URL 是相对于 CSS 文件(而不是调用 HTML 页面)的位置,因此使用相对 URL 通常很容易。只需将您的 CSS 放入一个文件夹中,然后将图像放入其下方更深的文件夹中,或者将图像放入您的 CSS 的并行文件夹中,然后使用单个 ../ 相对地获取图像。一般来说,这是一个很好的最佳实践——如果你还没有在 CSS 中使用相对 URL,你应该考虑这样做,不管反向代理如何。

    最后,您需要弄清楚如何处理其他奇怪的静态文件(例如旧的静态 HTML 文件有时会潜入)。一般来说,我推荐与 CSS 和图像相同的做法——理想情况下,您会将静态文件放入可预测的目录并依赖相对 URL。或者(取决于您的服务器平台)可能更容易重新映射这些静态文件的文件扩展名,以便它们由您的 Web 框架处理 - 然后为所有 URL 运行服务器端 URL 生成器。或者,除此之外,您可以保留文件并手动将 URL 修复为相对的 - 知道这很脆弱。

    回过头来,有时生成 URL 的地方太多了,使用像 mod_proxy_html 这样的服务器模块会更有效。但我认为这是最后的手段——尤其是如果您不愿意在需要时编辑源代码。

    顺便说一句,我意识到我没有提到您上面的想法 #4 (javascript-link-fixup)。我不会那样做——如果用户关闭了 javascript 或者(更常见的)一些网络问题在页面的其余部分加载后的一段时间内阻止了该 javascript,那么您的链接将无法正常工作。太冒险了。

    【讨论】:

    • 感谢您的详细回答。我已经计划对所有服务器端 URI 构建使用单个 uri 方法,所以我最终可能会采用这个想法。我想到的一个潜在问题是,有时需要生成绝对 URI(包括主机),例如在 RSS 提要或自动电子邮件中。
    • 嗯,如果您需要绝对 URL,那么我最喜欢的方法可能行不通。您是否查看过从反向代理传递到您的应用程序的 HTTP 标头?一些(虽然,AFAIK,不是全部)反向代理可能会将标头传递给代理 ee,让他们知道原始 URL——以帮助应用程序作者处理此类情况。
    • 另外,如果 HTTP 标头不起作用并且相同的 Web 服务器同时处理 RSS/电子邮件和常规流量,您可以将 javascript 添加到每个发送请求的非 RSS/电子邮件页面中使用 URL(从客户端的角度)作为查询字符串参数返回服务器。然后,该请求的服务器端处理程序可以解析出正确的主机和路径,缓存它,您的 RSS/电子邮件处理程序可以使用它来构建绝对 URL。如果担心恶意客户端,请等到您听到来自 N 个不同客户端的相同主机/路径。
    • ... 但是,在做像我上面古怪的 javascript-logger 想法这样复杂的事情之前,我会开始考虑部署时配置选项。 :-)
    • 我确实调查了标题问题。尽管mod_proxy 会向您发送X-Forwarded-For 以获取真正的远程地址,但不幸的是它不会发送真正的URI(如果发送了,我会使用它而不是问问题。:))。电子邮件和 RSS 提要是此应用程序中的可选功能,因此我不介意需要额外的配置信息。重要的一点(对我来说)是立即启动并运行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    • 2023-03-13
    • 2023-03-08
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    相关资源
    最近更新 更多