Skip to main content

Configure SSL

Published

2026-11-06

WarningCaution

This section is being revised. Thank you for your patience.

Putting nginx in front

An app that binds to 127.0.0.1 (the pattern used by baseball-trajectory in Install Apps and daemonized in Lab 10) isn’t reachable from outside its own instance. Something has to accept the connection on 443, terminate TLS, and forward the request inward.

Installing nginx

Install nginx:

sudo amazon-linux-extras install -y nginx1

Writing the server block

The server block points at Let’s Encrypt certificate files, so request them first. Install certbot from EPEL and ask for a certificate for the hostname (port 80 must be open in the security group, and nginx must not be running yet, because --standalone binds to that port itself):

sudo amazon-linux-extras install -y epel
sudo yum install -y certbot
sudo certbot certonly --standalone -d trajectory.example.com

Create the site configuration:

sudo vim /etc/nginx/conf.d/baseball-trajectory.conf

Give it a server block that listens on 443, loads the certificate, and proxies to the app on 8000:

server {
    listen 443 ssl;
    server_name trajectory.example.com;

    ssl_certificate     /etc/letsencrypt/live/trajectory.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/trajectory.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

The certificate paths assume you have a hostname pointed at the instance, covered in Configure DNS.

Testing and starting nginx

Test the configuration before reloading, since a syntax error will otherwise take nginx down entirely:

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Enable and start it in one command, the same enable --now used for the app:

sudo systemctl enable --now nginx

The proxy_http_version, Upgrade, and Connection lines are not boilerplate. Shiny for Python holds its connection to the browser open over a WebSocket rather than plain request and response HTTP.

Leave them out and the failure is confusing rather than obvious: the page loads, the layout renders, and then nothing responds. No error appears in the browser, and the app’s own logs show a session opening and immediately closing. Any Shiny app behind any proxy needs this, in R or in Python.

Load the hostname in a browser and you should get the app’s landing page, served over HTTPS.

Recap

  1. An app bound to 127.0.0.1 isn’t reachable from outside its instance, so nginx accepts the connection on 443, terminates TLS, and forwards the request to http://127.0.0.1:8000.

  2. Run sudo nginx -t before reloading, because a syntax error will otherwise take nginx down entirely. Then systemctl enable --now nginx starts it with the same pattern as the app.

  3. The proxy_http_version, Upgrade, and Connection lines keep Shiny’s WebSocket connection open. Leave them out and the page loads, then nothing responds.