Redmine HTTPS Configuration: Running the Project Management Tool Behind a TLS Proxy

Redmine is a Ruby on Rails project management application. It does not include a production-ready web server — it is designed to run behind Puma or Passenger as a backend, with nginx or Apache as the TLS-terminating reverse proxy. This article covers configuring nginx with TLS for Redmine, setting the correct base URL so email notifications and file attachment links work over HTTPS, migrating existing HTTP links, and automating certificate renewal.

Redmine deployment architecture

Redmine can be deployed as:

  • Puma (default with Rails 6+) — starts an HTTP server, typically on port 3000 or via a Unix socket
  • Passenger (mod_rails for Apache/nginx) — Passenger embeds the Ruby runtime inside the web server
  • WEBrick — development only, not suitable for production

In all cases, the web server (nginx or Apache) fronts the Ruby process and handles TLS.

Option A: nginx + Puma via Unix socket

Step 1: Configure Puma to use a Unix socket

In Redmine’s config/puma.rb (create it if it does not exist):

# config/puma.rb
environment ENV.fetch("RAILS_ENV") { "production" }

# Use a Unix socket (preferred over TCP for security)
bind "unix:///var/run/redmine/puma.sock"

# Or use TCP on localhost
# bind "tcp://127.0.0.1:3000"

workers 2
threads 1, 5

pidfile "/var/run/redmine/puma.pid"
state_path "/var/run/redmine/puma.state"

activate_control_app

Create the socket directory:

sudo mkdir -p /var/run/redmine
sudo chown redmine:redmine /var/run/redmine

Start Puma:

sudo -u redmine bash -c "cd /var/www/redmine && bundle exec puma -C config/puma.rb"

Or via systemd:

# /etc/systemd/system/redmine.service
[Unit]
Description=Redmine Puma application server
After=network.target

[Service]
Type=simple
User=redmine
Group=redmine
WorkingDirectory=/var/www/redmine
Environment=RAILS_ENV=production
ExecStart=/usr/local/bin/bundle exec puma -C config/puma.rb
Restart=on-failure

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now redmine

Step 2: Configure nginx with TLS

sudo nano /etc/nginx/sites-available/redmine
upstream redmine {
    server unix:///var/run/redmine/puma.sock fail_timeout=0;
    # Or TCP: server 127.0.0.1:3000 fail_timeout=0;
}

server {
    listen 80;
    server_name redmine.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name redmine.example.com;

    ssl_certificate     /etc/letsencrypt/live/redmine.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/redmine.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1d;

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options SAMEORIGIN always;
    add_header X-Content-Type-Options nosniff always;

    root /var/www/redmine/public;

    # Increase upload limit for file attachments
    client_max_body_size 50M;

    location ^~ /assets/ {
        gzip_static on;
        expires max;
        add_header Cache-Control public;
    }

    # Try to serve static files from the public directory first
    try_files $uri/index.html $uri @redmine;

    location @redmine {
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Forwarded-Host  $host;
        proxy_set_header   Host              $http_host;
        proxy_redirect     off;
        proxy_pass         http://redmine;
        proxy_read_timeout 300s;
        proxy_connect_timeout 30s;
        proxy_send_timeout 300s;
    }

    error_page 500 502 503 504 /500.html;
}

Enable and reload:

sudo ln -s /etc/nginx/sites-available/redmine /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Step 3: Configure Redmine’s base URL

Redmine uses the configured hostname to generate URLs in email notifications, attachment links, and internal references. If this is wrong, links in emails will point to the wrong URL or use HTTP.

Open the Redmine admin panel:

  1. Log in as admin.
  2. Go to Administration → Settings → General.
  3. Set Host name and path to redmine.example.com (without protocol, without trailing slash).
  4. Set Protocol to HTTPS.
  5. Click Save.

Or update via the Rails console:

sudo -u redmine bash -c "cd /var/www/redmine && bundle exec rails console production"
Setting.host_name = "redmine.example.com"
Setting.protocol = "https"
exit

These settings control how Redmine generates https://redmine.example.com/... URLs in emails and API responses.


Option B: nginx + Passenger (mod_rails)

Install Passenger:

gem install passenger
passenger-install-nginx-module

Or use the Phusion Passenger packages:

sudo apt install nginx-extras passenger

Create the nginx configuration with Passenger:

