How to Enable Mastodon Instance Subdomain-Only Image Hosting
🔍 WiseChecker

How to Enable Mastodon Instance Subdomain-Only Image Hosting

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.

ADVERTISEMENT

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

  1. Create a DNS record for the subdomain
    Add a CNAME or A record for your chosen subdomain, such as media.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.
  2. 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 named mastodon-media. Configure the bucket for public read access. Note the bucket endpoint URL, access key, and secret key.
  3. Configure Mastodon environment variables
    Edit the Mastodon environment file, typically located at ~/mastodon/.env.production or /etc/mastodon/.env.production. Add or modify the following lines:
    S3_ENABLED=true
    S3_BUCKET=mastodon-media
    S3_REGION=us-east-1
    S3_PROTOCOL=https
    S3_HOSTNAME=s3.us-east-1.amazonaws.com
    AWS_ACCESS_KEY_ID=your-access-key
    AWS_SECRET_ACCESS_KEY=your-secret-key
    CDN_HOST=https://media.yourinstance.social
    Replace the values with your actual bucket region, hostname, and credentials.
  4. 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.

  5. 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.
  6. 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.
  7. 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.

ADVERTISEMENT

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.

ADVERTISEMENT