How to fix GitLab CE CORS issue on Synology 

Tags :

TL;DR; After upgrading GitLab CE on my Synology NAS to v18.2.1, the Access Tokens page failed due to CORS errors from mixed-origin requests and port mismatches (http API calls and https on port 80). The fix was to set the correct external_url, disable HTTPS in GitLab’s internal NGINX (TLS terminated at Synology), define trusted proxy CIDRs, and add X-Forwarded-* headers in both GitLab and Synology’s reverse proxy. This ensured all browser calls used https://ofuscated.domain.com without :80, resolving the CORS issue.

→ Make sure you replace ofuscated.domain.com by your own domain name in the code and settings below.

Leaving Bitbucket

When Bitbucket introduced a 1 GB cap on free plan workspace storage, I initially considered upgrading to a paid plan. The advertised price of USD 3.30 per user for 5 GB of storage seemed reasonable—until I discovered the 5-user minimum, bringing the actual cost to USD 16.50/month.

As I work alone, and my active subscriptions have grown significantly in recent years, I opted for a self-hosted alternative: GitLab Community Edition.

Self hosting GitLab

I run GitLab CE on a Synology NAS using Docker (via Synology’s Container Manager Registry). Installation tutorials for Synology are widely available, so I will not detail that process here. My setup uses a custom domain and a reverse proxy to allow access from multiple locations.

It all worked hunky dory for weeks until I attempted to create a new access token for Tower after upgrading to GitLab v18.2.1. The User Settings → Access Tokens page failed to fetch existing tokens or save new ones. The browser console indicated a CORS problem.

Not allowed to request resource
ⓧ XMLHttpRequest cannot load http://ofuscated.domain.com/api/v4/personal_access_tokens?user_id=34&sort=expires_asc&page=1&state=active due to access control checks.
Origin https://ofuscated.domain.com is not allowed by Access-Control-Allow-Origin. Status code: 200
ⓧ XMLHttpRequest cannot load https://ofuscated.domain.com:80/-/collect_events due to access control checks.
Failed to load resource: Origin https://ofuscated.domain.com is not allowed by Access-Control-Allow-Origin. Status code: 200
Origin https://ofuscated.domain.com is not allowed by Access-Control-Allow-Origin. Status code: 200
ⓧ XMLHttpRequest cannot load https://ofuscated.domain.com:80/-/collect_events due to access control checks.
Failed to load resource: Origin https://ofuscated.domain.com is not allowed by Access-Control-Allow-Origin. Status code: 200

Note: this issue and part of the solution were reported elsewhere on Reddit and Gitlab Forums.

Cause

The issue was due to mixed-origin and port mismatches:

Request URL Issue Result
http://ofuscated.domain.com/api/… Mixed content CORS blocked
https://ofuscated.domain.com:80/-/collect_events HTTPS on port 80 CORS blocked

Both errors pointed to an incorrect external_url and telemetry (Snowplow) port in the GitLab configuration.

As a web developer I have encountered CORS errors before, but I’m still new to docker images management. Anyway, I found a solution after a couple of hours Kagi search and chatting with Janet (my LLM of the moment).

Solution

In my case, the fix required updates to both GitLab’s configuration and the Synology reverse proxy.

1/ GitLab configuration (/etc/gitlab/gitlab.rb)

My copy of gitlab.rb is located in [volume]/docker/gitlab/config/gitlab.rb

# Public URL for browser access
external_url "https://ofuscated.domain.com"

# GitLab internal NGINX (TLS termination handled by Synology)
nginx['listen_https'] = false
nginx['listen_port']  = 80

# Trusted proxies
gitlab_rails['trusted_proxies']     = ['192.168.0.0/24']
gitlab_workhorse['trusted_proxies'] = ['172.17.0.0/24']

# Forward correct scheme/host from proxy
nginx['proxy_set_headers'] = {
  "Host"              => "$http_host",
  "X-Real-IP"         => "$remote_addr",
  "X-Forwarded-For"   => "$proxy_add_x_forwarded_for",
  "X-Forwarded-Proto" => "https",
  "X-Forwarded-Ssl"   => "on",
  "X-Forwarded-Host"  => "ofuscated.domain.com"
}

# Optional: disable Snowplow
gitlab_rails['snowplow_enabled'] = false

2/ Synology reverse proxy settings

Go to Control Panel → Login Portal → Reverse Proxy

Setting Value
Source https://ofuscated.domain.com:443
Destination http://localhost:8080
WebSocket Enabled
Custom headers
X-Forwarded-Proto https
X-Forwarded-Ssl on
X-Forwarded-Host ofuscated.domain.com

Validation

From the NAS shell, hit the container’s HTTP:

curl -I http://127.0.0.1:8080/-/health/

From a client, bypass Cloudflare to origin:

curl -Ik --resolve ofuscated.domain.com:443:[origin_public_ip] https://ofuscated.domain.com/users/sign_in

→ Replace [ origin_public_ip ] with the origin IP address of your server.

From inside the container:

docker exec -it [gitlab] bash -lc "netstat -tln | grep -E ':80|:8080'"

→ Replace [ gitlab ] with your own container name.

When aligned, /users/sign_in returns 200/302 and calls to /api/v4/... are over https://ofuscated.domain.comwith no :80.

Hope this helps 💜

Posted a response ? — Webmention it

This site uses webmentions. If you've posted a response and need to manually notify me, you can enter the URL of your response below.

Want more ? — prev/next entries