GitLab Pages: DNS Configuration for Single-Domain Sites
At a glance#
- Purpose: Configure GitLab Pages to serve sites from a single domain with namespace-in-path URLs, including TLS.
- Applies to: Self-hosted GitLab (Omnibus) 16.7 and later.
- Risk: Medium —
gitlab-ctl reconfigurerestarts services; existing Pages URLs change format. - Time: About 45 minutes plus DNS propagation.
Overview#
GitLab Pages can serve project sites in one of two mutually exclusive modes:
| Mode | URL format | DNS requirement |
|---|---|---|
| Wildcard (default) | namespace.pages.example.com/project | Wildcard DNS record and wildcard TLS certificate |
| Single-domain | pages.example.com/namespace/project | One A record and one standard certificate |
Single-domain mode is the simpler option when you cannot obtain a wildcard certificate, or when the DNS provider does not support wildcard records.
Warning: These modes cannot run side by side. Enabling namespace_in_path disables wildcard-style access, and every existing Pages URL changes format. Anything linking to the old URLs — documentation, bookmarks, CI jobs — will break. Communicate the change before making it.
Before you start#
- Root access to the GitLab server.
- Ability to create DNS records for the domain.
- A TLS certificate for the Pages domain, if serving over HTTPS.
- A maintenance window —
gitlab-ctl reconfigurerestarts GitLab services.
Back up the configuration first:
sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak-$(date +%F)Procedure#
1. Create the DNS A record#
At your DNS provider, add an A record pointing the Pages domain at the GitLab Pages instance:
pages.example.com. 1800 IN A 192.0.2.10Replace pages.example.com with your domain and 192.0.2.10 with the actual IP address of the Pages instance. If Pages runs on the same host as GitLab, this is the GitLab server's IP.
Confirm it resolves before continuing:
dig +short pages.example.com2. Enable namespace-in-path#
sudo nano /etc/gitlab/gitlab.rbgitlab_pages['namespace_in_path'] = true3. Install the TLS certificate#
Place the certificate and key in /etc/gitlab/ssl/. GitLab looks for filenames matching the Pages domain by default:
sudo mkdir -p /etc/gitlab/ssl
sudo cp pages.example.com.crt /etc/gitlab/ssl/
sudo cp pages.example.com.key /etc/gitlab/ssl/
sudo chmod 600 /etc/gitlab/ssl/pages.example.com.key
sudo chmod 644 /etc/gitlab/ssl/pages.example.com.crtThe .crt file must contain the full chain — your certificate followed by any intermediates. See Installing a Sectigo SSL Certificate on Self-Hosted GitLab.
4. Configure Pages with TLS#
In /etc/gitlab/gitlab.rb:
external_url "https://gitlab.example.com"
pages_external_url "https://pages.example.com"
pages_nginx['redirect_http_to_https'] = true
gitlab_pages['namespace_in_path'] = trueWarning:pages_external_urlmust not be a subdomain ofexternal_url. If GitLab is atgitlab.example.com, Pages cannot be atpages.gitlab.example.com. Use a sibling domain such aspages.example.com. This is a hard requirement — sharing a parent domain allows Pages content to set cookies for the GitLab instance, which is a security problem.
5. Point at custom certificate filenames (optional)#
Only needed if the files are not named after the domain:
pages_nginx['ssl_certificate'] = "/etc/gitlab/ssl/pages-nginx.crt"
pages_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/pages-nginx.key"6. Update OAuth settings if access control is enabled#
Only applies if GitLab Pages access control is in use.
- In the GitLab admin area, update the OAuth redirect URI to use HTTPS:
https://pages.example.com/projects/auth- Remove the stale
gitlab_pagesblock so the secret is regenerated:
sudo cp /etc/gitlab/gitlab-secrets.json /etc/gitlab/gitlab-secrets.json.bak
sudo nano /etc/gitlab/gitlab-secrets.jsonDelete the gitlab_pages section, then save.
Note: Back up gitlab-secrets.json before editing. It holds encryption keys for the whole instance — a mistake here affects far more than Pages.
7. Apply the configuration#
sudo gitlab-ctl reconfigureThis takes several minutes and restarts services. Watch for errors in the output.
Verification#
Confirm services are healthy:
sudo gitlab-ctl statusCheck that Pages is listening:
sudo ss -tulpn | grep pagesTest the certificate:
openssl s_client -connect pages.example.com:443 -servername pages.example.comLook for Verify return code: 0 (ok).
Then load an actual Pages site in a browser. The URL format is now:
https://pages.example.com/<namespace>/<project_slug>Confirm HTTP redirects to HTTPS:
curl -I http://pages.example.comThis should return 301 with an https:// Location header.
Troubleshooting#
| Symptom | Cause and fix |
|---|---|
| 404 on every Pages site | namespace_in_path set but sites not yet redeployed. Re-run the Pages CI job for the project. |
pages_external_url cannot be a subdomain of external_url | Exactly what it says — change the Pages domain to a sibling rather than a child. |
| Certificate errors in the browser | Chain incomplete, or filenames do not match the domain. Set the paths explicitly as in step 5. |
| OAuth login loops | Redirect URI still on HTTP, or the stale secret was not removed. Repeat step 6. |
gitlab-ctl reconfigure fails | Ruby syntax error in gitlab.rb. The output names the line. Restore the backup if needed. |
| Old wildcard URLs stopped working | Expected — the modes are mutually exclusive. Update any links. |