使用 apache 反向代理将 /blog 的所有请求发送到内部 wordpress 服务器

使用 apache 反向代理将 /blog 的所有请求发送到内部 wordpress 服务器

我有一个用 react 编写的网站,现在我想在网站上添加一个博客部分。该博客将基于 wordpress。

React 应用程序在 docker 容器中运行,我使用 wordpress docker 容器来运行 wordpress 博客。

为了访问该网站,我使用另一个运行 apache 并充当反向代理的容器。

在 apache 容器的文件中httpd.conf,我有以下部分:

<VirtualHost *:80>
    <Location "/">
        ProxyPreserveHost On
        ProxyPass "${REACT_SERVER}/"
        ProxyPassReverse "${REACT_SERVER}/"
    </Location>

    <Location /blog>
        ProxyPreserveHost On
        ProxyPass "${BLOG_SERVER}/"
        ProxyPassReverse "${BLOG_SERVER}/"
        ProxyPassReverseCookiePath  "/"  "/blog"
    </Location>

    # more config for handling websockets
</VirtualHost>

变量REACT_SERVER来自BLOG_SERVER环境。

我遇到的问题是,当我尝试访问博客时,apache 成功将我的请求重定向到内部 wordpress 站点,但是当 wordpress 执行自己的重定向时,它使用与 apache 相同的主机,但路径不以 开头/blog,因此我的 react 应用程序尝试处理该请求,但最终放弃并自行重定向到主页。

下面是一个使用的示例curl