server {
    listen 443 ssl;
    http2 on;
    server_name redmine.example.com;

    ssl_certificate     /etc/letsencrypt/live/redmine.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/redmine.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;

    root /var/www/redmine/public;
    passenger_enabled on;
    passenger_ruby /usr/local/bin/ruby;
    passenger_env_var RAILS_ENV production;

    # Increase upload limit
    client_max_body_size 50M;
}

Include Passenger configuration in the main nginx.conf:

http {
    passenger_root /usr/local/lib/ruby/gems/3.x.x/gems/passenger-x.x.x;
    passenger_ruby /usr/local/bin/ruby;
    # ...
}

Option C: Apache + Passenger

For Apache-based deployments:

<VirtualHost *:80>
    ServerName redmine.example.com
    RewriteEngine On
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
</VirtualHost>

<VirtualHost *:443>
    ServerName redmine.example.com

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/redmine.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/redmine.example.com/privkey.pem
    SSLProtocol           -All +TLSv1.2 +TLSv1.3

    DocumentRoot /var/www/redmine/public
    <Directory /var/www/redmine/public>
        Allow from all
        Options -MultiViews
        Require all granted
    </Directory>

    PassengerEnabled on
    PassengerAppEnv production

    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</VirtualHost>

Configuring Redmine to detect HTTPS when behind a proxy

When nginx terminates TLS and proxies to Puma, Redmine needs to know the request was made over HTTPS. Nginx passes X-Forwarded-Proto: https for this.

In Redmine’s config/application.rb, ensure:

config.force_ssl = false  # Let nginx handle redirects

And in config/environments/production.rb:

# Tell Rails to trust X-Forwarded-* headers from the proxy
config.web_console.allowed_request_origins = []

In config/initializers/session_store.rb:

Rails.application.config.session_store :cookie_store, 
  key: '_redmine_session',
  secure: true,      # Only send cookie over HTTPS
  httponly: true

Restart Puma after configuration changes:

sudo systemctl restart redmine

Migrating existing HTTP links in Redmine content

After switching to HTTPS, existing wiki pages and issue descriptions may contain http:// links. Use Redmine’s rake task to update stored URLs:

cd /var/www/redmine
sudo -u redmine bundle exec rake redmine:convert_wiki_attachments_path RAILS_ENV=production

For find-and-replace of HTTP URLs in the database (PostgreSQL example):

UPDATE wiki_contents 
SET text = REPLACE(text, 'http://redmine.example.com', 'https://redmine.example.com')
WHERE text LIKE '%http://redmine.example.com%';

UPDATE issues 
SET description = REPLACE(description, 'http://redmine.example.com', 'https://redmine.example.com')
WHERE description LIKE '%http://redmine.example.com%';

UPDATE journals 
SET notes = REPLACE(notes, 'http://redmine.example.com', 'https://redmine.example.com')
WHERE notes LIKE '%http://redmine.example.com%';

Always take a database backup before running UPDATE queries.


Email notifications with HTTPS links

After enabling HTTPS, verify that email notifications contain the correct HTTPS links:

  1. In Redmine admin: Administration → Settings → Email notifications.
  2. Click Send a test email to yourself.
  3. Check that the link in the email uses https://.

If links still use http://, check:

  • Administration → Settings → General → Protocol is set to HTTPS.
  • Restart Redmine after the settings change.

Certificate renewal

sudo nano /etc/letsencrypt/renewal-hooks/deploy/redmine.sh
#!/bin/bash
# Reload nginx — Redmine itself does not need restarting
systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/redmine.sh

Troubleshooting

ProblemCauseFix
Email links use http://Protocol setting not updatedAdministration → Settings → General → Protocol = HTTPS
Cookie not set (login loop)secure: true on cookie but accessing over HTTPAlways access via HTTPS; check redirect is working
File attachment download uses http://Host name setting wrongSet correct host name in Administration → Settings → General
502 Bad Gateway from nginxPuma socket not running or wrong socket pathCheck systemctl status redmine and socket path in nginx config
Assets not loading after HTTPS migrationRails serving asset URLs with http://Set config.asset_host in production.rb or update nginx to pass correct headers
Slow response under loadPuma worker count too lowIncrease workers in puma.rb and restart

Summary

Redmine HTTPS requires an nginx reverse proxy with TLS termination, configured to pass X-Forwarded-Proto: https to the Ruby backend. The most important Redmine configuration is Administration → Settings → General where you set the hostname and protocol — this controls all generated URLs including email notification links and API responses. For Puma deployments, use a Unix socket for the nginx-to-Puma connection. Certificate renewal is handled by reloading nginx with the Certbot deploy hook.

Scroll to Top