如何在.htaccess中设置代理

17

Apache文档说明RewriteRule和应该放在服务器配置中,但由于共享主机的情况,它们可以放在htaccess中。我正处于这样的情况下。

我正在尝试设置透明代理:

 RewriteEngine On
 RewriteCond %{REQUEST_URI} ^/foo [OR]
 RewriteCond %{REQUEST_URI} ^/bar
 RewriteRule ^(.*)$ http://example.com/$1 [P]

这个工作得很好……除了重定向(比如如果/foo 重定向到 /bar)。重定向回到了example.com,而不是我的服务器。

我知道ProxyPassReverse指令可以解决这个问题,但是当我在.htaccess中添加时,出现了“内部服务器错误”页面。

与Rewrite指令不同,ProxyPassReverse在htaccess中不起作用

在共享主机环境中如何设置透明代理,或者这不可能吗?

(这似乎是合理的,因为Rewrite已经完成了80%的工作,在一个htaccess中使用透明代理不会影响在另一个htaccess中使用它。)


你可以尝试使用以下代码:RewriteRule ^(foo|bar)/?$ http://example.com/$1 [P,L] - anubhava
这与我所写的相同,除了 L 标志,它只表示不需要测试更多规则。 - Paul Draper
Apache文档指出,包含一个P会强制添加L - Titou
2个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
24

很不幸,我相当确信你想要做的事情是不可能的:我正在尝试做完全相同的事情!从我的研究来看,我相当有信心这是不可能的。

简单来说,你需要使用ProxyPassReverse,它只在虚拟主机级别(或类似级别)可用;而不是htaccess级别。

编辑:我唯一实现这一点的方法是还要配置响应服务器/应用程序以知道它在代理后面,并相应地提供页面。也就是说,我使用.htaccess重定向到另一个服务器,如下所示:

  RewriteEngine on
  RewriteRule  (.*)  http://localhost:8080/$1  [P,L] 

然后在应用服务器上——在这种情况下,是一个JIRA安装——我适当配置了Java Tomcat / Catalina以使用代理的信息来提供页面:

 proxyName="my.public.address.com"
 proxyPort="80"

然而,这并不完全透明;应用程序服务器需要以代理方式提供页面。虽然这可能有些用处。


1
我认为你可能是对的。但这让我感到困扰,因为没有什么好的理由说明这不能够工作。 - Paul Draper
对于你来说,这只是简单的 JIRA 配置问题。但如果要配置 任何 Web 服务器以提供代理信息的页面呢?什么是“代理信息”意思? - Ciprian Tomoiagă
在这里,“代理信息”是指Web服务器知道实际访问的地址。这意味着它可以向客户端发送数据,其中包含客户端期望访问的正确地址和/或端口。(在上面的示例中,JIRA服务器实际上正在侦听端口8080;但是,客户端通过端口80访问它。Apache重写规则处理从80-> 8080的请求,但是JIRA服务器需要知道它将收到的请求(和回复)实际上是在其他地方寻址的。)简而言之?你无法透明地做到这一点。 - Michael Sallaway
谢谢,它在共享主机环境中完美运行。 - Felipe Costa

1

我设法收集了一些资源来弄清楚如何做到这一点。我使用共享托管提供商,因此无法访问服务器配置(httpd.conf)。我只能使用 .htaccess 来完成代理。这个例子是针对一个 WordPress 网站的,我希望大部分内容由 origin.example.com 提供,但会有一些页面本地提供,就像叠加层一样。您也可以采用另一种方式,仅使用不同的 RewriteCond 规则代理特定的子目录。

需要知道的事情:

  1. 您不能在 .htaccess 中使用 ProxyPass 或 ProxyPassReverse,因此我们必须使用其他方法来模拟它们所做的事情。
  2. 如果您的提供商未启用 SSLProxyEngine,则无法通过 HTTPS 进行代理调用,因此如果您担心中间人攻击,您将失去一些安全性。如果源服务器是内部的,则可能不存在此问题。您还可以在源服务器上使用 .htaccess 强制从除代理服务器以外的任何地方都使用 HTTPS。
  3. 您需要重写标头
  4. 您需要重写来自源服务器的 HTML,并且需要在源服务器上完成。您可以将其限制为某些 IP(即代理的 IP),以便在其他地方访问时不会出现问题。

