Null Value for REQUEST_URI from Apache Web Server - apache

My goal is to obtain the original request URL before proxying to another URL. I'm using Apache Http Server 2.4 in conjunction with Jetty. I'm creating a custom header called X-Forwarded-Uri in my httpd.conf file that provides me with the original request URI.
<VirtualHost *:80>
...
RequestHeader set X-Forwarded-Uri %{REQUEST_URI}e
ProxyPass /foo http://localhost:8080/foo
ProxyPassReverse /foo http://localhost:8080/foo
...
</VirtualHost>
However, when I make a request for http://localhost/foo and try to retrieve the value of X-Forwarded-Uri from my server side code, I consistently get back null. The request URI is supposed to be the path that comes after the host name and port number. Since I'm supplying /foo, I would expect to get back /foo.
Do I have a configuration error?

%{REQUEST_URI}e is not available to the internal var lookup stuff in mod_headers, since it is not always set, and when it is, it is set too late in request processing.
Use the expr="%{REQUEST_URI}" flavor of expressions instead. It knows how to answer that w/o the dependency on the environment variable by the same name.

Related

Apache Conditional Redirect Rule not forwarding header values

Goal:
To perform conditional redirect/forward from Site1 to Site2 based on specific header (HTTP_SM_USER) value of the request (in Site1) and ensure all the current custom header data is forwarded as part of the redirect/forward to Site2. The user can see the url change in the browser window after the redirect.
Flow:
User accesses Site 1 --> External Application Sets few custom headers in addition to default HTTP headers based on certain criteria --> Web-server looks for specific header value and if matches redirect all the headers to Site 2.
Apache Web server Config:
<VirtualHost *.443>
ServerName site1.com
ServerAdmin ashish#test.com
UseCanonicalName on
DocumentRoot /var/www/html
#
# SSL Config comes here
#
ErrorLog /path/to/error/log
TransferLog /path/to/access/log
LogLevel warn
RewriteEngine On
RewriteCond %{HTTP:SM_USER} ^USER1$ [NC]
RewriteRule .* https://site2.com/$1 [R=301, L]
<Location /page1>
.
.
</Location>
.
.
.
</VirtualHost>
Question: The Conditional Redirect happens but the headers are lost. What should I do to ensure the HTTP headers are forwarded as well ? I tried to see into apache mod_proxy but wasn't sure how in this scenario to use it. Experts please help. Any alternative suggestions are welcome too.
Solution:
I finally got it to work (with mod_proxy) after a lot of troubleshooting using the below flags and proxy rules. All Rewrite rules were removed.
ProxyRequests Off
ProxyPreserveHost On
SSLProxyEngine On
ProxyPass "/" "https://site2.com/" Keepalive=On
ProxyPassReverse "/" "https://site2.com/"
Note: User accesses Site 1 -> External app sets headers and apache immediately proxies to Site 2. Headers are now available on Site2.
You can't have the redirect "forward" headers, because it is up to the client what it sends to the next server after it has been issued a redirect. It may choose not to follow the redirect at all! Unlikely but just to make it clear what the situation is.
If you want the URL to change in the browser, then you need to do this on the application side. Have your application handle directing to the next server, and just issue a 200 response with an instruction for it to do so. Then it can send the custom headers as it did with the first request.
If that is not acceptable, then using mod_proxy could work as you said, but it would not change the URL in the browser, because the first server would be proxying the request to the second, so the browser would still see it as being the first.
In summary, you cannot do what you want to do, because HTTP does not work that way, so you are going to have to compromise in some way, either by not changing the URL in the browser, or by updating the application to take care of it.

Apache ProxyPass to separate VirtualHost on the same system

