Skip to main content

API Reference

Technical reference for ModPageSpeed 2.0 protocols and interfaces.

This page documents the internal protocols and interfaces of ModPageSpeed 2.0: the IPC wire format between nginx and the worker, the health check endpoint, the management socket protocol, and the capability mask encoding.

IPC Wire Format

The nginx module sends fire-and-forget notifications to the Factory Worker over a Unix socket. There is no response — the worker reads the notification, processes the content from the shared cache, and writes optimized variants back.

Message Structure

Each message consists of a 4-byte length header followed by the payload:

[4 bytes: payload_length (big-endian uint32)]
[4 bytes: url_length (big-endian uint32)]
[url_length bytes: URL string]
[4 bytes: hostname_length (big-endian uint32)]
[hostname_length bytes: hostname string]
[1 byte: content_type]
[4 bytes: capability_mask (big-endian uint32)]

The payload_length field does not include itself — it is the total size of everything after the first 4 bytes. The hostname is required for cache key computation (SHA-256(URL, hostname)).

Content Type Values

ValueType
0HTML
1CSS
2JavaScript
3Image
4Other

Example

For URL /style.css (10 bytes), hostname localhost (9 bytes), content type CSS (0x02), capability mask 0x00000008 (Desktop/Identity):

00 00 00 20          # payload_length = 32 bytes
00 00 00 0A          # url_length = 10
2F 73 74 79 6C 65 2E 63 73 73  # "/style.css"
00 00 00 09          # hostname_length = 9
6C 6F 63 61 6C 68 6F 73 74     # "localhost"
02                   # content_type = CSS
00 00 00 08          # capability_mask = 0x08

Connection Lifecycle

  1. Nginx opens a new Unix socket connection for each notification.
  2. Nginx writes the serialized message.
  3. Nginx closes the connection (fire-and-forget).
  4. On connection failure (ECONNREFUSED, ENOENT), nginx retries with exponential backoff: 2 retries, 10ms initial delay, 3x multiplier (10ms, 30ms).
  5. If all retries fail, the notification is dropped. The cache serves the original content. Subsequent requests for the same URL re-notify.

Health Endpoint

The worker listens on a Unix socket at {socket_path}.health. On connection, it immediately writes a status line and closes.

Socket Path

If the worker socket is /var/lib/pagespeed/pagespeed.sock, the health socket is /var/lib/pagespeed/pagespeed.sock.health.

Response Format

OK {active_connections}/{max_connections} notifs={N} variants={N} proactive={N} errors={N} cache_entries={N}\n

Fields

FieldDescription
active_connectionsNumber of currently active client connections
max_connectionsConfigured maximum connections (--max-connections)
notifsTotal notifications received from nginx
variantsTotal optimized variants written to cache
proactiveProactively generated sibling variants
errorsTotal processing errors
cache_entriesCurrent number of entries in the Cyclone cache

Example

OK 3/128 notifs=4821 variants=2106 proactive=1580 errors=7 cache_entries=3412

Usage

python3 -c "
import socket
s = socket.socket(socket.AF_UNIX)
s.connect('/var/lib/pagespeed/pagespeed.sock.health')
print(s.recv(256).decode())
s.close()
"

The health endpoint is designed for automated health checks (Docker HEALTHCHECK, Kubernetes liveness probes, load balancer checks). It accepts a connection, writes the response, and closes. No request data is needed.

Management Socket Protocol

The worker listens on a Unix socket at {socket_path}.mgmt for newline-terminated text commands. After processing a command, the worker writes a response and closes the connection.

Socket Path

If the worker socket is /var/lib/pagespeed/pagespeed.sock, the management socket is /var/lib/pagespeed/pagespeed.sock.mgmt.

Commands

STATS

Returns a JSON object with comprehensive worker statistics.

Request: STATS\n

Response:

