[error] App: 400: GET /api/sessions

Operating system

Linux

Joplin version

2.14.2

Sync target

Joplin Server

What issue do you have?

As this forum prevents me from adding links in my post so I've attached the relevant text file to this post.

This seems to be a commond problem but all resources I've found on it don't seem to fix the issue.
I've got a haproxy & nginx Reverse Proxy that I use to expose all my websites to the Internet. The HA Proxy sits on the outside facing the Internet, traffic is passed from the HAProxy to the nginx Reverse Proxy Server which terminates the SSL connection. It is then passed to the backend Server (in this case, the Linux Server running the Joplin Docker container).

Access to Joplin works internally both via the web and via the app on my phone connected to my LAN.

I have added the joplin.conf file to my nginx conf.d folder along with all my other website conf files. All configs involved are in the attached text file along with the rest of this post.

Log file

Problem.txt (3.58 KB)

@Callahan welcome to the forum.

I have only quickly glanced at the text file and seen your comments about APP_BASE_URL=

If your connection is over https shouldn't the APP_BASE_URL be https://joplin.myserver.net?

No idea if that would cause the 400 errors though...

Hi @dpoulton ,

Thanks for your reply. It's already set to the https://joplin.myserver.net URL. The issue is that as requests hit the haproxy first on IP 192.168.50.3, then get passed to the nginx reverse proxy, which then passes the request to the Joplin Server, the request has the header changed to 192.168.50.3. This is then rejected by Joplin Server as it doesn't match https://joplin.myserver.net.

To make this a simpler question, ignore the haproxy server. So, I have a docker host running Joplin.

Proxy Server IP: 192.168.50.2
Host of the Ubuntu Server running Joplin Server running as a docker container: 192.168.1.18

The config for the docker container is:

  joplin-app:
    container_name: joplin-app
    image: joplin/server:latest
    restart: unless-stopped
    depends_on:
      - joplin-db
    ports:
      - "22300:22300"
    environment:
      - APP_PORT=22300
      - APP_BASE_URL=http://192.168.1.18:22300
      - DB_CLIENT=pg
      - POSTGRES_DATABASE=joplin
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_PORT=5432
      - POSTGRES_HOST=joplin-db

The Reverse Proxy server is trying to:

curl http://192.168.1.18:22300/api/ping

The response is:

{"status":"ok","message":"Joplin Server is running"}

Now try:

curl http://192.168.1.18:22300/api/sessions

The reply is:

{"error":"Not allowed: GET api/sessions"}

Checking the log file on Joplin reveals:

16:18:20 0|app  | 2024-04-13 16:18:20: [error] App: 400: GET /api/sessions: ::ffff:192.168.50.2: Not allowed: GET api/sessions
16:18:20 0|app  | 2024-04-13 16:18:20: App: GET /api/sessions (400) (1ms)
16:18:22 0|app  | 2024-04-13 16:18:22: App: GET /api/items/root:/info.json:/content (200) (10ms)
16:18:22 0|app  | 2024-04-13 16:18:22: App: POST /api/locks (200) (5ms)
16:18:22 0|app  | 2024-04-13 16:18:22: App: GET /api/items/root:/:/delta (200) (5ms)
16:18:22 0|app  | 2024-04-13 16:18:22: App: DELETE /api/locks/1_1_f0b4e9f65cc24227bdb2378103adcb0d (200) (3ms)
16:18:22 0|app  | 2024-04-13 16:18:22: App: GET /api/share_users (200) (4ms)
16:18:22 0|app  | 2024-04-13 16:18:22: App: GET /api/shares (200) (6ms)

So at this point, how can I ensure that ANY server, let along the reverse proxy server, can get ANY content from the Joplin Server?

This is the same error I get from the Joplin app when trying to do a sync from outside my network via the Reverse Proxy Server.

The nginx Reverse Proxy config for this site:

server {
  listen 80;
  server_name joplin.myserver.net;
  return 301 https://$server_name$request_uri;
}

