Files
gh-lucacri-claude-gitlab-do…/skills/gitlab/references/gitlab-pages.md
2025-11-30 08:38:11 +08:00

12 KiB

GitLab Pages Reference

Overview

GitLab Pages allows you to host static websites directly from your GitLab repository. Perfect for documentation, portfolios, blogs, and landing pages.

Pages URL Format

GitLab.com:

https://username.gitlab.io/project-name

Custom domain:

https://www.example.com

Group pages:

https://groupname.gitlab.io

Creating a Pages Site

Basic Setup

.gitlab-ci.yml:

pages:
  stage: deploy
  script:
    - mkdir .public
    - cp -r * .public
    - mv .public public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Requirements:

  1. Job must be named pages
  2. Must create public/ directory
  3. Must have artifact with public/ path
  4. Runs on default branch (usually main)

Static HTML

index.html:

<!DOCTYPE html>
<html>
<head>
    <title>My GitLab Page</title>
</head>
<body>
    <h1>Welcome!</h1>
    <p>This is hosted on GitLab Pages</p>
</body>
</html>

.gitlab-ci.yml:

pages:
  stage: deploy
  script:
    - mkdir public
    - cp index.html public/
  artifacts:
    paths:
      - public
  only:
    - main

Static Site Generators

Hugo

image: registry.gitlab.com/pages/hugo:latest

variables:
  GIT_SUBMODULE_STRATEGY: recursive

pages:
  script:
    - hugo
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Jekyll

image: ruby:2.7

before_script:
  - bundle install

pages:
  script:
    - bundle exec jekyll build -d public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Gatsby

image: node:18

cache:
  paths:
    - node_modules/
    - .cache/

pages:
  script:
    - npm install
    - npm run build
    - mv public public-gatsby
    - mv public-gatsby public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Next.js

image: node:18

cache:
  paths:
    - node_modules/
    - .next/cache/

pages:
  script:
    - npm install
    - npm run build
    - npm run export
    - mv out public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

VuePress

image: node:18

pages:
  script:
    - npm install
    - npm run docs:build
    - mv docs/.vuepress/dist public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

MkDocs

image: python:3.11

pages:
  script:
    - pip install mkdocs mkdocs-material
    - mkdocs build
    - mv site public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Docusaurus

image: node:18

pages:
  script:
    - npm install
    - npm run build
    - mv build public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Hexo

image: node:18

pages:
  script:
    - npm install hexo-cli -g
    - npm install
    - hexo generate
    - mv public public-hexo
    - mv public-hexo public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Custom Domain Setup

Add Custom Domain

Via UI:

  1. Settings > Pages
  2. New Domain
  3. Enter domain name
  4. Save

Via API:

curl --request POST --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.com/api/v4/projects/:id/pages/domains" \
  --data "domain=www.example.com"

DNS Configuration

For root domain (example.com):

Type: A
Name: @
Value: 35.185.44.232

For subdomain (www.example.com):

Type: CNAME
Name: www
Value: username.gitlab.io

Verify DNS:

dig www.example.com
nslookup www.example.com

SSL/TLS Certificate

Automatic Let's Encrypt:

  1. Add custom domain
  2. Configure DNS
  3. Enable "Automatic certificate management using Let's Encrypt"

Manual certificate:

curl --request PUT --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.com/api/v4/projects/:id/pages/domains/www.example.com" \
  --form "certificate=@/path/to/cert.pem" \
  --form "key=@/path/to/key.pem"

Access Control

Private Pages

Require authentication to access pages (Premium/Ultimate):

Enable private pages:

curl --request PUT --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.com/api/v4/projects/:id" \
  --data "pages_access_level=private"

Access levels:

  • public - Anyone can access
  • private - Only project members
  • enabled - Controlled by project visibility
  • disabled - Pages disabled

Authentication

When pages are private:

  • Users must log in to GitLab
  • Project members can access
  • Access controlled by member permissions

Preview Environments

Create preview for merge requests:

pages:
  stage: deploy
  script:
    - npm run build
    - mv dist public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

pages:preview:
  stage: deploy
  script:
    - npm run build
    - mv dist public
  artifacts:
    paths:
      - public
  environment:
    name: review/$CI_COMMIT_REF_NAME
    url: https://$CI_PROJECT_ROOT_NAMESPACE.gitlab.io/-/$CI_PROJECT_NAME/-/jobs/$CI_JOB_ID/artifacts/public/index.html
  rules:
    - if: $CI_MERGE_REQUEST_ID

Advanced Configuration

Custom 404 Page

Create public/404.html:

<!DOCTYPE html>
<html>
<head>
    <title>Page Not Found</title>
</head>
<body>
    <h1>404 - Page Not Found</h1>
    <p>The page you're looking for doesn't exist.</p>
    <a href="/">Go Home</a>
</body>
</html>

Redirects

Create public/_redirects:

# Redirect /old-page to /new-page
/old-page /new-page 301

