Apache RewriteRule Proxy does not follow redirects - apache

I am trying to configure Apache 2.4 with mod_proxy as a reverse proxy and I'm having a problem with redirects not passed from the origin server to the client.
I have the following configuration in the virtual host configuration:
[...]
ProxyPreserveHost On
# ProxyPass "/" "http://old.domain.tld/"
ProxyPassReverse "/" "http://old.domain.tld/"
[...]
When using the commented-out ProxyPass directive in the virtual host config, everything works fine. Which means a 30x-redirect from the origin server gets correctly rewritten and forwarded to the client.
When configuring the ProxyPass in .htaccess (which I need because this only should happen under certain conditions), the reverse proxy is working fine except that it does not process any redirects to the client.
I have the following .htaccess:
RewriteEngine On
RewriteRule ^(.*)$ http://old.domain.tld/$1 [P]
Now, I always get a 404 - not found in the client, when the origin server sends a 30x-Redirect.
In the proxy server log, I can see the following traces:
[...] strip per-dir prefix: /[...]/domain.tld/htdocs/ ->
[...] applying pattern '^(.*)$' to uri ''
[...] rewrite '' -> 'http://old.domain.tld/'
[...] escaped URI in per-dir context for proxy, http://old.domain.tld/ -> http://old.domain.tld/
[...] forcing proxy-throughput with http://old.domain.tld/
[...] go-ahead with proxy request proxy:http://old.domain.tld/ [OK]
The client gets the following headers delivered:
HTTP/1.1 404 Not Found
Date: Fri, 25 Nov 2016 14:04:23 GMT
Server: Apache/2
Content-Length: 1060
Content-Type: text/html; charset=utf-8
Keep-Alive: timeout=5, max=100
Proxy-Connection: Keep-alive
I don't understand why Apache or mod_rewrite/mod_proxy is not forwarding the correct redirect when configured in .htaccess.
Is there any solution for that? Or am I doing something wrong?
Thank you.

ProxyPassReverse its spected to handle redirects so origin server wont take the client outside of the proxy.
This directive lets Apache adjust the URL in the Location,
Content-Location and URI headers on HTTP redirect responses. This is
essential when Apache is used as a reverse proxy (or gateway) to avoid
bypassing the reverse proxy because of HTTP redirects on the backend
servers which stay behind the reverse proxy.
Only the HTTP response headers specifically mentioned above will be
rewritten. Apache will not rewrite other response headers, nor will it
rewrite URL references inside HTML pages. This means that if the
proxied content contains absolute URL references, they will bypass the
proxy. A third-party module that will look inside the HTML and rewrite
URL references is Nick Kew's mod_proxy_html.

Related

.htaccess redirect to a new domain WITHOUT reload the page

Currently, I'm making a website and I would like this site to redirect (via .htaccess) to another domain WITHOUT reloading the page. Because in all the tutorials I saw, it loaded the page of the new domain. Outside what I want is that it keeps the page of the base domain while displaying the URL of the new domain.
Example:
redirect this site
https://DOMAIN1.US/folder1/folder2/page.html?param=1&param=2
to this site
https://DOMAIN2.US/folder1/folder2/page.html?param=1&param=2
i dont have the access to the main server config. Htaccess cannot "fake redirect" ??
What you are asking for is not a simple "redirect" (which is managed by the browser). You need to configure the server that hosts domain1.com as a "reverse proxy" - since this needs to be managed entirely server-side.
The user sends a request to domain1.com. The server at domain1.com then constructs an internal HTTP request (reverse proxy) to domain2.com. The response from domain2.com is then sent back to the server at domain1.com which then forwards the (possibly "rewritten") response back to the client.
This requires additional modules enabled on the server at domain1.com, ie. mod_proxy, mod_proxy_http and additional (optional) modules such as mod_proxy_html, etc. depending on your requirements.
Ideally, you would then configure this in the server config (or VirtualHost container). Notably, you would need to set ProxyPassReverse (in the server config) to cover the scenario of domain2.com issuing an external redirect to itself. The proxy server needs to rewrite the response headers so that the redirect goes to domain1.com, not domain2.com. ProxyPassReverse cannot be set in .htaccess.
You can then use mod_rewrite with mod_proxy by using the P flag. This part you can do in .htaccess. For example, to proxy the request from https://DOMAIN1.US/folder1/folder2/page.html?param=1&param=2 to https://DOMAIN2.US/folder1/folder2/page.html?param=1&param=2.
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?domain1\.us [NC]
RewriteRule ^ https://domain2.us%{REQUEST_URI} [P]
If you have access to the server config then you can do this more simply with the ProxyPass directive (no need for mod_rewrite in this instance since the source and target URLs are the same).
Reference:
https://httpd.apache.org/docs/2.4/mod/mod_proxy.html