{
  "status": "ok",
  "connections": {
    "active": 2,
    "max": 128
  },
  "notifications": {
    "received": 4821,
    "skipped_dedup": 1200
  },
  "variants": {
    "written": 2106,
    "proactive": 1580
  },
  "errors": 7,
  "cache": {
    "entries": 3412,
    "size": 268435456
  },
  "by_type": {
    "html": { "n": 120, "us": 450000 },
    "css": { "n": 340, "us": 180000 },
    "js": { "n": 280, "us": 150000 },
    "images": { "n": 1366, "us": 28500000 }
  },
  "by_format": {
    "webp": 580,
    "avif": 490,
    "jpeg": 180,
    "png": 116
  },
  "timing_us": {
    "total": 29280000
  }
}

Field descriptions:

FieldDescription
connections.activeCurrently open client connections
connections.maxConfigured --max-connections
notifications.receivedTotal notifications from nginx
notifications.skipped_dedupSkipped because variant already exists
variants.writtenTotal optimized variants written to cache
variants.proactiveSubset of written that were sibling variants
errorsTotal processing failures
cache.entriesCurrent number of cache entries
cache.sizeCurrent cache size in bytes
by_type.{type}.nNumber of items processed for this content type
by_type.{type}.usCumulative processing time in microseconds
by_format.{format}Number of variants generated per image format
timing_us.totalCumulative processing time across all types

PURGE <hostname> <url>

Deletes all cached variants for the specified URL and hostname.

Request: PURGE example.com /images/photo.jpg\n

Response: OK N entries deleted\n

The PURGE command iterates all possible capability mask combinations (image formats, viewports, pixel densities, Save-Data states, transfer encodings) and deletes every cache entry for the URL. It also deletes sentinel entries used for Early Hints (0xFFFFFFFF) and warmup (0xFFFFFFFE).

The maximum number of entries deleted per URL is 192 (4 formats x 3 viewports x 2 densities x 2 Save-Data x 4 encodings) plus 2 sentinel entries = 194.

AUTH <token>

Authenticates the connection for PURGE commands. Required when the PAGESPEED_PURGE_TOKEN environment variable is set on the worker.

Request: AUTH my-secret-token\n

Response: OK\n on success, or ERR invalid token\n on failure.

Send AUTH as the first command on each connection before issuing PURGE. The token is compared using constant-time comparison to prevent timing attacks. STATS and METRICS commands work without authentication regardless of whether the env var is set.

When PAGESPEED_PURGE_TOKEN is not set, PURGE works without authentication (backward compatible).

METRICS

Returns worker statistics in Prometheus text exposition format. This enables direct Prometheus scraping via a simple exporter script.

Request: METRICS\n

Response:

# HELP pagespeed_notifications_total Total notifications received.
# TYPE pagespeed_notifications_total counter
pagespeed_notifications_total 4821
# HELP pagespeed_notifications_skipped_total Notifications skipped (dedup).
# TYPE pagespeed_notifications_skipped_total counter
pagespeed_notifications_skipped_total 1200
# HELP pagespeed_variants_written_total Total variants written to cache.
# TYPE pagespeed_variants_written_total counter
pagespeed_variants_written_total 2106
# HELP pagespeed_proactive_variants_total Proactively generated variants.
# TYPE pagespeed_proactive_variants_total counter
pagespeed_proactive_variants_total 1580
# HELP pagespeed_errors_total Total processing errors.
# TYPE pagespeed_errors_total counter
pagespeed_errors_total 7
# HELP pagespeed_processing_seconds_total Cumulative processing time in seconds.
# TYPE pagespeed_processing_seconds_total counter
pagespeed_processing_seconds_total{type="html"} 0.450000
pagespeed_processing_seconds_total{type="css"} 0.180000
pagespeed_processing_seconds_total{type="js"} 0.150000
pagespeed_processing_seconds_total{type="image"} 28.500000
# HELP pagespeed_format_generated_total Variants generated by image format.
# TYPE pagespeed_format_generated_total counter
pagespeed_format_generated_total{format="webp"} 580
pagespeed_format_generated_total{format="avif"} 490
pagespeed_format_generated_total{format="jpeg"} 180
pagespeed_format_generated_total{format="png"} 116
# HELP pagespeed_cache_entries Current number of cache entries.
# TYPE pagespeed_cache_entries gauge
pagespeed_cache_entries 3412
# HELP pagespeed_cache_bytes Current cache size in bytes.
# TYPE pagespeed_cache_bytes gauge
pagespeed_cache_bytes 268435456
# HELP pagespeed_connections_active Currently active connections.
# TYPE pagespeed_connections_active gauge
pagespeed_connections_active 3
# HELP pagespeed_connections_max Maximum allowed connections.
# TYPE pagespeed_connections_max gauge
pagespeed_connections_max 128