我的要求:

我想让对 proxy.example.com 的调用提供 origin.example.com 的内容。在我的情况下,我想映射除了一些例外以外的所有内容。如果您只想映射网站的一部分,请相应地调整规则。

如何操作:

  1. 在 proxy.example.com 上配置 .htaccess 文件,将所有 URI 代理到 origin.example.com 上。我希望能够登录 proxy.example.com,因此我不会重写 /wp-admin 或 /wp-login.php。在我的情况下,我有一个 /programs/ 部分,我希望代理服务器本身(也是 WordPress 实例)提供服务。通过检查 REDIRECT_STATUS 来避免循环。
# I force everything coming into proxy.example.com to be HTTPS <IfModule mod_rewrite.c>
RewriteEngine On

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] </IfModule> <IfModule mod_proxy.c>
# Redirect access for / (or any index) to the origin. NOTE target is http:// without SSLProxyEngine
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(index\.(php|html|cgi))?$ http://origin.example.com/ [P]

# Do NOT redirect these patterns
RewriteCond %{REQUEST_URI} !^/wp-admin/
RewriteCond %{REQUEST_URI} !^/wp-login.php
RewriteCond %{REQUEST_URI} !^/programs/

# Redirect everything else. NOTE target is http:// without SSLProxyEngine
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(.+)$ "http://origin.example.com/$1" [P]

# Mimic ProxyPassReverse. Fix the headers. Force to be https.
Header edit  Location ^https?://origin\.example\.com/(.*)$ https://proxy.example.com/$1
Header edit* Link https?://origin\.example\.com/ https://proxy.example.com/ </IfModule>
  1. 仅针对代理服务器的IP,重写HTML本身中的任何引用。此示例适用于WordPress网站。

WordPress过滤器以修改最终HTML输出中窃取

2a)添加一个必须使用插件来添加“final_output”钩子。在wp-content/mu-plugins/buffer.php中添加一个文件:

<?php

/**  * Output Buffering  *  * Buffers the entire WP process, capturing
the final output for manipulation.  */

ob_start();

add_action('shutdown', function() {
    $final = '';

    // We'll need to get the number of ob levels we're in, so that we can iterate over each, collecting
    // that buffer's output into the final output.
    $levels = ob_get_level();

    for ($i = 0; $i < $levels; $i++) {
        // NOTE: Use only one of the two lines below
        // that has your output come out in the correct order.
        //$final .= ob_get_clean();
        $final = ob_get_clean() . $final;
    }

    // Apply any filters to the final output
    echo apply_filters('final_output', $final); }, 0); ?>

2b) 将以下 PHP 添加到 wp-content/themes/yourthemenamehere/functions.php 中。它使用上面的 'final_output' 钩子。(PHP 5.3 或更高版本需要使用匿名函数。)

add_filter('final_output', function($output) {
    // IP of the proxy server
    $WWW_IP = “4.4.4.4”; 
    //$WWW_IP = “4.4.2.2”;  // My workstation, for testing purpose only
    if ($_SERVER['REMOTE_ADDR'] == $WWW_IP) {
        // Force HTTPS when rewriting
        $output = str_replace('http://origin.example.com', 'https://proxy.example.com’, $output);
        // Catch anything that wasn’t a URL
        return str_replace(‘origin.example.com, 'proxy.example.com', $output);
    }
    return $output;
});

如果一切顺利,您现在应该看到从 origin.example.com 提供的内容被 proxy.example.com 代理了。

我仍在测试中,如果您发现错误或遗漏,请添加评论。


请确保将您的WordPress地址和站点地址设置为http://而不是https://,以避免在原始服务器上向前端发送301重定向,从而导致无限循环。并且请务必删除任何Strict-Transport-Security头信息,尽管我认为拉取信息的前端可能会忽略它,因为它只从http://拉取。 - Mike J-P.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,