Configure nginx
This page writes by hand the nginx files that orionplace domain add creates: a site on port 80 for the certificate check, a Let's Encrypt certificate and the HTTPS site that passes requests to the storefront service. The examples use the domain shop.example.com and the storefront on port 3000.
How it works
nginx receives the public requests on ports 80 and 443. Port 80 only answers certificate checks and redirects to HTTPS. The HTTPS site passes everything to the storefront on 127.0.0.1 and tells it the domain and the customer's address in request headers. The new files sit next to your other sites and change none of them.
1 Requirements
You need nginx with HTTPS support, certbot and the running storefront service, see Run the storefront as a service. The domain's DNS A record, and AAAA if the server has IPv6, must point to this server, and ports 80 and 443 must be open in the server's firewall and in your cloud provider's firewall.
Check that nginx has HTTPS support and see which files and folders it includes:
nginx -V 2>&1 | grep -o http_ssl_module
nginx -T 2>/dev/null | grep -E "^ *include"Put the files into the folder that the http block includes: /etc/nginx/conf.d on Debian, Ubuntu, the RHEL family, Fedora, Amazon Linux and openSUSE, /etc/nginx/http.d on Alpine. The examples use conf.d.
Arch Linux includes no folder by default. Add this line inside the http block of /etc/nginx/nginx.conf:
include /etc/nginx/conf.d/*.conf;2 Shared settings
Once per server, at the http level: this map lets connections that ask for an upgrade (WebSocket) through to the storefront. If another of your files already has such a map under another name, you can use that name in the site instead.
map $http_upgrade $storefront_connection_upgrade {
default upgrade;
'' close;
}
Long domain names can stop nginx with could not build server_names_hash. Then add this line once to the same file, unless another of your files sets it already: nginx refuses it twice.
server_names_hash_bucket_size 128;3 Site on port 80
Create the folder certbot puts its check files into:
sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challengeThen save the site file. It answers the certificate checks and shows a short notice until HTTPS is ready:
server {
listen 80;
listen [::]:80;
server_name shop.example.com www.shop.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type text/plain;
}
location / {
default_type text/plain;
return 503 "The storefront for shop.example.com is being set up.";
}
}
Check the configuration and load it (on Alpine: sudo rc-service nginx reload):
sudo nginx -t
sudo systemctl reload nginx4 Certificate
Get one certificate for the domain and its www name. certbot puts its check files into the folder above, so nginx keeps running:
sudo certbot certonly --webroot -w /var/www/letsencrypt \
--cert-name shop.example.com -d shop.example.com -d www.shop.example.com \
--agree-tos --email you@example.comCertificates last 90 days. The certbot package renews them with a timer; on Alpine add a daily cron job that runs certbot renew --quiet. nginx must reload after a renewal to use the new certificate, so add a deploy hook (on Alpine with rc-service nginx reload), check the timer and test the renewal:
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
printf '#!/bin/sh\nnginx -t && systemctl reload nginx\n' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx
systemctl list-timers | grep -i certbot
sudo certbot renew --dry-run5 HTTPS site
Replace the site file with the full version. Port 80 now redirects to HTTPS, www.shop.example.com redirects to shop.example.com, and the HTTPS site passes requests to the storefront:
server {
listen 80;
listen [::]:80;
server_name shop.example.com www.shop.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type text/plain;
}
location / {
return 301 https://shop.example.com$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name www.shop.example.com;
ssl_certificate /etc/letsencrypt/live/shop.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/shop.example.com/privkey.pem;
return 301 https://shop.example.com$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name shop.example.com;
ssl_certificate /etc/letsencrypt/live/shop.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/shop.example.com/privkey.pem;
client_max_body_size 64m;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Front-Domain $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Init-Ip $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $storefront_connection_upgrade;
proxy_buffering off;
proxy_buffer_size 32k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
proxy_read_timeout 120s;
}
}
| Line | What it does |
|---|---|
Host, X-Forwarded-Host | The domain the customer opened. |
X-Front-Domain | The storefront picks your personal backend by this domain. nginx always overwrites it with the requested domain, so a customer cannot send another value. |
X-Forwarded-Proto | Tells the storefront that the page was opened over HTTPS. |
X-Real-IP, X-Forwarded-For, X-Init-Ip | The customer's address; the storefront passes it on to your backend. |
Upgrade, Connection | Lets connections that ask for an upgrade (WebSocket) through, with the map from Shared settings. |
proxy_buffering off | Sends answers on to the customer as they arrive, without waiting for the whole page. |
proxy_buffer_size 32k | Room for large response headers. A SvelteKit storefront sends its script preloads in one long header; with the default 4 KB nginx answers 502. |
proxy_read_timeout 120s | Waits up to 120 seconds for a slow answer. |
client_max_body_size 64m | Accepts uploads up to 64 MB, such as review photos and avatars; the nginx default is 1 MB. |
nginx older than 1.25.1 does not know http2 on;: delete those lines and add http2 to the 443 listen lines instead. An nginx built without HTTP/2 gets neither.
listen 443 ssl http2;
listen [::]:443 ssl http2;6 Next to existing sites
Before loading the site, see which names and addresses your other sites use:
sudo nginx -T 2>/dev/null | grep -nE "server_name|listen|# configuration file"Each domain may be served by one site only. If another file lists shop.example.com or www.shop.example.com, nginx warns conflicting server name and uses only one of them: remove the name from one of the files. A wildcard name such as *.example.com in another site does not need a change, because exact names win over it.
If another site listens on a specific address, for example listen 203.0.113.5:443 ssl;, nginx sends that address's requests only to sites with the same listen line: add the line to these server blocks too. If another site has listen [::]:80 ipv6only=off;, delete listen 80; here, because that line already takes IPv4 as well.
7 Check
Check the configuration, load it and open the storefront:
sudo nginx -t
sudo systemctl reload nginx
curl -I https://shop.example.com/An answer with status 200 means the storefront is online. 502 means nginx does not reach the storefront: check the service and its port. 503 from the storefront means the domain is not added to the project.