➜ curl -v http://localhost:3005/blog/
*   Trying 127.0.0.1:3005...
* Connected to localhost (127.0.0.1) port 3005 (#0)
> GET /blog/ HTTP/1.1
> Host: localhost:3005
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Date: Fri, 20 Aug 2021 16:27:32 GMT
< Server: Apache/2.4.48 (Debian)
< X-Powered-By: PHP/7.4.22
< Expires: Wed, 11 Jan 1984 05:00:00 GMT
< Cache-Control: no-cache, must-revalidate, max-age=0
< X-Redirect-By: WordPress
< Location: http://localhost:3005/wp-admin/install.php
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
<
* Connection #0 to host localhost left intact

如您所见,在X-Redirected-By部分之后,Location以 开头,/wp-admin而不是/blog/wp-admin

来自文档ProxyPassReverse

例如,假设本地服务器有地址http://example.com/; 然后

ProxyPass         "/mirror/foo/" "http://backend.example.com/"
ProxyPassReverse  "/mirror/foo/" "http://backend.example.com/"
ProxyPassReverseCookieDomain  "backend.example.com" "public.example.com"
ProxyPassReverseCookiePath  "/"  "/mirror/foo/"

不仅会引起本地请求 http://example.com/mirror/foo/bar在内部转换为代理请求http://backend.example.com/bar(ProxyPass 在此处提供的功能)。它还负责处理服务器 backend.example.com 在重定向时发送的重定向 http://backend.example.com/barhttp://backend.example.com/quux. Apache httpd 将其调整为http://example.com/mirror/foo/quux在将 HTTP 重定向响应转发给客户端之前。请注意,用于构建 URL 的主机名是根据 UseCanonicalName 指令的设置来选择的。

看起来这就是让它发挥作用所需要的全部条件,但它仍然不起作用。

如果你想知道,是的我已经尝试过普通的(没有Location指令):

ProxyPass "/blog/" "${BLOG_SERVER}/"
ProxyPassReverse "/blog/" "${BLOG_SERVER}/"
ProxyPassReverseCookiePath  "/"  "/blog"

# etc...

我也得到了同样的结果。

我错过了什么?

答案1

这个问题看起来更像是 Wordpress 的问题,而不是配置错误。您需要告诉 wordpress 它位于子目录中,因为现在默认的 wordpress .htaccess 文件会将您重定向到 http://localhost:3005/wp-admin/install.php ,因为它不知道它位于名为的目录中博客

选项1。 尝试解决此问题的一种方法是告诉 wordpress,它在 wp-config.php 文件中有一个新的基本 URL

define('WP_HOME','http://example.com/blog');
define('WP_SITEURL','http://example.com/blog');

选项 2。 尝试处理此问题的另一种方法是编辑 wordpress 的 htaccess 文件

你的 wordpress 中的 htaccess 文件应该看起来像这样

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /blog/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /blog/index.php [L]

这将是 wordpress docker 容器内存在的 htaccess

答案2

好的,我想我已经解决问题了。

Wordpress 使用两个变量来确定:

  1. 哪个 URL 指向您的 wordpress 网站的位置(WP_HOME
  2. 哪个 URL 用于为您的 WordPress 网站加载资源(WP_SITEURL

对于 wordpress 容器,我要做的第一件事就是确保这些 URL 与内部容器 URL 匹配,即$BLOG_SERVER。因为我使用 docker-compose,所以很容易通过参数使用环境变量注入此 URL WORDPRESS_CONFIG_EXTRA

wordpress-blog:
  image: wordpress:5
  depends_on:
    - blog-db
  environment:
    WORDPRESS_DB_HOST: blog-db
    WORDPRESS_DB_NAME: blog
    WORDPRESS_DB_USER: wordpress
    WORDPRESS_DB_PASSWORD: wordpress
    WORDPRESS_CONFIG_EXTRA: |
      define('WP_HOME', 'http://wordpress-blog');
      define('WP_SITEURL', 'http://wordpress-blog');
  volumes:
    - wordpress:/var/www/html

现在已经完成,我们现在可以关注代理了。

在我采用完全反向代理之前,我假设代理会以某种方式接管每个请求/blog/,并从代理站点返回页面,这些页面看起来就像是直接从 wordpress 提供的一样。我没有考虑到的一件事是,这个假设还假设服务器端呈现的页面

从新的配置开始VirtualHost,它现在看起来像这样:

<VirtualHost *:80>
    ProxyPass "/blog/" "${BLOG_SERVER}/"
    ProxyPass "/" "${REACT_SERVER}/"

    <Location "/">
        ProxyPreserveHost On
        ProxyErrorOverride On
        ProxyPassReverse "${DEV_SERVER}/"
    </Location>

    <Location "/blog/">
        ProxyPreserveHost Off
        ProxyPassReverse "${BLOG_SERVER}/"
        ProxyPassReverseCookiePath  "/"  "/blog/"
        ProxyErrorOverride On

        ProxyHTMLEnable On
        ProxyHTMLExtended On
        ProxyHTMLURLMap "${BLOG_SERVER}/"
        SetOutputFilter INFLATE;proxy-html;DEFLATE
        # ProxyPassReverseCookieDomain "%{HTTP_HOST:${BLOG_SERVER}}" %{HTTP_HOST}
    </Location>
</VirtualHost>

要使此代理开始像代理一样运行,我必须做的下一件事是添加以下行:

ProxyPreserveHost Off

这确保了我们从 wordpress 获得的所有响应/请求看起来不像是来自我们(代理)。当我们开始处理代理 html 时,原因很快就会显而易见。


接下来,ProxyPass指令被移出容器Location,并直接移入VirtualHost

ProxyPass "/blog/" "${BLOG_SERVER}/"
ProxyPass "/" "${REACT_SERVER}/"

原因是Location块在匹配请求时非常晚,有时路径/会胜过/blog/路径。我需要它更可靠,所以我决定自行指定代理(我看到了一个例子这里),然后修改容器内的路径Location


此时,反向代理现在在职的!但是页面中的 html 有指向 wordpress 网站内部 url 的链接。这里是mod_proxy_html进来。它可用于重写 html 中的所有链接以指向反向代理。它发现任何指向内部博客站点的链接,该链接将被替换为使用反向代理的链接。

ProxyHTMLEnable On
ProxyHTMLExtended On
ProxyHTMLURLMap "${BLOG_SERVER}/"
SetOutputFilter INFLATE;proxy-html;DEFLATE

最后一行可能会造成瓶颈,因为它本质上会解压来自博客站点的有效负载,重写所有 URL 以指向反向代理,然后再次压缩它们。如果您不想这样做,另一种方法是使用:

RequestHeader    unset  Accept-Encoding

即使有了所有这些,解决方案仍然不完美,因为页面上加载的任何向内部站点发出请求的 javascript 文件都不会将其请求路由到代理。

解决此问题的一个方法是采用当前答案关于这个问题,并改为WP_SITEURL直接指向反向代理。

另一个解决方案是使用服务工作者拦截网络请求。我喜欢这个解决方案,因为它不会将博客网站与反向代理紧密耦合。我可以想象,将服务工作者注入从代理请求的任何 html 页面,并让该服务工作者拦截所有与内部博客网站 url 匹配的请求,并将其替换为反向代理 url,这个想法并不太牵强(呵呵)。

我都没有选择这两个。经过深思熟虑,我认为在子域中托管 wordpress 更符合我的需求。我可能会选择 blog.example.com 之类的域名,但那是另一天的工作了。


总之,反向代理很难用 apache 正确实现。我不知道 nginx 方面的情况是否更乐观,但也许有一天我们会检查一下。我所采用的解决方案假设服务器端内容,这已被证明是代理的完美选择,但遗憾的是,动态加载的内容将需要更多工作。

来源

已启用 html 代理的 Apache 模块

LoadModule deflate_module modules/mod_deflate.so
LoadModule xml2enc_module modules/mod_xml2enc.so
LoadModule proxy_html_module modules/mod_proxy_html.so

相关内容