Introduction

GitLab Pages supports automatic Let's Encrypt certificate provisioning for custom domains. When certificate renewal fails -- due to DNS changes, rate limiting, or validation challenges -- the domain's HTTPS certificate expires, causing browser security warnings and broken page access for visitors.

Symptoms

  • GitLab Pages custom domain shows SSL certificate expired in browser
  • GitLab Pages settings show Certificate renewal failed
  • Visitors see NET::ERR_CERT_DATE_INVALID or similar browser warnings
  • Let's Encrypt rate limit reached, preventing immediate renewal
  • Error message: Failed to renew Let's Encrypt certificate: challenge failed

Common Causes

  • DNS A/CNAME record changed, breaking domain validation for ACME challenge
  • Let's Encrypt rate limit exceeded (5 certificates per domain per 7 days)
  • Custom domain not properly configured in GitLab Pages settings
  • GitLab Pages daemon not running or misconfigured for certificate management
  • Firewall blocking Let's Encrypt validation requests to the GitLab Pages server

Step-by-Step Fix

  1. 1.Check the certificate status in GitLab Pages settings: Verify the renewal state.
  2. 2.`
  3. 3.# GitLab UI: Project > Pages > Your custom domain
  4. 4.# Check certificate status and expiration date
  5. 5.# Look for error messages about the renewal failure
  6. 6.`
  7. 7.Verify DNS records point to the correct Pages server: Ensure domain validation works.
  8. 8.```bash
  9. 9.# Check A record
  10. 10.dig A mydomain.example.com +short
  11. 11.# Check CNAME (if using CNAME)
  12. 12.dig CNAME mydomain.example.com +short
  13. 13.# Should point to GitLab Pages IP or <username>.gitlab.io
  14. 14.`
  15. 15.Trigger manual certificate renewal: Force a new Let's Encrypt request.
  16. 16.```bash
  17. 17.# Via GitLab API
  18. 18.curl --request PUT --header "PRIVATE-TOKEN: $TOKEN" \
  19. 19."https://gitlab.example.com/api/v4/projects/$PROJECT_ID/pages/domains/mydomain.example.com" \
  20. 20.--form "certificate=" --form "key="
  21. 21.# This triggers automatic Let's Encrypt renewal
  22. 22.`
  23. 23.Check Let's Encrypt rate limit status: Verify you are not rate-limited.
  24. 24.```bash
  25. 25.# Check via Let's Encrypt API
  26. 26.curl -s https://letsencrypt.org/documents/rate-limits/
  27. 27.# If rate-limited, wait for the cooldown period (typically 1 hour)
  28. 28.`
  29. 29.Verify the new certificate is active: Confirm HTTPS is working.
  30. 30.```bash
  31. 31.echo | openssl s_client -connect mydomain.example.com:443 -servername mydomain.example.com 2>/dev/null | openssl x509 -noout -dates
  32. 32.`

Prevention

  • Monitor certificate expiration dates and alert 14 days before expiry
  • Ensure DNS records are stable and point to the correct GitLab Pages server
  • Use GitLab-managed certificates rather than manual uploads when possible
  • Document DNS requirements for GitLab Pages custom domains in the project README
  • Test certificate renewal in a staging project before applying to production domains
  • Keep Let's Encrypt rate limits in mind when making DNS changes that trigger re-validation