Caddy as reverse proxy to rewrite a http redirect url from an upstream response

I am having a backend that is not able when running behind a reverse proxy since I cannot configure a custom base URL.
For the login process the backend makes heavy use of HTTP redirects but due to the fact that is behind a reverse proxy it sends redirection URL that are not reachable by the client.
So I was wondering if there is a way to rewrite the upstream HTTP HEADER Location
If the backend responses
HTTP/1.1 301
Location: http://backend-hostname/auth/login
Caddy should rewrite the Location header to
HTTP/1.1 301
Location: http://www.my-super-site.com/service/a/auth/login
Is something like this possible?
I've that we can remove headers by declaring
header / {
- Location
}
but it possible to replace the header and rewrite the URL?
I was also looking for answer for this question and unfortunately I've found this responses:
https://caddy.community/t/v2-reverse-proxy-but-upstream-server-redirects-to-nonexistent-path/8566
https://caddy.community/t/proxy-url-not-loading-site/5393/7
TLDR:
You need to use sub-domains rather than sub-paths for services that are not design for being after proxy (or at least configure base URL). :(

ProxyPassReverse not taking effect for relative path in Location

My backend Tomcat server sends a 302 redirection with a relative path.
HTTP/1.1 302
Date: Wed, 13 Dec 2017 16:55:05 GMT
Server: Apache TomEE
Location: /StoreWeb/catalog/cotton-shirts
Content-Length: 0
I have this reverse proxy setup in Apache.
ProxyPass /catalog/ http://localhost:8080/StoreWeb/catalog/
ProxyPassReverse /catalog/ http://localhost:8080/StoreWeb/catalog/
But this is not having any effect on the Location header. Apache leaves it unchanged. How can I have Apache convert:
Location: /StoreWeb/catalog/cotton-shirts
To:
Location: /catalog/cotton-shirts
Performing URL rewrites as part of a ProxyPass is a Bad Idea™. You should deploy your webapp in Tomcat under the same URL as you intend to mount it into your URL space and you will never have any of these problems.
If you do manage to re-write the URL in your Location header, I think you'll find that you then have to re-write all of the URLs in all of the pages dynamically-generated by the StoreWeb application. Once you fix those, you'll find that the cookies have the wrong path. And on. And on.
Just deploy your application on the same URL path and your life will be infinitely easier.
I had the same problem and solved it with the following configuration:
ProxyPass /path1 http://server2/path2
ProxyPassReverse /path1 http://server2/path2
ProxyPassReverse /path1 /path2
I'm not sure of that being a 'best practice', though

How to disable HTTP 1.0 protocol in Apache?

HTTP 1.0 has security weakness related to session hijacking.
I want to disable it on my web server.
You can check against the SERVER_PROTOCOL variable in a mod-rewrite clause. Be sure to put this rule as the first one.
RewriteEngine On
RewriteCond %{SERVER_PROTOCOL} ^HTTP/1\.0$
RewriteCond %{REQUEST_URI} !^/path/to/403/document.html$
RewriteRule ^ - [F]
The additional negative check for !^/path/to/403/document.html$ is so that the forbidden page can be shown to the users. It would otherwise lead to a recursion.
If you are on a name-based virtual host (and each virtual server does not have its own separate IP address), then it is technically impossible to connect to your virtual host using HTTP/1.0; Only the default server --the first virtual server defined-- will be accessible. This is because HTTP/1.0 does not support the HTTP "Host" request header, and the Host header is required on name-based virtual hosts in order to "pick" which virtual host the request is being addressed to. In most cases, the response to a true HTTP/1.0 request will be a 400-Bad Request.If you did manage to get that code working, but you later tried to use custom error documents (see Apache core ErrorDocument directive), then the result of blocking a request would be an 'infinite' loop: The server would try to respond with a 403-Forbidden response code, and to serve the custom 403 error document. But this would result in another 403 error because access to all resources --including the custom 403 page-- is denied. So the server would generate another 403 error and then try to respond to it, creating another 403, and another, and another... This would continue until either the client or the server gave up.
I'd suggest something like:
SetEnvIf Request_Protocol HTTP/1\.0$ Bad_Req
SetEnvIf Request_URI ^/path-to-your-custom-403-error-page\.html$
Allow_Bad_Req
#Order Deny,Allow
Deny from env=BadReq
Allow from env=Allow_Bad_Req
In mod_rewrite, something like:
RewriteCond %{THE_REQUEST} HTTP/1\.0$
RewriteCond %{REQUEST_URI} !^/path-to-your-custom-403-error-page\.html$
This will (note the FUTURE tense - as of October 2018) be possible with Apache 2.5, using the PolicyVersion directive in mod_policy. The PolicyVersion directive sets the lowest level of the HTTP protocol that is accepted by the server, virtual host, or directory structure - depending on where the directive is placed.
First enable the policy module:
a2enmod mod_policy
Then in the server config, vhost, or directory (will not work in .htaccess), add:
PolicyVersion enforce HTTP/1.1
Finally restart the server:
systemctl restart apache2

Force mod_proxy to preserve some response headers

We're using Apache in front of Jenkins. Jenkins' Ajax calls include a n header that apparently needs to survive the roundtrip. If we access Jenkins on port 8080, then the n header is included in the response, if we access it through mod_proxy, the n header is getting stripped.
I tried using mod_headers to preserve this header, but for some reason that doesn't work. Is there any other way I can force mod_proxy to leave this header alone?
Edit 1:
This is the response getting returned by Jenkins.
HTTP/1.1 200 OK
Server: Winstone Servlet Engine v0.9.10
Content-Type: text/html;charset=UTF-8
n: 131
Connection: Close
Date: Tue, 20 Mar 2012 09:53:42 GMT
X-Powered-By: Servlet/2.5 (Winstone/0.9.10)
This is what Apache is returning:
Connection:close
Content-Encoding:gzip
Content-Type:text/html;charset=UTF-8
Date:Tue, 20 Mar 2012 10:37:21 GMT
Transfer-Encoding:chunked
Vary:Accept-Encoding
Edit 2:
It turns out Nginx does pass the appropriate headers back. That's the way I managed to solve it now. Still the original question is relevant: is there any way to get it done using Apache?
I found a way to get around this issue under apache.
it was created by alex (see https://issues.jenkins-ci.org/browse/JENKINS-327)
basically
my jenkins running at "http://localhost:8080/jenkins"
I want to access it via jenkins.mydomain.com.
now when I access jenkins.mydomain.com apache will redirect me to jenkins.mydomain.com/jenkins, not perfact but at least works.
<VirtualHost *:80>
ServerName jenkins.mydomain.com
Redirect / http://jenkins.mydomain.com/jenkins
<Location /jenkins>
ProxyPass http://localhost:8080/jenkins
ProxyPassReverse http://localhost:8080/jenkins
</Location>
</VirtualHost>
I eventually moved to Nginx. Nginx didn't strip out the headers. Still, it remains weird that you cannot get Apache to leave the n header alone.