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.