Prometheus scraping example:

# One-liner to scrape metrics via socat
echo "METRICS" | socat -t 5 - UNIX-CONNECT:/var/lib/pagespeed/pagespeed.sock.mgmt

For a Prometheus exporter, use a simple script that connects to the management socket and exposes the output on an HTTP endpoint.

BROWSER-STATUS

Returns a JSON object with browser analysis status. If browser analysis is not enabled, returns {"enabled":false}.

Request: BROWSER-STATUS\n

Response:

{
  "enabled": true,
  "chrome_running": true,
  "chrome_pid": 12345,
  "chrome_rss_mb": 180,
  "queue_depth": 2,
  "analysis_in_progress": true,
  "templates_tracked": 14,
  "profiles_generated": 87,
  "profiles_used": 62,
  "analysis_errors": 3,
  "chrome_restarts": 1,
  "chrome_crashes": 0,
  "queue_enqueued": 120,
  "queue_dropped": 5,
  "queue_processed": 113,
  "css_inlining_attempted": 87,
  "css_inlining_stylesheets_found": 340,
  "css_inlining_stylesheets_cached": 298,
  "css_inlining_bytes_inlined": 145200,
  "reanalyses_scheduled": 12,
  "scripts_analyzed": 156,
  "scripts_deferrable": 42
}
FieldDescription
chrome_runningWhether the managed Chrome instance is alive
chrome_pidOS process ID of Chrome (0 if not running)
chrome_rss_mbChrome resident memory in megabytes
queue_depthURLs currently waiting for analysis
analysis_in_progressWhether an analysis is actively running
templates_trackedNumber of page templates detected
profiles_generatedTotal CSS coverage profiles completed
profiles_usedProfiles that resulted in critical CSS injection
analysis_errorsTotal analysis failures
chrome_restartsTimes Chrome was restarted (planned)
chrome_crashesTimes Chrome crashed unexpectedly
queue_enqueued / queue_dropped / queue_processedQueue lifecycle counters
css_inlining_*Critical CSS inlining statistics
reanalyses_scheduledPages queued for re-analysis (e.g., after CSS changes)
scripts_analyzed / scripts_deferrableScript deferral analysis counters

Unknown Commands

Any command other than AUTH, STATS, PURGE, METRICS, or BROWSER-STATUS returns:

ERR unknown command\n

Connection Safety

The management socket disconnects clients whose buffer exceeds 16 KB without containing a newline. This prevents denial-of-service from malformed input.

Capability Mask Encoding

The 32-bit capability mask encodes client characteristics as a bitmask. Only the lowest 8 bits are used; bits 8-31 are reserved and must be zero (except for sentinel values).

Bit Layout

Bit:    7    6    5    4    3    2    1    0
      +----+----+----+----+----+----+----+----+
      |  Transfer  |Save| Px |  Viewport | Format |
      |  Encoding  |Data|Den |   Class   |        |
      +----+----+----+----+----+----+----+----+

Field Definitions

BitsFieldWidthValues
0-1Image Format200 Original, 01 WebP, 10 AVIF, 11 SVG
2-3Viewport Class200 Mobile, 01 Tablet, 10 Desktop
4Pixel Density10 1x, 1 2x+ (Retina)
5Save-Data10 off, 1 on
6-7Transfer Encoding200 Identity, 01 Gzip, 10 Brotli, 11 Reserved

