Static Web Server 2.43.0
Overview
Static Web Server (SWS) is a fast, asynchronous web server for static files, written in Rust. It is a single self-contained binary with no runtime dependencies, so it starts instantly and uses very little memory. You point it at a directory and it serves your website, documentation or single-page application build output. It offers on-the-fly compression, cache control headers, custom error pages and a quiet health endpoint for load balancers.
This guide covers launching and using the Static Web Server 2.43.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud web servers listing on AWS Marketplace. You select it as the software version when you subscribe and launch.
What you get:
- HTTPS from the first boot. On your instance's first boot, a certificate is generated for this instance alone, and the site is served on port 443 over HTTP/2. Port 80 redirects to HTTPS. No certificate or key is baked into the image.
- nginx as the public edge, with Static Web Server behind it. Static Web Server listens on
127.0.0.1:8080only. nginx terminates TLS on ports 80 and 443 and passes requests to it. This design addresses a published weakness in every Static Web Server 2.x release (GHSA-2fch-9x9r-mxmv): the server never closes an idle or stalled connection, so a single client that holds connections open can take a directly exposed server offline. nginx closes connections that send nothing, send slowly, sit idle or stop reading, and it allows at most 64 connections per client address. - Only your host name is served. The site answers for the name it was configured with (by default, your instance's public DNS name from the EC2 instance metadata service) and for the instance's own IP addresses. Requests for any other host name are dropped.
- Hardened defaults. Directory listing is off, dotfiles such as
.gitor.envare never served, symbolic links are never followed, and the Prometheus metrics endpoint is off. Security headers (Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Content-Security-Policy, Referrer-Policy) are sent on every response. Serving pre-compressed.gz/.br/.zstfiles is off, because in 2.43.0 that feature can be tricked by a symbolic link into serving a file outside the web root (GHSA-cg27-w6jh-934r). Responses are still compressed on the fly. - Locked-down services. Static Web Server runs as the unprivileged
swsaccount with no capabilities, a read-only view of the system and no network access beyond the local machine. Both services are blocked from the instance metadata service, and the AMI requires IMDSv2. - Fail closed. nginx does not start until first boot has generated the certificate, so nothing is served before the instance is set up.
- The official release, verified. The Static Web Server 2.43.0 release is checked against a pinned SHA-256 checksum before it is installed. Ubuntu 24.04 LTS is fully patched at build time, with unattended security upgrades switched on.
Static Web Server has no login and no administration console, so this image has no password.
What is on the image: Static Web Server 2.43.0 (/usr/local/bin/static-web-server, licenses in /usr/share/doc/static-web-server), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large.
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with a route to the internet (or the private route your users reach it by)
- A security group that allows TCP 22 (SSH) from your own networks, and TCP 443 (the site) and TCP 80 (redirects to HTTPS) from your visitors
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Select Static Web Server 2.43.0 on Ubuntu 24.04 as the software version and select your Region, then choose Launch through EC2.
Choose m5.large (a small static site runs comfortably on less), your key pair and a security group as described in Prerequisites.
Alternatively, launch with the AWS CLI. Replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=static-web-server}]'Connect to your instance
Version SSH user
Static Web Server 2.43.0 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot generates the certificate and determines the host name. Then Static Web Server and nginx start. Confirm that all three services are active:
systemctl is-active static-web-server-firstboot static-web-server nginx static-web-server --version | sed -n 1,2pactive active active Version: 2.43.0 Built: 2026-06-10 23:25:42 +02:00Static Web Server listens on the loopback address only, and nginx owns ports 80 and 443:
sudo ss -lntp | grep -E ':(80|443|8080) ' | awk '{print $4, $6}' | sed -E 's/users:\(\("([a-z-]+)".*/\1/'127.0.0.1:8080 static-web-serv 0.0.0.0:443 nginx 0.0.0.0:80 nginx [::]:443 nginx [::]:80 nginxFind your site's address
First boot writes the address the site is served at to an information file. The file holds no secret:
sudo grep -E '^(SITE_URL|HEALTH_URL)=' /root/static-web-server-info.txtSITE_URL=https://ec2-35-171-19-96.compute-1.amazonaws.com/ HEALTH_URL=https://ec2-35-171-19-96.compute-1.amazonaws.com/healthOpen SITE_URL in your browser. The certificate is self-signed, so the browser asks you to confirm the first time. Step 8 shows how to use your own name and certificate.
The browser shows the SecureAnyCloud landing page, served from /var/www/static.
Check HTTPS, the redirect and the host name check
Run these commands on the instance. They talk to nginx over the loopback address, using the configured host name:
H=$(sudo grep '^SITE_URL=' /root/static-web-server-info.txt | sed -E 's#^SITE_URL=https://([^/]+)/$#\1#')
curl -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w 'https://%{url.host}/ -> %{http_code} over HTTP/%{http_version}\n' "https://$H/"
curl -s --resolve "$H:80:127.0.0.1" -o /dev/null -w 'http://%{url.host}/ -> %{http_code} %{redirect_url}\n' "http://$H/"
curl -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w '/health -> %{http_code}\n' "https://$H/health"
curl -sk --resolve unknown.example:443:127.0.0.1 -o /dev/null -w 'unknown host -> %{http_code} (connection closed)\n' https://unknown.example/ || truehttps://ec2-35-171-19-96.compute-1.amazonaws.com/ -> 200 over HTTP/2 http://ec2-35-171-19-96.compute-1.amazonaws.com/ -> 301 https://ec2-35-171-19-96.compute-1.amazonaws.com/ /health -> 200 unknown host -> 000 (connection closed)The certificate was generated on this instance's first boot. It names the host and the instance's addresses:
H=$(sudo grep '^SITE_URL=' /root/static-web-server-info.txt | sed -E 's#^SITE_URL=https://([^/]+)/$#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltNamesubject=CN = ec2-35-171-19-96.compute-1.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-35-171-19-96.compute-1.amazonaws.com, IP Address:35.171.19.96, IP Address:172.31.93.2, IP Address:127.0.0.1, DNS:localhostCheck the hardening
Check that the security headers appear on every response:
H=$(sudo grep '^SITE_URL=' /root/static-web-server-info.txt | sed -E 's#^SITE_URL=https://([^/]+)/$#\1#')
curl -skI --resolve "$H:443:127.0.0.1" "https://$H/" | grep -iE '^(strict-transport|x-frame|x-content-type|content-security|referrer-policy)'strict-transport-security: max-age=63072000; includeSubDomains; preload x-frame-options: DENY x-content-type-options: nosniff content-security-policy: frame-ancestors 'self' referrer-policy: strict-origin-when-cross-originA directory with no index file is not listed, a file inside it is served, and the metrics endpoint and dotfiles are not reachable. Confirm this:
H=$(sudo grep '^SITE_URL=' /root/static-web-server-info.txt | sed -E 's#^SITE_URL=https://([^/]+)/$#\1#') for p in /downloads/ /downloads/README.txt /metrics /.env /no-such-page; do printf '%-22s %s\n' "$p" "$(curl -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code}' "https://$H$p")" done/downloads/ 404 /downloads/README.txt 200 /metrics 404 /.env 404 /no-such-page 404Check the connection limits that nginx enforces in front of Static Web Server, and the account that Static Web Server runs as:
sudo nginx -T 2>/dev/null | grep -E '^\s*(client_header_timeout|client_body_timeout|keepalive_timeout|send_timeout|limit_conn |reset_timedout)' | sort -u ps -o user=,comm= -C static-web-server systemctl show -p IPAddressDeny -p IPAddressAllow static-web-server.serviceclient_body_timeout 15s; client_header_timeout 15s; keepalive_timeout 30s; limit_conn sws_perip 64; reset_timedout_connection on; send_timeout 30s; sws static-web-serv IPAddressAllow=127.0.0.0/8 ::1/128 IPAddressDeny=::/0 0.0.0.0/0Publish your own site
Static Web Server serves whatever is in /var/www/static. Files there are owned by root and readable by everyone, and the server can read but never change them.
To try it, add a page, fetch it over HTTPS, and remove it again:
H=$(sudo grep '^SITE_URL=' /root/static-web-server-info.txt | sed -E 's#^SITE_URL=https://([^/]+)/$#\1#')
echo '<!DOCTYPE html><title>Hello</title><h1>Hello from my site</h1>' | sudo tee /var/www/static/hello.html >/dev/null curl -sk --resolve "$H:443:127.0.0.1" "https://$H/hello.html"; echo
sudo rm -f /var/www/static/hello.html curl -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w 'after removal: %{http_code}\n' "https://$H/hello.html"<!DOCTYPE html><title>Hello</title><h1>Hello from my site</h1> after removal: 404To publish a whole site, copy your build output from your own machine and set the permissions on the instance:
# from your machine:
# rsync -a --delete -e "ssh -i <your-key.pem>" ./public/ ubuntu@<public-ip>:/tmp/site/
# then on the instance:
#
sudo rsync -a --delete --exclude 404.html --exclude 50x.html /tmp/site/ /var/www/static/
#
sudo chown -R root:root /var/www/static
#
sudo find /var/www/static -type d -exec chmod 0755 {} +
#
sudo find /var/www/static -type f -exec chmod 0644 {} +New files are served immediately, so no restart is needed. For a single-page application whose router handles every path, add page-fallback = "/var/www/static/index.html" to the [general] section of /etc/static-web-server/config.toml and run sudo systemctl restart static-web-server.
Your own DNS name and certificate
Point your DNS name at the instance. An Elastic IP keeps the address stable across stops and starts.
Then tell the instance its name. This command records the name, generates a new self-signed certificate for it and reloads nginx. Port 80 then redirects to the new name:
sudo sws-set-hostname <your-domain>To use a certificate that your visitors' browsers trust, obtain one for your name (for example with certbot or acme.sh). Point nginx at it in /etc/nginx/sites-available/sac-static-web-server (the two ssl_certificate lines of the second port 443 server block). Then check the configuration and reload:
sudo nginx -t && sudo systemctl reload nginxConfiguration reference
What Where
Your files /var/www/static
Static Web Server settings /etc/static-web-server/config.toml (restart static-web-server after a change)
nginx site (ports 80/443, host names, certificate) /etc/nginx/sites-available/sac-static-web-server
nginx connection limits /etc/nginx/static-web-server-limits.conf
Served host names /etc/nginx/static-web-server-host.conf (written by sws-set-hostname)
Certificate and key /etc/ssl/static-web-server/ (key readable by root only)
First boot script /usr/local/sbin/static-web-server-firstboot.sh
Site address note /root/static-web-server-info.txt
Licenses and release checksum /usr/share/doc/static-web-server/
The settings that matter most:
grep -E '^(host|port|root|security-headers|directory-listing|ignore-hidden-files|disable-symlinks|compression|compression-static|accept-markdown|health|metrics) ' /etc/static-web-server/config.tomlhost = "127.0.0.1" port = 8080 root = "/var/www/static" security-headers = true directory-listing = false ignore-hidden-files = true disable-symlinks = true compression-static = false accept-markdown = false compression = true health = true metrics = falseStatic Web Server also offers custom headers, redirects, rewrites, CORS and virtual hosts in the [advanced] section. See the Static Web Server documentation for details. Keep host = "127.0.0.1", because the server must stay behind nginx.
Leave these features off on 2.43.0: compression-static, accept-markdown, metrics and basic-auth. Each has a published security advisory that is fixed only in Static Web Server 2.44.0 or later. If you need password protection, add it in nginx (auth_basic in the port 443 server block) instead of in Static Web Server.
Security recommendations
- Allow port 22 in the security group from your own networks only.
- Use your own DNS name and a certificate that your visitors trust (Step 8).
- Keep everything under
/var/www/staticowned by root, and never put secrets,.envfiles or private keys in the web root. - Keep the operating system patched. Unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else.
Support and licensing
Static Web Server is open source and dual-licensed under the Apache License 2.0 and the MIT License. There is no per-user or per-CPU license fee. The licenses are in /usr/share/doc/static-web-server/. The AMI price covers SecureAnyCloud's hardening, first-boot setup and support. SecureAnyCloud is not affiliated with or endorsed by the Static Web Server project.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24 hour response SLA
Deploy on AWS
Launch Static Web Server 2.43.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com