LAN-only without SSL mattermost setup causing CORS errors

Summary
Deploy went smooth, but I get the red banner “Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port.”

Steps to reproduce
Fresh new installation through docker, deployed without NGINX. In the following I report my .env. Docker compose yml untouched, as I followed instructions here: Deploy Mattermost using Containers - Mattermost documentation, steps 1,2,3,5 (only the part without nginx). Notice that, since I want a LAN-only deploy without SSL (I’m doing some quick tests to see if mattermost fullfills my needs), I am putting the IP as domain. Not sure if this is compromising the deploy (I also tried to put “localhost”, but I obtain the same behaviour).
No certificate generated / used.

.env file:

# Domain of service
DOMAIN=<IP> # same of the one in the logs, public IP of the ubuntu VM hosting mattermost on docker

# Container settings
## Timezone inside the containers. The value needs to be in the form 'Europe/Berlin'.
## A list of these tz database names can be looked up at Wikipedia
## https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TZ=UTC
RESTART_POLICY=unless-stopped

# Postgres settings
## Documentation for this image and available settings can be found on hub.docker.com
## https://hub.docker.com/_/postgres
## Please keep in mind this will create a superuser and it's recommended to use a less privileged
## user to connect to the database.
## A guide on how to change the database user to a nonsuperuser can be found in docs/creation-of-nonsuperuser.md
POSTGRES_IMAGE_TAG=13-alpine
POSTGRES_DATA_PATH=./volumes/db/var/lib/postgresql/data

POSTGRES_USER=username_chosen
POSTGRES_PASSWORD=chosen_passwd
POSTGRES_DB=chosen_name

# Nginx
## The nginx container will use a configuration found at the NGINX_MATTERMOST_CONFIG. The config aims
## to be secure and uses a catch-all server vhost which will work out-of-the-box. For additional settings
## or changes ones can edit it or provide another config. Important note: inside the container, nginx sources
## every config file inside */etc/nginx/conf.d* ending with a *.conf* file extension.

## Inside the container the uid and gid is 101. The folder owner can be set with
## `sudo chown -R 101:101 ./nginx` if needed.
## Note that this repository requires nginx version 1.25.1 or later
NGINX_IMAGE_TAG=alpine

## The folder containing server blocks and any additional config to nginx.conf
NGINX_CONFIG_PATH=./nginx/conf.d
NGINX_DHPARAMS_FILE=./nginx/dhparams4096.pem

CERT_PATH=./volumes/web/cert/cert.pem
KEY_PATH=./volumes/web/cert/key-no-password.pem
#GITLAB_PKI_CHAIN_PATH=<path_to_your_gitlab_pki>/pki_chain.pem
#CERT_PATH=./certs/etc/letsencrypt/live/${DOMAIN}/fullchain.pem
#KEY_PATH=./certs/etc/letsencrypt/live/${DOMAIN}/privkey.pem

## Exposed ports to the host. Inside the container 80, 443 and 8443 will be used
HTTPS_PORT=443
HTTP_PORT=80
CALLS_PORT=8443

# Mattermost settings
## Inside the container the uid and gid is 2000. The folder owner can be set with
## `sudo chown -R 2000:2000 ./volumes/app/mattermost`.
MATTERMOST_CONFIG_PATH=./volumes/app/mattermost/config
MATTERMOST_DATA_PATH=./volumes/app/mattermost/data
MATTERMOST_LOGS_PATH=./volumes/app/mattermost/logs
MATTERMOST_PLUGINS_PATH=./volumes/app/mattermost/plugins
MATTERMOST_CLIENT_PLUGINS_PATH=./volumes/app/mattermost/client/plugins
MATTERMOST_BLEVE_INDEXES_PATH=./volumes/app/mattermost/bleve-indexes

## Bleve index (inside the container)
MM_BLEVESETTINGS_INDEXDIR=/mattermost/bleve-indexes

## This will be 'mattermost-enterprise-edition' or 'mattermost-team-edition' based on the version of Mattermost you're installing.
MATTERMOST_IMAGE=mattermost-team-edition
## Update the image tag if you want to upgrade your Mattermost version. You may also upgrade to the latest one. The example is based on the latest Mattermost ESR version.
MATTERMOST_IMAGE_TAG=10.5.2

## Make Mattermost container readonly. This interferes with the regeneration of root.html inside the container. Only use
## it if you know what you're doing.
## See https://github.com/mattermost/docker/issues/18
MATTERMOST_CONTAINER_READONLY=false

## The app port is only relevant for using Mattermost without the nginx container as reverse proxy. This is not meant
## to be used with the internal HTTP server exposed but rather in case one wants to host several services on one host
## or for using it behind another existing reverse proxy.
APP_PORT=8065