Common Mask Values

MaskHexDescription
0x080x08Default: Desktop, Identity, Original format, 1x, no Save-Data
0x090x09Desktop, Identity, WebP
0x0A0x0ADesktop, Identity, AVIF
0x190x19Desktop, Identity, WebP, 2x
0x290x29Desktop, Identity, WebP, Save-Data
0x490x49Desktop, Gzip, WebP
0x010x01Mobile, Identity, WebP
0x050x05Tablet, Identity, WebP

Sentinel Values

Five alternate IDs are reserved for special purposes and are never produced by header classification:

Alternate IDConstantPurpose
0x1CPS_SENTINEL_EARLY_HINTSStores preload hints for 103 Early Hints
0x2CPS_SENTINEL_WARMUPTriggers full variant matrix generation
0x3CPS_SENTINEL_CONTENT_HASHContent hash for change detection
0x4CPS_SENTINEL_SUBRESOURCESubresource metadata
0x5CPS_SENTINEL_BROWSER_PROFILEBrowser analysis profile data

Cache Key Format

Cache entries are keyed by SHA-256(URL, hostname) using Cyclone’s native key system. Multiple variants of the same URL are stored as alternates within this key, each identified by an AlternateId (the low 8 bits of the capability mask). Per-alternate metadata carries the full 32-bit mask and content type.

Lookups use PageSpeedSelector for best-fit alternate selection in a single pass over all stored alternates (O(alternates), not O(fallback_masks)).

Variant Fallback

When no exact match exists among the stored alternates, the PageSpeedSelector scores alternates by mask similarity and returns the closest match. The original content (stored at the default mask 0x08) always serves as a fallback, so a client always receives a response — either the optimal variant, a close variant, or the original.

C API

ModPageSpeed 2.0 exposes a C API (lib/pagespeed/pagespeed.h) for embedding the cache, HTML processor, CSS minifier, and capability mask logic into custom applications. All functions use the ps_ prefix. The API uses opaque handle types and C linkage for maximum portability.

Handle Types

TypeDescription
ps_cache_tCache volume handle (opaque)
ps_read_result_tResult from a cache read (opaque)
ps_write_handle_tStreaming write handle (opaque)
ps_html_result_tResult from ps_html_process() (opaque)
ps_scan_result_tResult from ps_html_scan() (opaque)
ps_critical_css_result_tResult from critical CSS extraction
ps_html_transform_tHTML transform handle (opaque)

Error Handling

All fallible functions return ps_error_t (marked PS_NODISCARD):

ConstantValueDescription
PS_OK0Success
PS_ERR_NOT_FOUND1Cache key or alternate not found
PS_ERR_IO2I/O error
PS_ERR_CORRUPTED3Data corruption detected
PS_ERR_NO_SPACE4Cache volume full
PS_ERR_INVALID_ARG5Invalid argument
PS_ERR_BUSY6Resource busy
PS_ERR_CLOSED7Handle already closed
PS_ERR_TOO_MANY_ALTERNATES8Alternate limit exceeded
PS_ERR_EXISTS9Entry already exists
PS_ERR_NOT_OWNED10Caller does not own the resource
PS_ERR_VERSION_MISMATCH11Incompatible cache format version
PS_ERR_INTERNAL99Internal error
ps_error_t err = ps_cache_open(&config, &cache);
if (err != PS_OK) {
    fprintf(stderr, "%s: %s\n", ps_error_name(err), ps_strerror(err));
    // ps_last_error_message() returns additional detail (thread-local)
}

Error utility functions:

FunctionSignatureDescription
ps_error_name()const char* ps_error_name(ps_error_t err)Returns the error constant name (e.g., "PS_ERR_IO")
ps_strerror()const char* ps_strerror(ps_error_t err)Returns a human-readable error description
ps_last_error_message()const char* ps_last_error_message(void)Returns thread-local detail message from the last failed call

Content Type Constants

