Mastodon instances store user-uploaded images on the same domain as the instance by default. This can increase server load and expose the main domain to potential security risks from malicious file uploads. Separating image hosting to a dedicated subdomain improves performance and security by isolating static content. This article explains how to configure your Mastodon instance to serve images exclusively from a subdomain.
Key Takeaways: Configuring Subdomain-Only Image Hosting in Mastodon
- CDN_HOST environment variable: Sets the subdomain URL for serving static files, including user images.
- Nginx or Apache reverse proxy: Routes image requests to the correct subdomain while keeping the main domain clean.
- Object storage (S3-compatible): Required for offloading images to a subdomain with a separate bucket or path.
Why Use a Subdomain for Image Hosting in Mastodon
Mastodon stores user-uploaded images, avatars, and headers as static files. When these files are served from the same domain as the instance, every image request hits the main web server. This can slow down the instance for other users and makes the domain vulnerable to cross-site scripting attacks through uploaded content. By using a subdomain such as media.yourinstance.social, you isolate static content from the application logic. This also allows you to configure a content delivery network or a separate caching layer for images without affecting the main site.
The Mastodon configuration relies on the CDN_HOST environment variable. Despite its name, this variable controls the host used for serving all static files, including user-uploaded media. Setting this variable to a subdomain URL tells Mastodon to generate image URLs pointing to that subdomain. You must also configure your web server or reverse proxy to serve those requests correctly. Object storage, such as Amazon S3 or a compatible service, is required because Mastodon cannot serve subdomain-only images from the local filesystem with this method.
Prerequisites
Before starting, ensure you have the following:
- Root or sudo access to the Mastodon server
- An S3-compatible object storage bucket (e.g., AWS S3, DigitalOcean Spaces, MinIO)
- A subdomain DNS record pointing to your Mastodon server or CDN
- Mastodon version 3.0 or later (older versions lack the CDN_HOST variable)
Steps to Enable Subdomain-Only Image Hosting
- Create a DNS record for the subdomain
Add a CNAME or A record for your chosen subdomain, such asmedia.yourinstance.social. Point it to the same IP address as your main Mastodon domain or to your CDN endpoint. Wait for DNS propagation before proceeding. - Set up an S3-compatible object storage bucket
Create a new bucket in your object storage service. For example, in AWS S3, create a bucket namedmastodon-media. Configure the bucket for public read access. Note the bucket endpoint URL, access key, and secret key. - Configure Mastodon environment variables
Edit the Mastodon environment file, typically located at~/mastodon/.env.productionor/etc/mastodon/.env.production. Add or modify the following lines:S3_ENABLED=trueS3_BUCKET=mastodon-mediaS3_REGION=us-east-1S3_PROTOCOL=httpsS3_HOSTNAME=s3.us-east-1.amazonaws.comAWS_ACCESS_KEY_ID=your-access-keyAWS_SECRET_ACCESS_KEY=your-secret-keyCDN_HOST=https://media.yourinstance.social
Replace the values with your actual bucket region, hostname, and credentials. - Configure the web server to serve the subdomain
In your Nginx configuration, add a new server block for the subdomain. For example:server { listen 443 ssl; server_name media.yourinstance.social; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }If using a CDN, point the subdomain to the CDN endpoint instead.
- Migrate existing media to the object storage
Run the Mastodon media migration command to move existing uploads to the S3 bucket. Use:RAILS_ENV=production bin/tootctl media migrate
This command copies all local media files to the configured S3 bucket. Wait for the process to complete. - Restart Mastodon services
Restart the web, streaming, and sidekiq processes to apply the changes. Run:systemctl restart mastodon-web mastodon-streaming mastodon-sidekiq
Or use the appropriate service manager for your setup. - Verify the configuration
Upload a test image from a Mastodon account. Inspect the image URL in the browser. It should use the subdomain, for example:https://media.yourinstance.social/media_attachments/files/123/456/789/original/abc.jpg. If the URL still shows the main domain, check your environment variables and restart services again.
Common Mistakes and Limitations
Image URLs Still Use the Main Domain After Configuration
This usually happens when the CDN_HOST variable is not applied correctly. Verify that the variable is set in the correct environment file and that there are no typos. Also ensure that the Mastodon services were restarted after the change. If you are using a load balancer or reverse proxy, check that it does not override the Host header.
Existing Media Not Served from Subdomain
The bin/tootctl media migrate command only moves files to object storage. It does not update the URLs of already-uploaded media in the database. To update existing media URLs, run bin/tootctl media regenerate. This re-processes media attachments and regenerates URLs using the new CDN_HOST.
Object Storage Costs Increase Unexpectedly
Moving media to object storage shifts storage costs from your server to the S3 provider. Monitor your bucket usage and set up billing alerts. You can also configure lifecycle policies to delete old or unused media from the bucket.
Mastodon Subdomain Image Hosting vs Default Local Storage
| Item | Subdomain with Object Storage | Default Local Storage |
|---|---|---|
| Image URL pattern | https://media.instance.social/... |
https://instance.social/... |
| Server load for media | Offloaded to object storage or CDN | Handled by the main web server |
| Security isolation | Separate subdomain reduces XSS risk | Images served from the same origin |
| Setup complexity | Requires DNS, S3 bucket, and config changes | Built-in, no extra configuration |
| Cost | Object storage fees apply | Only uses server disk space |
Enabling subdomain-only image hosting separates static content from the Mastodon application. This reduces server load and improves security. After completing the steps above, test the setup by uploading an image and verifying the URL. For advanced configurations, consider adding a CDN in front of the subdomain to further speed up image delivery worldwide.