My config for zammad behind external nginx reverse proxy

Due to my problem with Zammad 7.0 like in this thread here:

i’ve checked my reverse proxy settings. It seems, that the assets are not correctly delivered, like it was in 6.5.x. First i tried to set the RAILS_SERVE_STATIC_FILES environment variable. While this works, it’s probably not the ideal solution in terms of performance.

Doing some conversion with my AI i got pointed to the way that is working for me. The default nginx on the zammad host stays active and serves only the static files. My reverse proxy takes all the other requests. I have the reverse proxy for ssl termination, access limiting for my websites, logging and so on. So i dont want to maintain a second webserver like the zammads nginx instance.

This is my nginx config on the zammad host:
(hidden, use it at your own risk)

Summary
server {
  listen 80;
  server_name localhost; 
  root /opt/zammad/public;

  location ~ ^/(assets/|robots.txt|humans.txt|favicon.ico|apple-touch-icon.png) {
    expires max;
    add_header Cache-Control "public, max-age=31536000";
    access_log off;
    try_files $uri =404;
  }

  # Optional: block other requests or proxy to local rails if needed
  location / { return 404; }
}

And this is the config at the reverse proxy:

Summary

upstream zammad-railsserver {
  server zammad-ip:3000;
}

upstream zammad-websocket {
  server zammad-ip:6042;
}

upstream zammad-static {
  server zammad-ip:80;
}

server {
  listen 80;
  listen [::]:80;

  server_name example.de;

  # security - prevent information disclosure about server version
  server_tokens off;

  access_log /var/log/nginx/zammad.access.log;
  error_log /var/log/nginx/zammad.error.log;

  location /.well-known/ {
    root /var/www/html;
  }

  return 301 https://$server_name$request_uri;
}


server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;

  server_name example.de;
  
  #Optional
  #include /etc/nginx/conf.d/common_acl.conf;

  ssl_certificate /root/.acme.sh/example.de_ecc/fullchain.cer;
  ssl_certificate_key /root/.acme.sh/example.de_ecc/example.de.key;

  # security - prevent information disclosure about server version
  server_tokens off;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
  ssl_prefer_server_ciphers off;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 180m;
  ssl_session_tickets off;

  # Use your prefered resolver - also allows external like 1.1.1.1 and 8.8.8.8
  resolver 127.0.0.1;

  add_header Strict-Transport-Security "max-age=63072000" always;

  access_log /var/log/nginx/zammad.access.log;
  error_log  /var/log/nginx/zammad.error.log;

  client_max_body_size 50M;

  # ---- static files ----
  location ~ ^/(assets/|packs/|uploads/|images/|favicon.ico|robots.txt|apple-touch-icon\.png) {
    proxy_set_header Host localhost;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_pass http://zammad-static;
    proxy_buffering on;
    expires max;
    access_log off;
    add_header Cache-Control "public, max-age=31536000";
  }

  # legacy web socket server
  location /ws {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Connection $http_connection;
    proxy_set_header Host $http_host;
    proxy_set_header CLIENT_IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 86400;
    proxy_pass http://zammad-websocket/;
  }

  # action cable
  location /cable {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $http_host;
    proxy_set_header CLIENT_IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 86400;
    proxy_pass http://zammad-railsserver/;
  }

  location / {
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header CLIENT_IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto	$scheme;

    # change this line in an SSO setup
    proxy_set_header X-Forwarded-User "";

    proxy_read_timeout 180;
    proxy_pass http://zammad-railsserver/;

    gzip on;
    gzip_types text/plain text/xml text/css image/svg+xml application/javascript application/x-javascript application/json application/xml;
    gzip_proxied any;
  }
}

Replace example.de and zammad-ip with the values for your server.

This config works for my setup. Maybe someone with similar problems like in the linked thread will have a look and test this on his own setup.

1 Like

Oh so your problem actually was that the nginx that’s serving the Zammad requests is actually not on the same Zammad host. Well yeah that explains why it didn’t work for you.