Skip to main content

Default Configuration

Sable includes a production-ready nginx configuration in docker-nginx.conf. This configuration is optimized for serving single-page applications.

Basic Configuration

server {
  listen 80;
  listen [::]:80;

  location / {
    root /usr/share/nginx/html;

    rewrite ^/config.json$ /config.json break;
    rewrite ^/manifest.json$ /manifest.json break;

    rewrite ^/sw.js$ /sw.js break;
    rewrite ^/pdf.worker.min.js$ /pdf.worker.min.js break;

    rewrite ^/public/(.*)$ /public/$1 break;
    rewrite ^/assets/(.*)$ /assets/$1 break;

    rewrite ^/element-call/dist/(.*)$ /element-call/dist/$1 break;

    rewrite ^(.+)$ /index.html break;
  }
}

Configuration Breakdown

Server Block

server {
  listen 80;
  listen [::]:80;
Listens on port 80 for both IPv4 and IPv6 connections.

Location Block

location / {
  root /usr/share/nginx/html;
Serves files from /usr/share/nginx/html, which is where the built Sable application is located.

Rewrite Rules

The configuration uses specific rewrite rules to handle different file types:

Configuration Files

rewrite ^/config.json$ /config.json break;
rewrite ^/manifest.json$ /manifest.json break;
Serves configuration and manifest files directly.

Service Workers and Scripts

rewrite ^/sw.js$ /sw.js break;
rewrite ^/pdf.worker.min.js$ /pdf.worker.min.js break;
Serves the service worker and PDF worker scripts from the root.

Static Assets

rewrite ^/public/(.*)$ /public/$1 break;
rewrite ^/assets/(.*)$ /assets/$1 break;
Serves static assets from their respective directories.

Element Call Assets

rewrite ^/element-call/dist/(.*)$ /element-call/dist/$1 break;
Serves Element Call embedded assets.

SPA Routing

rewrite ^(.+)$ /index.html break;
Routes all other requests to index.html for client-side routing.

Reverse Proxy Setup

If you’re running Sable behind a reverse proxy (e.g., another nginx instance or Caddy):

Nginx Reverse Proxy

server {
  listen 80;
  server_name sable.example.com;

  location / {
    proxy_pass http://localhost:8080;
    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;
  }
}

Apache Reverse Proxy

<VirtualHost *:80>
  ServerName sable.example.com

  ProxyPreserveHost On
  ProxyPass / http://localhost:8080/
  ProxyPassReverse / http://localhost:8080/
</VirtualHost>

SSL/TLS Configuration

Always use HTTPS in production to protect user credentials and communications.

Let’s Encrypt with Certbot

1

Install Certbot

sudo apt install certbot python3-certbot-nginx
2

Obtain SSL certificate

sudo certbot --nginx -d sable.example.com
3

Auto-renewal

Certbot automatically sets up certificate renewal. Test with:
sudo certbot renew --dry-run

Manual SSL Configuration

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name sable.example.com;

  ssl_certificate /path/to/fullchain.pem;
  ssl_certificate_key /path/to/privkey.pem;

  # Modern SSL configuration
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;

  # HSTS
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

  location / {
    proxy_pass http://localhost:8080;
    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;
  }
}

# Redirect HTTP to HTTPS
server {
  listen 80;
  listen [::]:80;
  server_name sable.example.com;
  return 301 https://$server_name$request_uri;
}

Additional Optimizations

Gzip Compression

gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript;

Caching Headers

location /assets/ {
  root /usr/share/nginx/html;
  expires 1y;
  add_header Cache-Control "public, immutable";
}

location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

location / {
  root /usr/share/nginx/html;
  try_files $uri $uri/ /index.html;
  expires -1;
  add_header Cache-Control "no-store, no-cache, must-revalidate";
}

Security Headers

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;

Testing Configuration

Before reloading nginx, always test the configuration:
sudo nginx -t
If the test is successful, reload nginx:
sudo systemctl reload nginx

Build docs developers (and LLMs) love