I am trying to solve an issue with an internal ProxyPass setup on an apache http server, version 2.4. Everything works fine otherwise, it is just this ProxyPass issue that I fail to solve:
I operate several name based virtual hosts on a single system with example.org serving as the default host and all hosts using the https protocol on port 443 with valid (signed) certificates:
example.org
host1.example.org
host2.example.org
For one single type of request I want to setup an internal ProxyPass rule to example.org.
https://host1.example.org/_-_-_/bar => https://example.org/foo/bar
A ProxyPassReverse is not required, since no result is returned. (I also tried with, but that does not make any difference):
This is the rule set which is part of the configuration of host host1.example.org:
SSLProxyEngine On
ProxyRequests Off
ProxyVia Off
ProxyPass "/_-_-_/bar" "https://example.org/foo/bar"
The proxy does work, it proxies the request. But the issue is that it does not request the correct host! So not example.org but the originally requested host host1.example.org, so it requests itself. I can clearly see both requests in the same access log file whilst the access log of example.org stays untouched. Obviously that behavior results in a http status 404. That is also shown on the client side as an error message generated by host host1.example.org (fine) but showing the internal path /foo/bar (not fine)!So my question obviously is:
What to change so that the proxy request is internally processed by the desired virtual host?
Ok, as obviously also previously suspected by myself I had an issue with other configuration details interfering here. I post this answers as a hint for others who might stumble about a similar situation:
Another configuration file included by the host configuration set a global ProxyPreserveHost On directive. That obviously resulted in the internal proxy request to feature the same, preserved host header as the original request, which had to lead to the internal request getting processed by the same virtual host as the original request.
I solved the issue by moving (and duplicating) that directive into specific <Location> blocks, so that the global default to not preserve the host in proxy requests is not modified. Consequently the proxy requests described in the question work as expected, they are processed by the desired virtual host now.

Reverse proxy with request dispatch (to Rstudio server)

