How to set up proxy in .htaccess
Asked Answered
Q

2

20

The Apache documentation states that RewriteRule and the should be put in the server configuration, but they can be put in htaccess because of shared hosting situations. I am in such a situation.

I am trying to set up a transparent proxy:

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

This is working fine...except for redirects (like if /foo redirects to /bar). Redirects go back to example.com, not my server.

I understand the the ProxyPassReverse directive will solve this, but I get an "Internal Server Error" page when I add this to .htaccess

Unlike the Rewrite directives, ProxyPassReverse will not work in htaccess.

How do I set up a transparent proxy in shared hosting situation, or is this not possible?

(This seems reasonable, since Rewrite already gets 80% of the way there, and having a transparent proxy in one htaccess would not interfere with having it in another.)

Qktp answered 6/10, 2013 at 3:25 Comment(3)
Can you try: RewriteRule ^(foo|bar)/?$ http://example.com/$1 [P,L]Maggiore
That is the same as what I have written, except for the L flag, which just indicates that no more rules should be tested.Qktp
The apache documentation states that including a P forces the adding of the LOrchestra
S
25

Unfortunately, I'm fairly sure what you want to do isn't possible: I'm trying to do the exact same thing! From my research, I'm fairly confident it's not possible.

Put simply, you need to use ProxyPassReverse, which is only available at a VirtualHost level (or similar); not a htaccess level.

Edit: the only way I have achieved this is by also configuring the responding server/application to know it's behind a proxy, and serving pages appropriately. That is, I use .htaccess to redirect to another server as follows:

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

Then on the application server -- in this case, a JIRA installation -- I configured the Java Tomcat/Catalina appropriately to serve pages with the proxied information:

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

However, that's not completely transparent; the app server needs to serve pages in a proxied manner. It might be of some use, though.

Squarrose answered 17/10, 2013 at 6:22 Comment(4)
I think you may be right. I bug me, though, since there is no good reason why this shouldn't be able to work.Qktp
for you, it was a case of simple config of JIRA. How would one configure any web server to serve pages with proxied information ? What does proxied information mean ?Pampa
by "proxied information" here, I mean that the web server is aware of the address is it actually being accessed on. This means it can send data back to the client with the correct address and/or ports that the client expects to access it on. (In my example above, the JIRA server is actually listening on port 8080; however, clients access it on port 80. The Apache rewrite rule handles the requests from 80 -> 8080, but the JIRA server needs to know that the requests it will get (and replies it makes) are actually addressed elsewhere.) Put simply? You can't do it transparently.Squarrose
Thank you it just works fine to me in a shared host environment.Billhook
Z
2

I managed to gather a few sources to figure out how to do this. I use a shared hosting provider, so I don't have access to server configuration (httpd.conf). I can only use .htaccess to accomplish the proxying. This example is for a WordPress site where I want most of the content served by origin.example.com, but will have some pages served locally, sort of like an overlay. You could go the other way and ONLY proxy specific subdirectories using different RewriteCond rules.

Things to know:

  1. You can’t use ProxyPass or ProxyPassReverse in .htaccess, so we have to use other methods to mimic what they do.
  2. You can’t make proxy calls over HTTPS if SSLProxyEngine is not turned on by your provider, so you will lose some security if you have concerns about MITM attacks. If the origin server is internal, this may not be an issue. You could also use .htaccess on the origin server to enforce HTTPS from everywhere except the proxy server.
  3. You need to rewrite headers
  4. You need to rewrite the HTML that comes back from the origin server, and that needs to be done on the origin server. You can restrict it to certain IPs (i.e. the IP of the proxy) so it won’t break if you access it elsewhere.

What I want:

I want calls to proxy.example.com to serve content origin.example.com. In my case, I want to map everything with a few exceptions. If you only want to map a portion of your site, adjust your rules accordingly.

How to do it:

  1. Configure the .htaccess file on proxy.example.com to proxy all URIs to origin.example.com. I want to be able to log into proxy.example.com, so I don’t rewrite /wp-admin or /wp-login.php. In my case, I have a /programs/ section that I want served by the proxy server itself (also a WordPress instance). Prevent looping by checking 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. ONLY for the IP of the PROXY server, rewrite any references in the HTML itself. This example is for a WordPress site.

Stolen from WordPress filter to modify final html output

2a) Add a Must Use plugin to add a ‘final_output’ hook. Add a file in 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) Add the following PHP to the wp-content/themes/yourthemenamehere/functions.php. It uses the ‘final_output’ hook above. (PHP 5.3 or later required for use of anonymous function.)

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;
});

If all goes well, you should now see the content from origin.example.com served from proxy.example.com.

I'm still testing this, so if you find errors or omissions, please add a comment.

Zeta answered 2/12, 2022 at 18:50 Comment(2)
Be sure to set your WordPress Address and Site Address to http:// instead of https:// on the origin server to avoid sending a 301 redirect to the front end, which would result in an infinite loop. And be sure to remove any Strict-Transport-Security header, although I think the front end that's pulling the information would probably ignore it, as it only pulls from http://.Zeta
To modify the "HTML itself" (HTTP response body) you could potentially use Apache's mod_substitute, if this is available to you in a shared server environment (although mod_proxy itself is often not available on shared servers).Ruysdael

© 2022 - 2024 — McMap. All rights reserved.