The standard setup for Mastodon is to use nginx as a reverse proxy. After one too many missing features I recently switched my installation over to using good old Apache.
There's an example Apache config (cache) in the unmaintained old documentation archive for Mastodon, and since I assume it's useless to try to update that, I'll quickly dump my current config here. There's no guarantee for correctness, but it currently seems to work for me. Note that this configuration does not do any caching for requests to static content retrieved through the reverse proxy.
The following Apache modules are used:
General SSL configuration (personal preference, CipherSuite selection is probably going to age badly). TLS v1.3 is disabled since Ubuntu bionic ships an Apache version that's too old for that:
Mastodon vhost configuration:
The trailing / on the websocket ProxyPass directive is missing by design (it's there in the old example config): Some API requests seen in the wild will not match /api/v1/streaming/ and will get lost.
There's an example Apache config (cache) in the unmaintained old documentation archive for Mastodon, and since I assume it's useless to try to update that, I'll quickly dump my current config here. There's no guarantee for correctness, but it currently seems to work for me. Note that this configuration does not do any caching for requests to static content retrieved through the reverse proxy.
The following Apache modules are used:
- proxy
- proxy_http
- http2
- proxy_http2
- proxy_wstunnel
- headers
- socache_shmcb
- ssl
General SSL configuration (personal preference, CipherSuite selection is probably going to age badly). TLS v1.3 is disabled since Ubuntu bionic ships an Apache version that's too old for that:
<IfModule mod_ssl.c> SSLCertificateFile <path to combined public key / certificate chain file> SSLCertificateKeyFile <path to private key> # the referenced file can be the same as SSLCertificateFile # when the CA certificates are directly appended to the server # certificate for convinience. SSLCertificateChainFile <path to combined public key / certificate chain file> # SSLProtocol -all +TLSv1.2 +TLSv1.3 SSLProtocol -all +TLSv1.2 +TLSv1.1 SSLHonorCipherOrder on SSLCipherSuite ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:EECDH+AESGCM:AES256+EECDH:AES128+EECDH SSLCompression off SSLSessionTickets off SSLSessionCache "shmcb:logs/session-cache(512000)" SSLStaplingResponderTimeout 5 SSLStaplingReturnResponderErrors off SSLUseStapling on SSLStaplingCache "shmcb:logs/stapling-cache(150000)" # needs to be generated first, see https://weakdh.org/sysadmin.html SSLOpenSSLConfCmd DHParameters /etc/ssl/dhparam.pem </IfModule>
Mastodon vhost configuration:
<VirtualHost *:443> ServerAdmin webmaster@example.com ServerName mastodon.example.com SSLEngine on Protocols h2 http/1.1 # fetch static files directly from local file system (adapt to installation path) DocumentRoot /home/mastodon/live/public Header always set Strict-Transport-Security "max-age=31536000" <LocationMatch "^/(assets|avatars|emoji|headers|packs|sounds|system)"> Header always set Cache-Control "public, max-age=31536000, immutable" Require all granted </LocationMatch> <Location "/"> Require all granted </Location> ProxyPreserveHost On RequestHeader set X-Forwarded-Proto "https" ProxyAddHeaders On # these files / pathes don't get proxied and are retrieved from DocumentRoot ProxyPass /500.html ! ProxyPass /sw.js ! ProxyPass /robots.txt ! ProxyPass /manifest.json ! ProxyPass /browserconfig.xml ! ProxyPass /mask-icon.svg ! ProxyPassMatch ^(/.*\.(png|ico)$) ! ProxyPassMatch ^/(assets|avatars|emoji|headers|packs|sounds|system) ! # everything else is either going to the streaming API or the web workers ProxyPass /api/v1/streaming ws://localhost:4000 ProxyPassReverse /api/v1/streaming ws://localhost:4000 ProxyPass / http://localhost:3000/ ProxyPassReverse / http://localhost:3000/ ErrorDocument 500 /500.html ErrorDocument 501 /500.html ErrorDocument 502 /500.html ErrorDocument 503 /500.html ErrorDocument 504 /500.html </VirtualHost>
The trailing / on the websocket ProxyPass directive is missing by design (it's there in the old example config): Some API requests seen in the wild will not match /api/v1/streaming/ and will get lost.