How to fix GitLab CE CORS issue on Synology
Posted in daily
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.

Docker Containers on Synololgy
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.

An error occured while fetching the tokens.
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 💜