How can I forward requests from my web server?
This is a Canonical Question about reverse proxies, how they work and how they are configured.
How can I serve requests from a service on a different port or from a different server with the same webserver and distinguish it via the URL?
Solution 1:
If you want to serve serve content from multiple services or multiple servers from the same domain you can configure your webserver to act as a reverse proxy. The services (often called backend server or application server) do not necessarily need to be directly reachable from the client, it is common that only the webserver that actually serves the client is directly reachable. It is also possible to have different schemes or protocols (http, https, ajp, ...) between client and server and between server and backend.
Client ---[https]---> Server ---[http]---> backend:8080
|
|---[direct]---> files from document root
`---[http]---> localhost:4000
Examples
- https://example.com/ -> served directly from the webserver
- https://example.com/jenkins/ -> proxied through to a different server that is reachable from the webserver
- https://api.example.com/ -> proxied to a service with a different port on the same host
The reverse proxy can be configured as virtual directories or as subdomains.
Example configuration for Apache
You need to load the necessary apache modules to be able to use it as a reverse proxy. These are at least:
- proxy
- proxy_http
For more protocols you will need to enable more modules.
You can enable the modules with Debian/Ubuntu based distributions like this:
sudo a2enmod proxy proxy_http
With RedHat/CentOS based distributions you will need to find the proper configuration files in /etc/httpd/
and enable the modules manually.
A configuration for the examples above could look like this:
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/html
# SSL options left out for simplicity
ProxyRequests Off
ProxyPreserveHost On
ProxyPass /jenkins/ http://192.168.42.18:8080/
ProxyPassReverse /jenkins/ http://192.168.42.18:8080/
</VirtualHost>
<VirtualHost *:443>
ServerName api.example.com
DocumentRoot /var/www/html
# SSL options left out for simplicity
ProxyRequests Off
ProxyPreserveHost On
ProxyPass / http://localhost:4000/
ProxyPassReverse / http://localhost:4000/
</VirtualHost>
Example configuration for nginx
server {
listen 443;
server_name example.com;
root /var/www/html;
# SSL options left out for simplicity
location /jenkins/ {
proxy_pass http://192.168.42.18:8080/;
}
}
server {
listen 443;
server_name api.example.com;
# SSL options left out for simplicity
location / {
proxy_pass http://localhost:4000/;
}
}
Backend configuration
One thing to keep in mind is that the backend server should be configured accordingly. If, for example, Jenkins is configured to serve requests with the URL http://192.168.42.18:8080/
, it will use this base URL for paths to CSS, JS, and image files, resulting in a lot of 404 errors or timeouts and a non working server. It has to be configured with the proxy URL https://example.com/jenkins/
as a base URL to make it work.
Other backends require a similar configuration. This is the preferred method of resolving issues with URLs pointing to the backend server instead of the frontend server. If for some reason it is not possible to change the configuration of the backend server, you can configure the frontend server to rewrite the HTML before serving it to the client.
However, this should be seen as a last resort method, as it adds unnecessary load to the frontend server.
Apache
With Apache you can use the module mod_proxy_html
. This module provides the directive ProxyHTMLURLMap
, which you can use to rewrite the response.
ProxyPass /jenkins/ http://192.168.42.18:8080/
ProxyPassReverse /jenkins/ http://192.168.42.18:8080/
SetOutputFilter proxy-html # make sure the output is filtered by proxy-html
ProxyHTMLURLMap http://192.168.42.18:8080/ https://example.com/jenkins/
ProxyHTMLExtended On # by default only HTML output is filtered
This should rewrite all occurences of the localserver URL in the answer with the publichostname URL.
nginx
With nginx a similar result can be achieved with the ngx_http_sub_module:
location /jenkins/ {
proxy_pass http://192.168.42.18:8080/;
sub_filter '<a href="http://192.168.42.18:8080/' '<a href="https://$host/jenkins/';
sub_filter_once on;
}