SSL not working on all pages [duplicate]

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;
  }