Skip to content

gps-license: GetPageSpeed license and update-telemetry module

Installation

You can install this module in any RHEL-based distribution, including, but not limited to:

  • RedHat Enterprise Linux 7, 8, 9 and 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 and Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-gps-license
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-gps-license

Enable the module by adding the following at the top of /etc/nginx/nginx.conf:

load_module modules/ngx_http_gps_license_module.so;

This document describes nginx-module-gps-license v1.11.0 released on Oct 29 2026.


GetPageSpeed License Module for nginx. Validates JWT-based license tokens, reports usage telemetry, and exposes license state via nginx variables.

Overview

This module provides license validation for nginx-mod packages distributed via GetPageSpeed. It supports:

  • JWT token validation with RS256 signatures
  • Shared memory caching of license state
  • Grace period for expired licenses (90 days)
  • Soft/footer/hard enforcement modes
  • nginx variables for license status

Note: Usage telemetry reporting is planned for a future version.

Configuration

Directives

gps_license_zone

Syntax: gps_license_zone zone=name:size Context: http Default: —

Defines shared memory zone for caching license state.

http {
    gps_license_zone zone=license:1m;
}

gps_license

Syntax: gps_license on | off Context: http, server, location Default: off

Enables license checking for requests.

gps_license_token

Syntax: gps_license_token path | env:VAR_NAME Context: http Default: /etc/getpagespeed/license.jwt, then /etc/nginx/license.jwt

Path to JWT token file or environment variable name.

When the directive is absent, the module looks for the canonical /etc/getpagespeed/license.jwt first (one file licenses every GetPageSpeed product on the host — NGINX, Angie, PHP), then falls back to the legacy /etc/nginx/license.jwt, which keeps working forever. An explicit directive always wins over both.

gps_license_token /etc/getpagespeed/license.jwt;
## OR
gps_license_token env:GPS_LICENSE_TOKEN;

gps_license_enforcement

Syntax: gps_license_enforcement soft | footer | hard Context: http, server, location Default: soft

Enforcement mode: - soft: Log warnings, continue serving traffic - footer: Continue serving traffic, but append a visible GetPageSpeed notice to unlicensed text/html responses (free-trial watermark). The footer disappears as soon as a valid license token is installed. Responses that are pre-compressed upstream (Content-Encoding set), non-HTML, non-200, or subrequests are never touched. Locally produced gzip is fine — the filter runs before the gzip filter. - hard: Return 503 with error details

The backend report response may override the configured mode (field enforcement: "soft" | "footer" | "hard"); a backend-pushed value takes precedence over local config until the next restart.

gps_license_report_endpoint

Syntax: gps_license_report_endpoint url Context: http Default: https://www.getpagespeed.com/wp-json/getpagespeed/v1/license/report

URL for usage reporting (not yet implemented).

gps_license_report_interval

Syntax: gps_license_report_interval time Context: http Default: 1h

Interval between usage reports (not yet implemented).

gps_license_grace_period

Syntax: gps_license_grace_period time Context: http Default: 90d

Grace period after license expiration.

Variables

Variable Description
$gps_license_valid "1" if valid or in grace, "0" otherwise
$gps_license_tier License tier ("ultra", "plus", "standard")
$gps_license_expires Expiration timestamp
$gps_license_grace "1" if in grace period, "0" otherwise
$gps_license_enforcement Current enforcement mode

Example Configuration

http {
    gps_license_zone zone=license:1m;

    server {
        listen 80;
        server_name example.com;

        gps_license on;
        gps_license_token /etc/getpagespeed/license.jwt;
        gps_license_enforcement soft;

        # Log license state
        log_format license '$remote_addr - $gps_license_tier [$gps_license_valid]';

        location / {
            root /var/www/html;

            # Add license header for debugging
            add_header X-License-Tier $gps_license_tier;
        }
    }
}

Error Responses

When gps_license_enforcement hard and license is invalid:

HTTP 503 with headers: - X-GPS-License-Error: missing|invalid|expired|revoked

Response body:

GetPageSpeed License Error: expired

Your nginx-mod license is not valid.
Visit https://www.getpagespeed.com/repo-subscribe to get a license.

JWT Token Format

Tokens are signed with RS256 and contain:

{
  "iss": "getpagespeed.com",
  "aud": "nginx-mod",
  "sub": "I-XXXXXXXXX",
  "exp": 1234567890,
  "tier": "ultra",
  "jti": "uuid-v4",
  "client_id": 12345,
  "features": {
    "reposync_allowed": true,
    "pro_modules_allowed": true
  }
}

features.pro_modules_allowed (v1.13.0) is the Pro-module runtime entitlement: the module scans the loaded nginx modules against a baked-in list of GetPageSpeed Pro modules (generated from the nginx-extras plan: pro manifests) and mirrors the claim into shared memory. In the current shadow phase an unentitled install with Pro modules loaded gets a single startup [warn] plus pro_modules/pro_entitled fields in the usage heartbeat — serving and enforcement are never affected. Tokens minted before the claim existed parse as "unknown" and are never treated as unentitled.

Public Key

The RSA public key is embedded at compile time. Key rotation requires module rebuild.

Important Notes

nginx Request Phases

The license module runs in the ACCESS_PHASE. Some nginx directives run in earlier phases and will bypass license checking:

  • return and rewrite ... last run in REWRITE_PHASE (before ACCESS_PHASE)
  • error_page redirects may bypass the check

For license enforcement to work, use content handlers like proxy_pass, fastcgi_pass, try_files, or static file serving with root/alias.

## WRONG - return runs before license check
location / {
    gps_license on;
    gps_license_enforcement hard;
    return 200 "OK";  # Bypasses license check!
}

## CORRECT - static files trigger ACCESS_PHASE
location / {
    gps_license on;
    gps_license_enforcement hard;
    root /var/www/html;
}

## CORRECT - proxy_pass triggers ACCESS_PHASE
location / {
    gps_license on;
    gps_license_enforcement hard;
    proxy_pass http://backend;
}

gps_license_token Location

The gps_license_token directive must be in the http {} block (not in server or location blocks) because license validation happens once at worker startup, not per-request.