Reputation: 83343
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.)
Upvotes: 20
Views: 50681
Reputation: 141
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:
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:
# 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>
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.
Upvotes: 2
Reputation: 411
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.
Upvotes: 25