server {
  listen 443 ssl http2;
  server_name joplin.myserver.net;
  client_max_body_size 0;
  underscores_in_headers on;

  location / {
    proxy_headers_hash_max_size 512;
    proxy_headers_hash_bucket_size 64;
    proxy_set_header Host $host;
    proxy_hide_header X-Powered-By;
    proxy_set_header X-Forwarded-Ssl on;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host joplin.myserver.net;
    add_header Front-End-Https on;
    proxy_pass https://192.168.1.18:22300;
    proxy_read_timeout 90;
  }

#    access_log /var/log/nginx/access.log combined_ssl;
    access_log /var/log/nginx/joplin.myserver.net.access.log;
    error_log /var/log/nginx/joplin.myserver.net.error.log;

    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_certificate /etc/letsencrypt/live/joplin.myserver.net-0001/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/joplin.myserver.net-0001/privkey.pem; # managed by Certbot
}

I have many other services set up behind a Reverse proxy Server and I don't experience these issues so I'm confused as to why Joplin is so strict on the base URL. In my mind, this relates to how the website serves pages, not a definition of what can connect to the server.

What am I missing?

Have manged to fix the issue. So it looks like the block for the redirect in the Reverse Proxy config was the culprit. I'm still not sure why but by accessing the site from outside using https instead of http, the problem is resolved. I guess I'm so used to just dropping the FQDN into the config boxes and letting my Reverse proxy take care of upgrading the session to HTTPS, that I didn't bother to think about it.

I'll add my final config here in case this helps anyone stumbling over this in the future. My set up is a little different to most I've seen in that I use haproxy exposed to the Internet which has a set of simple rules that drops all connections inbound unless they are trying to get to a valid URL of the sites I host.
This prevents all attempts for bot scanners that scan bulks of IP addresses, to even see I have anything on port 80 and 443 as attempting to connect on http(s)://X.X.X.X immediately gets dropped as it doesn't contain a valid URL. That connection, if valid is then handed off to an nginx Reverse Proxy Server that terminates the SSL connection and then in turn, hands off a http connection to the backend server/website.

So, my docker-compose.yml file:

  joplin-db:
    container_name: joplin-db
    image: postgres:16
    restart: unless-stopped
    volumes:
      - /mnt/nfs/docker_DBs/joplin:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=secret_user
      - POSTGRES_PASSWORD=secret_password
      - POSTGRES_DB=joplin

  joplin-app:
    container_name: joplin-app
    image: joplin/server:latest
    restart: unless-stopped
    depends_on:
      - joplin-db
    ports:
      - "22300:22300"
    environment:
      - APP_PORT=22300
      - APP_BASE_URL=http://joplin.myserver.net
      - DB_CLIENT=pg
      - POSTGRES_DATABASE=joplin
      - POSTGRES_USER=secret_user
      - POSTGRES_PASSWORD=secret_password
      - POSTGRES_PORT=5432
      - POSTGRES_HOST=joplin-db

My Reverse Proxy config:

server {
  listen 80;
  server_name joplin.myserver.net;
  return 301 https://$server_name$request_uri;
}

server {
  listen 443 ssl http2;
  server_name joplin.myserver.net;
  client_max_body_size 0;
  underscores_in_headers on;

  location / {
    proxy_headers_hash_max_size 512;
    proxy_headers_hash_bucket_size 64;
    proxy_set_header Host $host;
    proxy_hide_header X-Powered-By;
    proxy_set_header X-Forwarded-Ssl on;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host joplin.myserver.net;
    add_header Front-End-Https on;
    proxy_pass http://192.168.1.18:22300;
    proxy_read_timeout 90;

    # Hardening headers that get a top score on https://securityheaders.com
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Content-Security-Policy "default-src 'self' https:" always;
    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
  }

#    access_log /var/log/nginx/access.log combined_ssl;
    access_log /var/log/nginx/joplin.myserver.net.access.log;
    error_log /var/log/nginx/joplin.myserver.net.error.log;

    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_certificate /etc/letsencrypt/live/joplin.myserver.net-0001/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/joplin.myserver.net-0001/privkey.pem; # managed by Certbot
}

I'll skip the haproxy config as it's rather long but if anyone wants it, drop me a PM.

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