404 Not Found error when using silverbullet v2 on nixos, with nginx url prefix

Hi, I’m trying to use silverbullet (v2.0.0) behind nginx under the path prefix /sb, but I’m getting a 404 when I make a request to the silverbullet web server. My nginx configuration:

        location /sb/ {
            proxy_pass http://127.0.0.1:3030/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            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 $scheme;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Forwarded-Server $host;
        }

The only environment variable I am passing is SB_USER_PREFIX=/sb in an env file to the systemd service. In journalctl, I see that silverbullet is seeing the prefix, and listening on localhost:

Sep 21 03:51:47 snowcap systemd[1]: Started Silverbullet service.
Sep 21 03:51:48 snowcap silverbullet[169598]: Storing database in /var/lib/silverbullet/.silverbullet.db.json.
Sep 21 03:51:48 snowcap silverbullet[169598]: Starting SilverBullet binding to 127.0.0.1:3030
Sep 21 03:51:48 snowcap silverbullet[169598]: SilverBullet will only be available locally, to allow outside connections, pass -L0.0.0.0 as a flag, and put a TLS terminator on top.
Sep 21 03:51:48 snowcap silverbullet[169598]: Host URL Prefix: /sb
Sep 21 03:51:48 snowcap silverbullet[169598]: Using local disk as a storage backend: /var/lib/silverbullet
Sep 21 03:51:48 snowcap silverbullet[169598]: Running with ALL shell commands enabled.
Sep 21 03:51:48 snowcap silverbullet[169598]: Listening on http://127.0.0.1:3030/
Sep 21 03:51:48 snowcap silverbullet[169598]: SilverBullet is now running: http://127.0.0.1:3030

However, opening it on my laptop gives a 404 response, and running curl -v localhost:3030 on the server also gives 404:

# curl -v http://127.0.0.1:3030
*   Trying 127.0.0.1:3030...
* Connected to 127.0.0.1 (127.0.0.1) port 3030
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 127.0.0.1:3030
> User-Agent: curl/8.14.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 404 Not Found
< content-type: text/plain; charset=UTF-8
< vary: Accept-Encoding
< content-length: 13
< date: Sun, 21 Sep 2025 08:11:11 GMT
<
* Connection #0 to host 127.0.0.1 left intact
404 Not Found#

The “space” that I am using is located at /var/lib/silverbullet (which is also silverbullet’s state directory), and it contains the default index.md created by silverbullet.

What am I missing? Is this an issue with my nginx configuration, or silverbullet configuration?

:thinking:

SB_USER_PREFIX does not exist. I guess you are using SB_URL_PREFIX when looking at the logs. The logs aren’t great here, but when you add a url prefix your app will only be available at that prefix. So you will have to look at localhost:3030/sb not localhost:3030

Sorry, that was a typo on my side, I did mean SB_URL_PREFIX. And you are correct, it is being served at localhost:3030/sb! Although it is failing to load all the static assets, so I’ll have to figure that out now. Thanks.

Static assets are now served under /.fs/ or in your case /sb/.fs/

Then why is it trying to load from .client? I didn’t change the source code or anything. :thinking:

If it helps at all, here’s my nix config for silverbullet:

{ config, domain, lib, pkgs, ... }:
{
  services.silverbullet = {
    enable = true;
    openFirewall = true;
    user = "me";
    listenAddress = "0.0.0.0";
    envFile = "/run/secrets/sb_env";
  };

  users.users.me.extraGroups = [ config.services.silverbullet.group ];
  services.nginx.virtualHosts = {
    "notes.${domain}" = {
      useACMEHost = "${domain}";
      forceSSL = true;
      locations."/" = {
        proxyPass = "http://127.0.0.1:${config.services.silverbullet.listenPort}/";
        recommendedProxySettings = true;
      };
      extraConfig =
       # nginx
       ''
         # force HSTS
         add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
       '';
    };
  };
}

Maybe it’s because I’m trying to use it without HTTPS? I am accessing it over tailscale.