ConstantValueMIME type
PS_CONTENT_HTML0text/html
PS_CONTENT_CSS1text/css
PS_CONTENT_JS2application/javascript
PS_CONTENT_IMAGE3image/*
PS_CONTENT_OTHER4(other)

Use ps_classify_content_type(header) to map a Content-Type header string to a ps_content_type_t, and ps_content_type_mime(type) for the reverse.

Sentinel Constants

ConstantValuePurpose
PS_SENTINEL_EARLY_HINTS0x1CPreload hints for 103 Early Hints
PS_SENTINEL_WARMUP0x2CFull variant matrix generation
PS_SENTINEL_CONTENT_HASH0x3CContent hash for change detection
PS_SENTINEL_SUBRESOURCE0x4CSubresource metadata
PS_SENTINEL_BROWSER_PROFILE0x5CBrowser analysis profile data

Cache Functions

Opening and closing:

ps_cache_config_t config;
ps_cache_config_init(&config);            // zero-fills and sets struct_size
config.volume_path = "/data/cache.vol";
config.volume_size = 1ULL << 30;          // 1 GB
config.enable_checksum = 1;
config.ram_cache_size = 64 * 1024 * 1024; // 64 MB RAM cache

ps_cache_t* cache = NULL;
ps_error_t err = ps_cache_open(&config, &cache);
// ...
ps_cache_close(cache);

Reading (zero-copy via mmap):

ps_read_result_t* result = NULL;
ps_error_t err = ps_cache_read_best(cache, url, hostname, mask, &result);
if (err == PS_OK) {
    const uint8_t* data;
    size_t len;
    err = ps_read_content(result, &data, &len);  // pointer into mmap'd region
    uint32_t stored_mask = ps_read_mask(result);
    ps_content_type_t ct = ps_read_content_type(result);
    uint8_t flags = ps_read_flags(result);
    int ram_hit = ps_read_is_ram_hit(result);
    ps_read_free(result);
}

Other read functions:

FunctionDescription
ps_cache_read_best()Best-fit alternate for a capability mask
ps_cache_read_alternate()Read a specific alternate by ID
ps_cache_read_early_hints()Read the Early Hints sentinel
ps_read_content()Get pointer + length from a read result
ps_read_copy()Copy content into a caller-owned buffer
ps_read_mask()Get the stored capability mask
ps_read_content_type()Get the content type
ps_read_origin_content_type()Get the original Content-Type string
ps_read_flags()Get the flags byte (PS_FLAG_NEEDS_REVALIDATION)
ps_read_is_ram_hit()Whether the result came from RAM cache
ps_read_free()Free a read result

Writing (streaming):

ps_write_params_t params;
ps_write_params_init(&params);
params.alternate_id = mask & 0xFF;
params.content_length = total_len;
params.full_mask = mask;
params.content_type = PS_CONTENT_CSS;
params.origin_ct = "text/css; charset=utf-8";

ps_write_handle_t* wh = NULL;
ps_error_t err = ps_cache_write_begin(cache, url, hostname, &params, &wh);
if (err == PS_OK) {
    err = ps_write_data(wh, chunk1, chunk1_len);
    err = ps_write_data(wh, chunk2, chunk2_len);
    err = ps_write_close(wh);  // commits to cache
}
// On error: ps_write_abort(wh) discards the write

Write a sentinel entry (e.g., Early Hints):

ps_write_handle_t* wh = NULL;
ps_cache_write_sentinel(cache, url, hostname,
                        PS_SENTINEL_EARLY_HINTS, hints_len, &wh);
ps_write_data(wh, hints_data, hints_len);
ps_write_close(wh);

Management:

FunctionDescription
ps_cache_alternate_exists()Check if a specific alternate exists
ps_cache_remove()Delete all alternates for a URL
ps_cache_list_alternates()List all alternates (returns ps_alternate_info_t array)
ps_alternates_free()Free the alternate list
ps_cache_stats()Get cache statistics (ps_cache_stats_t)

Version

FunctionSignatureDescription
ps_version_major()int ps_version_major(void)Major version number
ps_version_minor()int ps_version_minor(void)Minor version number
ps_version_patch()int ps_version_patch(void)Patch version number

Capability Mask Helpers

// Classify request headers into a 32-bit capability mask
uint32_t mask = ps_classify(accept_header, user_agent, save_data, accept_encoding);

// Adjust viewport from a pixel width
mask = ps_mask_set_viewport_from_width(mask, 375);  // → Mobile

// Score how well a stored alternate matches a client mask
int score = ps_score_alternate(client_mask, stored_mask);

// Normalize a hostname for cache key computation
char normalized[256];
int len = ps_normalize_hostname("WWW.Example.COM:443", normalized, sizeof(normalized));
// normalized = "example.com", len = 11
FunctionSignatureDescription
ps_normalize_hostname()int ps_normalize_hostname(const char* hostname, char* out_buf, size_t buf_size)Lowercases hostname, strips www. prefix and port suffix. Returns length of normalized string, or -1 on error.

HTML Processing

Single-call HTML processing with cache-backed resource lookup:

ps_html_config_t config;
ps_html_config_init(&config);
config.enable_critical_css = 1;
config.enable_lazy_load = 1;
config.enable_lcp_preload = 1;
config.viewport = PS_VIEWPORT_DESKTOP;

ps_html_result_t* result = NULL;
ps_error_t err = ps_html_process(html, html_len, url, hostname,
                                  &config, cache, &result);
if (err == PS_OK) {
    size_t out_len;
    const char* output = ps_html_result_output(result, &out_len);
    int modified = ps_html_result_modified(result);
    int needs_reval = ps_html_result_needs_revalidation(result);

    size_t hints_len;
    const char* hints = ps_html_result_early_hints(result, &hints_len);

    ps_html_result_free(result);
}

For lower-level control, use the scan + transform pipeline:

// Step 1: Scan HTML to extract structure
ps_scan_result_t* scan = NULL;
ps_html_scan(html, html_len, url, &scan);

// Step 2: Extract critical CSS using scan results
ps_critical_css_config_t css_config;
ps_critical_css_config_init(&css_config);
ps_critical_css_result_t* crit = NULL;
ps_css_extract_critical(scan, css, css_len, &css_config, &crit);
size_t crit_len;
const char* critical_css = ps_critical_css_output(crit, &crit_len);

// Step 3: Transform HTML with critical CSS injected
ps_html_transform_t* xform = NULL;
ps_html_transform_create(scan, &html_config, critical_css, crit_len,
                          cache, hostname, NULL, 0, &xform);
char* out_html = NULL;
size_t out_len;
ps_html_transform_run(xform, html, html_len, url, &out_html, &out_len);

ps_free(out_html);
ps_html_transform_free(xform);
ps_critical_css_result_free(crit);
ps_scan_result_free(scan);

Scanner inspection:

FunctionSignatureDescription
ps_scan_element_count()size_t ps_scan_element_count(const ps_scan_result_t* result)Number of above-the-fold elements found
ps_scan_element()ps_error_t ps_scan_element(const ps_scan_result_t* result, size_t index, const char** out_tag, const char** out_id, int* out_depth, int* out_element_index)Get tag, id, depth, and DOM index of element at index
ps_scan_element_classes()size_t ps_scan_element_classes(const ps_scan_result_t* result, size_t index, const char*** out_classes)Get CSS classes for element at index; returns class count
ps_scan_stylesheet_count()size_t ps_scan_stylesheet_count(const ps_scan_result_t* result)Number of external stylesheets found
ps_scan_stylesheet()ps_error_t ps_scan_stylesheet(const ps_scan_result_t* result, size_t index, const char** out_href, const char** out_media)Get href and media attribute of stylesheet at index
ps_scan_inline_css()const char* ps_scan_inline_css(const ps_scan_result_t* result, size_t* out_len)Concatenated inline <style> content
ps_scan_lcp_candidate()const char* ps_scan_lcp_candidate(const ps_scan_result_t* result, const char** out_srcset, const char** out_sizes, int* out_element_index)LCP image candidate (returns src, outputs srcset, sizes, element index)
ps_scan_origin_count()size_t ps_scan_origin_count(const ps_scan_result_t* result)Number of third-party origins detected
ps_scan_origin()const char* ps_scan_origin(const ps_scan_result_t* result, size_t index)Third-party origin URL at index
ps_scan_result_free()void ps_scan_result_free(ps_scan_result_t* result)Free a scan result

HTML result inspection:

FunctionSignatureDescription
ps_html_result_has_critical_css()int ps_html_result_has_critical_css(const ps_html_result_t* result)Whether the result contains inlined critical CSS
ps_html_transform_modified()int ps_html_transform_modified(const ps_html_transform_t* transform)Whether the transform changed the HTML

Critical CSS inspection:

FunctionSignatureDescription
ps_critical_css_output()const char* ps_critical_css_output(const ps_critical_css_result_t* result, size_t* out_len)Extracted critical CSS string
ps_critical_css_stats()void ps_critical_css_stats(const ps_critical_css_result_t* result, int* out_total_rules, int* out_critical_rules)Total and critical rule counts
ps_critical_css_result_free()void ps_critical_css_result_free(ps_critical_css_result_t* result)Free a critical CSS result

CSS Processing

// Minify CSS (single call, no minifier object)
char* out_css = NULL;
size_t out_len;
ps_error_t err = ps_css_minify(css, css_len, &out_css, &out_len);
if (err == PS_OK) {
    // use out_css / out_len
    ps_free(out_css);
}

// Validate CSS syntax
ps_error_t err = ps_css_validate(css, css_len);

// Flatten @import directives (requires a lookup callback)
char* flat_css = NULL;
size_t flat_len;
int resolved, unresolved;
ps_css_flatten_imports(css, css_len, css_url, my_lookup_fn, user_data,
                       /*max_depth=*/3, &flat_css, &flat_len,
                       &resolved, &unresolved);
