Reverse Proxy

This contains examples for how to use sharry behind a reverse proxy.

For the examples below, assume the following:

  • Sharry app is available at 192.168.1.11:9090.
  • The external domain/hostname is sharry.example.com

Configuring Sharry

These settings require a complement config part in the sharry configuration file:

  • First, if Sharry REST server is on a different machine, you need to change the bind.address setting to be either 0.0.0.0 or the ip address of the network interface that the reverse proxy server connects to.

    sharry.restserver {
      # Where the server binds to.
      bind {
        address = "192.168.1.11"
        port = 9090
      }
    }
    

    Note that a value of 0.0.0.0 instead of 192.168.1.11 will bind the server to every network interface. If it is running on the same machine as the reverse proxy server, you can set localhost instead.

  • Sharry needs to know the external url. The base-url can be used to explicitely specify this url. If it is left at its default value, sharry finds the external url from the request. It is recommended to set this url, if you have a single external url. Using above values, it must be set to https://sharry.example.com.

    sharry.restserver {
    
      # This is the base URL this application is deployed to. This is used
      # to create absolute URLs and to configure the cookie.
      #
      # Note: Currently deploying behind a path is not supported. The URL
      # should not end in a slash.
      base-url = "https://sharry.example.com"
      ...
    }
    
  • The maximum request size should probably be increased at the reverse proxy. This depends on your machine, of course. The sharry related setting is sharry.restserver.webapp.chunk-size. This defines the size that is used for uploading chunks of data in one request.

    sharry.restserver {
      webapp {
        # Chunk size used for one request. The server will re-chunk the
        # stream into smaller chunks. But the client can transfer more in
        # one requests, resulting in faster uploads.
        #
        # You might need to adjust this value depending on your setup. A
        # higher value usually means faster uploads.
        chunk-size = "100M"
    }
    

    Here a chunk-size of 100M is used and the reverse proxy must be set to at least this value. Below it is set to 105M, just to be sure.

If you have examples for more servers, please let me know or add it to this site.

Nginx

This defines two servers: one listens for http traffic and redirects to the https variant. Additionally it defines the let’s encrypt .well-known folder name. For more information about how to setup let’s encrypt, please refer to their documentation and/or the nginx documentation.

The https server endpoint is configured with the let’s encrypt certificates and acts as a proxy for the application at 192.168.1.11:9090.

The setting client_max_body_size is relevant, too. This is the maximum size of a single requests. This must be greater than sharry’s webapp.chunk-size setting.

The setting proxy_buffering off; disables buffering responses from the application coming to nginx. Buffering may introduce backpressure problems if the client is not reading fast enough. The response coming from the application may quickly be too large to fit in memory and nginx then writes a temporary file (which is limited to 1G by default). If this limit is reached, nginx waits until the client has received all disk buffered data which in turn can result in send timeouts.

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    server {
        listen 0.0.0.0:80 ;
        listen [::]:80 ;
        server_name subdomain.otherdomain.tld ;
        location /.well-known/acme-challenge {
            root /var/data/nginx/ACME-PUBLIC;
            auth_basic off;
        }
        location / {
            return 301 https://$host$request_uri;
        }
    }
    server {
        listen 0.0.0.0:443 ssl http2 ;
        listen [::]:443 ssl http2 ;
        server_name sharry.example.com ;
        location /.well-known/acme-challenge {
            root /var/data/nginx/ACME-PUBLIC;
            auth_basic off;
        }
        ssl_certificate /var/lib/acme/sharry.example.com/fullchain.pem;
        ssl_certificate_key /var/lib/acme/sharry.example.com/key.pem;
        ssl_trusted_certificate /var/lib/acme/sharry.example.com/full.pem;
        location / {
            proxy_pass http://192.168.1.11:9090;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;

            proxy_set_header    Host                $host;
            proxy_set_header    X-Real-IP           $remote_addr;
            proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
            proxy_set_header    X-Forwarded-Proto   $scheme;

            proxy_buffering off;
            client_max_body_size 105M;
            proxy_send_timeout   300s;
            proxy_read_timeout   300s;
            send_timeout         300s;
        }
    }
}

Serving two domains

This config shows an example to serve nginx on two domains, while the sharry app is fixed to one domain. This will allow to always use the same base-url in e-mail templates, while serving the webapp on different domains.

This is in contrast to the default behaviour (leaving base-url setting to its default): the url in mail templates would change according to the request.

# sharry.conf: base-url = "https://example.org"

map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}

# this is the domain as configured in sharry, serve as is
server {
  listen [::]:443 ssl default_server;

  server_name example.org;

  root /srv/http;

  location / {
    # This is the nginx example from the sharry docs
    proxy_pass http://localhost:9090/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_buffering off;
    client_max_body_size 105M;
    proxy_send_timeout   300s;
    proxy_read_timeout   300s;
    send_timeout         300s;
  }
}

# this is the second domain as configured in sharry, do the replacing
server {
  listen [::]:80 default_server;

  server_name example.com;

  root /srv/http;

  location / {
    # This is the nginx example from the sharry docs
    proxy_pass http://localhost:9090/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_buffering off;
    client_max_body_size 105M;
    proxy_send_timeout   300s;
    proxy_read_timeout   300s;
    send_timeout         300s;

    # Replace the protocol/domain in Location headers
    proxy_redirect https://example.org/ http://example.com/;
    # Replace the protocol/domain in the content
    sub_filter_once off;
    sub_filter "https://example.org" "http://example.com";
    sub_filter "example.org" "example.com";
    # sub_filter does not work with gzip, so enforce plaintext
    # see https://www.nginx.com/resources/wiki/modules/substitutions/#subs-filter-types
    proxy_set_header Accept-Encoding "";
  }
}