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:
- You can’t use ProxyPass or ProxyPassReverse in .htaccess, so we have to use other methods to mimic what they do.
- 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.
- You need to rewrite headers
- 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:
- 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>
- 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.
RewriteRule ^(foo|bar)/?$ http://example.com/$1 [P,L]
– MaggioreL
flag, which just indicates that no more rules should be tested. – QktpP
forces the adding of theL
– Orchestra