ps_free(flat_css);

Configuration Structs

ps_cache_stats_t — returned by ps_cache_stats():

FieldTypeDescription
struct_sizesize_tSize of this struct (for ABI compat)
ram_cache_hitsuint64_tRAM cache hit count
ram_cache_missesuint64_tRAM cache miss count
disk_cache_hitsuint64_tDisk cache hit count
disk_cache_missesuint64_tDisk cache miss count
bytes_readuint64_tTotal bytes read from cache
bytes_writtenuint64_tTotal bytes written to cache
evictionsuint64_tTotal evictions
current_entriesuint64_tCurrent number of cache entries
current_size_bytesuint64_tCurrent cache size in bytes
volume_capacity_bytesuint64_tTotal volume capacity in bytes
ram_cache_bytesuint64_tCurrent RAM cache usage in bytes
total_hitsuint64_tTotal hits (RAM + disk)
total_missesuint64_tTotal misses (RAM + disk)

ps_css_lookup_fn — callback for ps_css_flatten_imports():

typedef const char* (*ps_css_lookup_fn)(const char* url, size_t* out_len, void* user_data);

Called for each @import URL encountered during flattening. Return the CSS content and set *out_len to its length. Return NULL if the URL cannot be resolved (counted as unresolved). The user_data pointer is passed through from ps_css_flatten_imports().

Worker Notification

// Send a fire-and-forget notification to the worker over a Unix socket
ps_error_t err = ps_notify_worker("/var/lib/pagespeed/pagespeed.sock",
                                   url, hostname, PS_CONTENT_IMAGE, mask);

Memory Management

Buffers allocated by the API (ps_css_minify, ps_html_transform_run) must be freed with ps_free(). Opaque result types have dedicated free functions (ps_read_free, ps_html_result_free, etc.).

Thread Safety

Cache operations are thread-safe (the underlying Cyclone cache uses lock-free reads and per-bucket write locks). ps_html_result_t, ps_scan_result_t, ps_html_transform_t, and write handles are not thread-safe — use one per thread.

Next Steps