Please help - convert Nginx config to CaddyFile

Hello, I’m hoping I can find someone a lot smarter than myself to help me convert this over. The Nginx file is as followed

    map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name example.com;
  # Useful for Let's Encrypt
  location /.well-known/acme-challenge/ { allow all; }
  location / { return 301 https://$host$request_uri; }
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name example.com;

  ssl_protocols TLSv1.2;
  ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
  ssl_dhparam         /etc/ssl/certs/dhparam.pem;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  root /home/mastodon/live/public;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";
  add_header Content-Security-Policy "style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'self'; img-src data: https:; media-src data: https:; connect-src 'self' wss://example.com; upgrade-insecure-requests";

  location / {
    try_files $uri @proxy;
  }

  location ~ ^/(packs|system/media_attachments/files|system/accounts/avatars) {
    add_header Cache-Control "public, max-age=31536000, immutable";
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header Host $host;
    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 https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    proxy_set_header Host $host;
    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 https;
    proxy_set_header Proxy "";

    proxy_pass http://localhost:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}

I can send you the copy I created, but I don’t think it’s working as it should. Push notifications are suppose to work off the api via WebSocket, but push notifications are not working as they should. I’ve noticed that /if/ I get a push notification, it’s only when my phone is on the same network as the server. I do not receive them while on cellular, or another network. Thanks in advance!

Hi Tony, I’ve formatted your post properly using three backticks for a code block, so people can read it easier. :slight_smile:

1 Like

Please do post it here if you can, it will give us a good idea of what you’ve tried.

Generally, though, by breaking down the nginx config I’m seeing the following key points you’ll need to write for your Caddyfile:

  1. A webroot: /home/mastodon/live/public
    Use Caddy’s root directive: https://caddyserver.com/docs/root
  2. Enabling and configuring gzip
    Use gzip directive: https://caddyserver.com/docs/gzip
  3. Strict transport security and content security policy headers
    You want the header directive: https://caddyserver.com/docs/header
  4. A try_files fallback across a number of locations (some with cache-control headers)
    rewrite can provide this functionality* (see below): https://caddyserver.com/docs/rewrite
  5. A transparent proxy to 127.0.0.1:3000
    Use a proxy with the transparent preset: https://caddyserver.com/docs/proxy
  6. A transparent proxy from /api/v1/streaming to localhost:4000
    Likewise #5
  7. Some configured error pages
    The errors directive sets the response page for error codes: https://caddyserver.com/docs/errors

They’re mostly straightfoward, except for the way your proxies need to interact with your try_files. You need to get a bit creative to achieve this in Caddy, so I’ll write a little bit on how I go about it.

*Using rewrite as nginx’s try_files:

Normally this is quite easy, and looks something like this:

rewrite {
  to {path} {path}/ /index.php
}

The above example tries the request as a file, then as a directory, then falls back to serving index.php if those failed. The problem here is that you need to try and fall back to a proxy, not a file. The way I solve this is by using rewrite to “flag” the request by prepending a subfolder, then proxying all requests to that subfolder, using without to avoid sending the subfolder upstream. It looks something like this:

rewrite {
  to {path} {path}/ /proxy{path}
}
proxy /proxy 127.0.0.1:3000 {
  transparent
  without /proxy
}

With the above, all requests that aren’t servable files or directories in the webroot are proxied upstream. You’ve got one more step to go, though, because you actually have two separate proxies. We’ll need to do a bit more tweaking - we only want the generic proxy in cases where the second proxy won’t be used. A slight tweak to the rewrite, and then we can just add the second proxy as normal:

rewrite {
  if {path} not_match ^/api/v1/streaming
  to {path} {path}/ /proxy{path}
}
proxy /proxy 127.0.0.1:3000 {
  transparent
  without /proxy
}
proxy /api/v1/streaming localhost:4000 {
  transparent
}

That should cover the hard part (#4-6 above), the others (#1-3 and #7) are fairly simple by comparison and will probably be single-line directives each (the docs are more than comprehensive enough to help you implement them).

3 Likes

Thanks Matt!

So, here’s my current CaddyFile config. I have all the assets being served through RAILS on Docker. The main problem I’m having is that push notifications are not working on apps, even though streaming in new posts work.

social.nofftopia.com {
root /Users/glen/mastodon/public
gzip
expires {
match /packs 1m
}
proxy / 127.0.0.1:3000 {
transparent
}
header / {
Strict-Transport-Security "max-age=31536000;"
}
header /packs {
Cache-Control "max-age=2592000"
}
header /about {
Cache-Control "max-age=2592000"
}
proxy /api/v1/streaming 127.0.0.1:4000 {
websocket
}
errors {
502 500.html
}
}

I’ve also tried adding transparent to the api proxy as well with no luck :-/

You know, I think the proxy to :3000 wants a websocket as well. Try add transparent and websocket to both proxies and see if that has an effect?

I actually just tried this a few minutes ago as well, no effects, positive or negative :-/

Apart from the Content-Security-Policy header, I don’t think I see anything else that might make or break the push notifications between the nginx config and your Caddyfile.

Perhaps someone else could chime in if they can (or can’t) see anything we’re missing?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.