## Configuration settings for Mattermost. Documentation on the variables and the settings itself can be found at
## https://docs.mattermost.com/administration/config-settings.html
## Keep in mind that variables set here will take precedence over the same setting in config.json. This includes
## the system console as well and settings set with env variables will be greyed out.

## Below one can find necessary settings to spin up the Mattermost container
MM_SQLSETTINGS_DRIVERNAME=postgres
MM_SQLSETTINGS_DATASOURCE=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable&connect_timeout=10

## Example settings (any additional setting added here also needs to be introduced in the docker-compose.yml)
MM_SERVICESETTINGS_SITEURL=http://${DOMAIN}

Moreover, ufw is disabled.

Expected behavior
No banner appearing

Observed behavior

Once logged in, after a while the red banner about the connection appears, and the logs of the container show:
{"timestamp":"2025-05-28 15:12:19.438 Z","level":"debug","msg":"URL Blocked because of CORS. Url: http://<IP>:8065","caller":"web/context.go:119","path":"/api/v4/websocket","request_id":"<ID>","ip_addr":"<CLIENT_IP>","user_id":"<ID>","method":"GET","err_where":"connect","http_code":400,"error":"connect: URL Blocked because of CORS. Url: http://<IP>:8065, websocket: request origin not allowed by Upgrader.CheckOrigin"}
{"timestamp":"2025-05-28 15:12:19.438 Z","level":"debug","msg":"Received HTTP request","caller":"web/handlers.go:185","method":"GET","url":"/api/v4/websocket","request_id":"<ID>","user_id":"<ID>","status_code":"400"}

Where:

  • <IP> is the ubuntu VM public IP,
  • <CLIENT_IP> is my client address.

Did you reference the variable in your docker-compose file, too?

If so, setting AllowCORS From to “*” works reliably. Probably not ideal from a security standpoint, but gives you a working baseline to fall back to. I remember looking into this about a year ago, trying to find out more about the parameters of what constitutes a valid statement in there (IP, mask, space or comma delimited, etc.)

First, thanks a lot for the quick answer!
Yes, as this environment variable:

is passed in the compose (attached, the environment variables of the mattermost service):

    environment:
      # timezone inside container
      - TZ

      # necessary Mattermost options/variables (see env.example)
      - MM_SQLSETTINGS_DRIVERNAME
      - MM_SQLSETTINGS_DATASOURCE

      # necessary for bleve
      - MM_BLEVESETTINGS_INDEXDIR

      # additional settings
      - MM_SERVICESETTINGS_SITEURL

I have two questions:

  1. What would be the implications of setting AllowCORS From?
  2. Can it be due to the fact that I am not deploying the version with SSL?

Essentially, it’s a list of domains your Mattermost server will accept cross-origin requests from. Integrations configuration settings - Mattermost documentation

And, no, this has nothing to do with TLS (I know lots of people say SSL, but SSL as a protocol was deprecated a very long time ago, and even TLS 1.0 and 1.1 are considered insecure now)

Thanks a lot, again!

So, if I have some IPs I know I can trust (of my clients), I could potentially accept only those?

Like "192.168.1.2,192.168.1.3,..." instead of *?

A qualified yes. The docs say to use spaces, and the last time this came up I recall there being some ambiguity about it. Please let us know what recipe winds up working for you!

Hi, thanks again for your patience.

Apologies for being late to reply to my own opened question, but I was a little bit busy last week, and I had to wait until the weekend to do further tests, and I solved the issue!

I will try to answer as shortly as possible, still being exhaustive.

Solution

In particular, by setting DOMAIN as in the following line of the .env file:

It is wrong to set (in the same .env file):

The correct way of setting it is:

MM_SERVICESETTINGS_SITEURL=http://${DOMAIN}:8065

No need of setting AllowCORS From to “*”

Why

As written in the documentation here,

The URL that users use to access Mattermost. The port number is required if it’s not a standard port, such as 80 or 443. This field is required.

As said in the same documentation paragraph, this can be set through MM_SERVICESETTINGS_SITEURL, and hence, since the default port on which I am serving it is the default one from the container, I also needed to specify :8065.

How I discovered this problem

As said from the documentation here, the system config path of Mattermost is visible at System Config path: Environment > Web Server from the webUI. At that page, when MM_SERVICESETTINGS_SITEURL was set wrongly to http://${DOMAIN}, the Test Live URL button returned an error. By setting instead MM_SERVICESETTINGS_SITEURL to http://${DOMAIN}:8065, the test became successful, and the banner did not appear again.

After changing the environment variable, I deleted volumes and re-created everything, not sure if changing it and restarting the containers would be enough.


Hope this will be useful for others who may encounter this problem! Thanks again for the patience!

1 Like