I have a multi-tier application of three layers lets say public, business and workspace (all running apache).
Client requests hits the public servers, requests are processed and dispatched on to business servers that does 'things' and response is returned back to public server which then processes the response and pass it on to the client.
I have a scenario wherein I want a request say /rstudio coming to the public server dispatched onto the business which intern reverse proxy to workspace server. There are two catch here:
the workspace server varies per request
application running on workspace server (Rstudio) uses GWT and references resources (static resources js, css etc and RPC coms) on the root url. All the in-application redirection also happens on the domain.
From the business server, I have setup reverse proxy to Rstudio server from my application server.
<Proxy *>
Allow from localhost
</Proxy>
ProxyPass /rstudio/ http://business_server/
ProxyPassReverse /rstudio/ http://business_server/
RedirectMatch permanent ^/rstudio$ /rstudio/
and this work fine (ref. https://support.rstudio.com/hc/en-us/articles/200552326-Running-with-a-Proxy). To handle dynamic workspace server, I could the following but ProxyPassReverse does not support expression in value and this no joy with this approach.
ProxyPassMatch ^/rstudio/(.*)$ http://$1
ProxyPassReverse ^/rstudio/(.*)$ http://$1
RedirectMatch permanent ^/rstudio$ /rstudio/
I have tried the same with mod_rewrite rule (following) but without ProxyPassReverse and due to domain redirection on the GWT Rstudio, this does not work. Adding ProxyPassReverse would fix the problem but I am caught up with no expression on value part to deal with dynamic workspace server issue.
RewriteRule "^/rstudio/(.*)" "http://$1" [P]
Following is the third approach to solve this problem using LocationMatch and mod_headers:
<LocationMatch ^/rstudio/(.+)>
ProxyPassMatch http://$1
Header edit Location ^http:// "http://%{SERVER_NAME}e/rstudio/"
</LocationMatch>
But this is no joy too because value on header directive is not evaluated against environment variable (and only back-references work here). Althought I can get the reverse proxy thing working if I had code the business_server, which is :
<LocationMatch ^/rstudio/(.+)>
ProxyPassMatch http://$1
Header edit Location ^http:// "http://private_server/rstudio/"
</LocationMatch>
Question 1: I was wondering if there are any better way to solve this problem without hardcoding the server DNS in apache conf?
Question 2: With the hard coded server DNS the reverse proxy works for me (patchy but works) but I am hit with GWT issue of resource references on root and the request dispatch is not fully working. I get to the signin page but resources are not found.
I was wondering if there is any better way to handle that?
Following is the example log from browser:
Navigated to https://public_server/rstudio
rworkspaces:43 GET https://public_server/rstudio.css
rworkspaces:108 GET https://public_server/js/encrypt.min.js
rworkspaces:167 GET https://public_server/images/rstudio.png 404 (Not Found)
rworkspaces:218 GET https://public_server/images/buttonLeft.png 404 (Not Found)
rworkspaces:218 GET https://public_server/images/buttonTile.png 404 (Not Found)
rworkspaces:218 GET https://public_server/images/buttonRight.png 404 (Not Found)

ProxyPassMatch with ProxyPassReverse

Folks,
We are trying to setup Apache reverse proxy for the following scenario:
Incoming requests take the form http://foo.com/APP/v1/main.html
For some servers the URL will reference a difference version, say, http://foo.com/APP/v2/main.html
An upstream load balancer (HAProxy) will send the request to the right server which will have an Apache2 reverse proxy fronting a JBoss server.
When the request shows up at Apache 2 it will have request path like /APP/v1/main.html
We want it to (reverse) proxy out to http://localhost:8080/AppContext/main.html, irrespective of version fragment in URL (v1, v2, etc.).
I have been trying to do this like so:
ProxyPassMatch ^/.*?/APP.*?/(.*)$ http://localhost:8080/AppContext/$1
ProxyPassReverse /APP http://localhost:8080/AppContext
My questions are:
Is my use of ProxyPassMatch correct?
My ProxyPassReverse is "static". How do I make it aware of the potentially variable stuff after /APP?
Thanks for any insights.
-Raj
You're close, try changing the regex a little to account for the version fragment:
ProxyPassMatch ^/.*?/APP.*?/v[0-9]+/(.*)$ http://localhost:8080/AppContext/$1
The ProxyPassReverse is mostly to ensure the rewriting on-the-fly of location header fields in the responses given by the proxied app. So when it returns a 301 redirect to, say, http://localhost:8080/AppContext/something, apache knows to change it to /APP/v1/something so information behind the proxy won't get exposed. Because you have a dynamic URL used in the reverse proxy, you have a few choices here. You can either send it to the HAProxy load balancer (not sure where that is for you), or you can just pick one and hope for the best. For example, if you have a load balancer at /APP/balancer/ which then sends requests to /APP/v1/, /APP/v2/, /APP/v3/, etc. Then you can do this:
ProxyPassReverse /APP/balancer http://localhost:8080/AppContext
Otherwise, you can just point it to one and hope for the best:
ProxyPassReverse /APP/v1 http://localhost:8080/AppContext

Retain original request URL on mod_proxy redirect

I am running a WebApplication on a Servlet Container (port 8080) in an environment that can be accessed from the internet (external) and from company inside (intenal), e.g.
http://external.foo.bar/MyApplication
http://internal.foo.bar/MyApplication
The incomming (external/internal) requests are redirected to the servlet container using an apache http server with mod_proxy. The configuration looks like this:
ProxyPass /MyApplication http://localhost:8080/MyApplication retry=1 acquire=3000 timeout=600 Keepalive=On
ProxyPassReverse /MyApplication http://localhost:8080/MyApplication
I am now facing the problem that some MyApplication responses depend on the original request URL. Concrete: a WSDL document will be provided with a element that has a schemaLocation="<RequestUrl>?xsd=MyApplication.xsd" element.
With my current configuration it always looks like
<xs:import namespace="..." schemaLocation="http://localhost:8080/MyApplication?xsd=MyApplication.xsd"/>
but it should be
External Request: <xs:import namespace="..." schemaLocation="http://external.foo.bar/MyApplication?xsd=MyApplication.xsd"/>
Internal Request: <xs:import namespace="..." schemaLocation="http://internal.foo.bar/MyApplication?xsd=MyApplication.xsd"/>
I suppose this is a common requirement. But as I am no expert in configuration of the apache http server and its modules I would be glad if someone could give some (detailed) help.
Thanks in advance!
If you're running Apache >= 2.0.31 then you might try to set the ProxyPreserveHost directive as described here.
This should pass the original Host header trough mod_proxy into your application, and normally the request URL will be rebuild there (in your Servlet container) using the Host header, so the schema location should be build using the host and path infos from "before" the proxy.
(Posted here too for the sake of completeness)
Here is another alternative if you would like to retain both the original host name and the proxied host name.
If you are using mod_proxy disable ProxyPreserveHost in the Apache configuration. For most proxy servers, including mod_proxy, read the X-Forwarded-Host header in your application. This identifies the original Host header provided by the HTTP request.
You can read about the headers mod_proxy (and possible other standard proxy servers) set here:
http://httpd.apache.org/docs/2.2/mod/mod_proxy.html
You should be able to do a mod_rewrite in apache to encode the full URL as a query parameter, or perhaps part of the fragment. How easy this might be depends on whether you might use one or the other as part of your incoming queries.
For example, http://external.foo.bar/MyApplication might get rewritten to http://external.foo.bar/MyApplication#rewritemagic=http://external.foo.bar/MyApplication which then gets passed into the ProxyPass and then stripped out.
A bit of a hack, yes, and perhaps a little tricky to get rewrite and proxy to work in the right order and not interfere with each other, but it seems like it should work.