# Redirect with wildcards
/blog/* /news/:splat 301

# Conditional redirects
/api/* https://api.example.com/:splat 200

# Country-based redirects
/  /us  302  Country=us
/  /uk  302  Country=uk

Headers

Create public/_headers:

# Security headers
/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  X-XSS-Protection: 1; mode=block
  Referrer-Policy: no-referrer-when-downgrade

# Cache control
/static/*
  Cache-Control: public, max-age=31536000, immutable

# CORS
/api/*
  Access-Control-Allow-Origin: *
  Access-Control-Allow-Methods: GET, POST, OPTIONS

Base URL Configuration

For subdirectory deployment:

Hugo:

# config.toml
baseURL = "https://username.gitlab.io/project-name/"

Jekyll:

# _config.yml
baseurl: "/project-name"
url: "https://username.gitlab.io"

VuePress:

// .vuepress/config.js
module.exports = {
  base: '/project-name/',
}

Optimization

Image Optimization

pages:
  before_script:
    - npm install -g sharp-cli
  script:
    - |
      find public -name '*.jpg' -o -name '*.png' | while read img; do
        sharp -i "$img" -o "$img" resize 1920
      done

Minification

pages:
  script:
    - npm run build
    - npm install -g html-minifier clean-css-cli uglify-js
    - |
      find public -name '*.html' -exec html-minifier --collapse-whitespace --remove-comments --minify-css --minify-js {} -o {} \;
      find public -name '*.css' -exec cleancss -o {} {} \;
      find public -name '*.js' -exec uglifyjs {} -c -m -o {} \;

Compression

pages:
  script:
    - npm run build
    - mv dist public
    - find public -type f -regex '.*\.\(htm\|html\|txt\|text\|js\|css\|svg\|json\)$' -exec gzip -f -k {} \;
    - find public -type f -regex '.*\.\(htm\|html\|txt\|text\|js\|css\|svg\|json\)$' -exec brotli -f -k {} \;

CI/CD Examples

Multi-Branch Deployment

pages:
  stage: deploy
  script:
    - npm run build
    - mv dist public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

pages:staging:
  stage: deploy
  script:
    - npm run build
    - mv dist public
  artifacts:
    paths:
      - public
  environment:
    name: staging
    url: https://staging.example.com
  rules:
    - if: $CI_COMMIT_BRANCH == "staging"

Conditional Deployment

pages:
  stage: deploy
  script:
    - |
      if [ "$CI_COMMIT_BRANCH" == "main" ]; then
        export NODE_ENV=production
      else
        export NODE_ENV=development
      fi
    - npm run build
    - mv dist public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH

Scheduled Rebuild

pages:
  script:
    - npm run build
    - mv dist public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
    - if: $CI_PIPELINE_SOURCE == "schedule"

Monitoring

Analytics

Add Google Analytics:

<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'GA_MEASUREMENT_ID');
</script>

Status Checks

test:pages:
  stage: test
  script:
    - npm run build
    - npm run test
    - |
      # Check for broken links
      npm install -g broken-link-checker
      blc http://localhost:8000 -ro

Troubleshooting

Pages Not Updating

  1. Check pipeline succeeded
  2. Verify pages job ran
  3. Check artifacts were created
  4. Clear browser cache
  5. Wait for CDN propagation (up to 30 minutes)

404 Errors

  1. Verify file exists in public/ directory
  2. Check file paths (case-sensitive)
  3. Verify artifact includes files
  4. Check baseURL configuration

Custom Domain Issues

  1. Verify DNS configuration: dig www.example.com
  2. Check SSL certificate status
  3. Ensure domain verified in GitLab
  4. Check domain registrar settings
  5. Wait for DNS propagation (up to 48 hours)

Build Failures

pages:
  script:
    - set -e  # Exit on error
    - npm install
    - npm run build || (echo "Build failed" && exit 1)
    - mv dist public
  artifacts:
    paths:
      - public
    when: on_success

Best Practices

1. Performance

  • Optimize images
  • Minify CSS/JS
  • Enable compression
  • Use CDN
  • Implement caching

2. Security

  • Use HTTPS (automatic with Let's Encrypt)
  • Set security headers
  • Validate user input (if using forms)
  • Keep dependencies updated

3. SEO

<meta name="description" content="Site description">
<meta name="keywords" content="keywords, here">
<meta property="og:title" content="Page Title">
<meta property="og:description" content="Description">
<meta property="og:image" content="https://example.com/image.jpg">
<meta name="twitter:card" content="summary_large_image">

<!-- Robots -->
<meta name="robots" content="index, follow">

<!-- Canonical URL -->
<link rel="canonical" href="https://example.com/page">

4. Accessibility

  • Use semantic HTML
  • Add alt text to images
  • Ensure keyboard navigation
  • Maintain color contrast
  • Test with screen readers

5. Version Control

pages:
  script:
    - echo "Build $CI_COMMIT_SHA on $(date)" > public/version.txt
    - npm run build

API Reference

Get Pages

curl --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.com/api/v4/projects/:id/pages"

Delete Pages

curl --request DELETE --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.com/api/v4/projects/:id/pages"

List Domains

curl --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.com/api/v4/projects/:id/pages/domains"

Add Domain

curl --request POST --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.com/api/v4/projects/:id/pages/domains" \
  --data "domain=www.example.com" \
  --data "auto_ssl_enabled=true"

Update Domain

curl --request PUT --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.com/api/v4/projects/:id/pages/domains/www.example.com" \
  --data "auto_ssl_enabled=true"

Delete Domain

curl --request DELETE --header "PRIVATE-TOKEN: <token>" \
  "https://gitlab.com/api/v4/projects/:id/pages/domains/www.example.com"

Examples Repository

GitLab provides example pages projects:

Additional Resources