wp-client-side-media-processing
Implement or audit compatibility with the WordPress 7.1 browser-side media pipeline. Covers wp_is_client_side_media_processing_enabled, wp_client_side_media_processing_enabled, REST media create/sideload/finalize, generate_sub_sizes, convert_format, client-side image resizing and
Install
npx skills add https://github.com/Lonsdale201/wp-agent-skills/tree/main/wordpress/wp-client-side-media-processing
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install lonsdale201-wp-agent-skills@llmmart
git clone https://github.com/Lonsdale201/wp-agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole lonsdale201/wp-agent-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
WordPress Client-side Media Processing
WordPress 7.1 can resize, compress, rotate, convert, and create image derivatives in the browser before files are finalized on the server. Treat this as a second execution path, not as a transparent optimization: server image-editor hooks may not run, attachment metadata can be generated in stages, and the block editor may receive a Document-Isolation-Policy header.
When to use this skill
- A plugin filters image quality, formats, EXIF rotation, sub-size generation, or attachment metadata.
- Code hooks
wp_generate_attachment_metadata,intermediate_image_sizes_advanced,image_editor_output_format,wp_image_editors, orimage_make_intermediate_size. - An editor asset, CDN, analytics script, page builder, or media workflow fails only on WP 7.1.
- Code calls
/wp/v2/media, reads attachment REST fields, uploads external images, or replaces attachment files. - A CSP blocks
blob:workers or a cross-origin editor dependency stops loading.
Model both processing paths
| Path | Processing location | Important consequence |
|---|---|---|
| Server fallback | GD/Imagick on the server | Traditional server image hooks run. |
| WP 7.1 client path | Browser, then REST sideload/finalize | Some server-only hooks never run; metadata arrives in stages. |
Do not feature-detect by version alone:
$client_media_available = function_exists( 'wp_is_client_side_media_processing_enabled' )
&& wp_is_client_side_media_processing_enabled();
The core function defaults to enabled only in a secure context: HTTPS, localhost, or a .localhost host. The wp_client_side_media_processing_enabled filter can override it. A browser can still fall back because of unsupported capabilities or a failed client operation.
Keep upload integrations path-independent
Prefer hooks that describe the completed attachment contract rather than a particular editor implementation. Make metadata filters deterministic and idempotent:
add_filter(
'wp_generate_attachment_metadata',
static function ( array $metadata, int $attachment_id, string $context ): array {
$metadata['myplugin'] = array(
'version' => 1,
'source' => wp_get_attachment_url( $attachment_id ),
);
return $metadata;
},
10,
3
);
On the client path, the filter can run once for the initial create and again with $context === 'update' when /finalize applies the browser-produced files. Never bill, send a webhook, enqueue a unique job, or append duplicate data merely because this filter fired. Guard side effects with durable state and make retries safe.
Hooks tied to server image editing, including wp_image_editors, image_memory_limit, and image_make_intermediate_size, do not describe the client path and may not fire. Do not use them as the only place to enforce a security policy or business invariant.
Respect the REST workflow
The standard create route accepts these WP 7.1 parameters:
generate_sub_sizes: whether the server creates derivatives;convert_format: whether server-side format conversion runs;url: server-side sideload of an external image.
When client processing is enabled, core also registers:
POST /wp/v2/media/{id}/sideloadfor a generated file;POST /wp/v2/media/{id}/finalizefor the collected sub-size metadata.
These are authenticated core endpoints with attachment edit/upload checks, bounded schemas, size-name validation, upload-directory pinning, and file provenance validation. Do not reproduce the flow with a permissive custom endpoint. Do not trust client-supplied filenames, dimensions, MIME types, or metadata in your own routes.
New response fields include missing_image_sizes, filename, filesize, and edit-context exif_orientation. Request only the fields needed and handle filesize: null.
Preserve core image filters
The browser pipeline maps core policy into client behavior. Continue to use the established filters when they express the desired rule:
big_image_size_threshold;image_editor_output_format;image_save_progressive;wp_image_maybe_exif_rotate;wp_editor_set_qualityandjpeg_quality.
Test both paths. A PHP filter that accepts a filename or MIME type may be evaluated at a different stage, and a filter with hidden side effects is unsafe.
Handle editor isolation deliberately
For supported Chromium versions, the block editor can send:
Document-Isolation-Policy: isolate-and-credentialless
Core adds crossorigin="anonymous" to relevant cross-origin media/script/style tags in the isolated editor response. Consequences:
- third-party resources must send compatible CORS headers;
- credentials are not available to anonymous cross-origin requests;
- a strict CSP must permit the required WebAssembly worker path, including
worker-src blob:when applicable; - code must not assume the editor canvas and all third-party frames can share DOM state.
Do not globally disable client processing to hide one incompatible asset. First make the asset CORS/CSP-safe or load it only where needed. Use the enablement filter only as a deliberate site compatibility escape hatch.
External media and replacement
Do not fetch arbitrary remote images in the isolated browser. Use the url parameter on the core media create endpoint so WordPress performs a server-side, SSRF-checked HTTP request. For plugin-owned remote imports, apply the same URL validation, capability, MIME, size, timeout, and provenance controls described in wp-file-upload-security.
When an API exposes file replacement, treat it as an update of the attachment's complete file/metadata graph. Do not overwrite a path directly and leave stale derivatives or cache entries.
Test matrix
Run at least:
- HTTPS client path and forced server fallback;
- JPEG/PNG plus HEIC/HEIF where the browser supports it;
- EXIF-oriented image and big-image downscaling;
- custom registered image sizes and output-format filters;
- failed/interrupted sideload followed by retry/finalize;
- metadata filters firing more than once without duplicate side effects;
- external image URL, invalid/private URL, and CORS-restricted asset;
- block editor with the production CSP and third-party scripts.
Critical rules
- Support client and server paths; never make correctness depend on one image editor hook.
- Make metadata callbacks idempotent and retry-safe.
- Keep authorization, schema validation, MIME checks, and filename provenance server-side.
- Use core REST media routes instead of cloning the sideload/finalize protocol.
- Audit CORS and CSP before disabling the feature.
- Do not treat browser-produced dimensions, files, or MIME data as trusted.
Cross-references
- Use
wp-file-upload-securityfor the upload trust boundary. - Use
wp-rest-apifor custom endpoint authorization and schemas. - Use
wp-admin-media-framefor media modal selection behavior. - Use
wp-block-editor-iframe-compatibilityfor canvas DOM and asset placement.
References
- Read
references/pipeline.mdfor the exact REST phases, hook portability matrix, isolation effects, and source paths. - Client-side media processing dev note: https://make.wordpress.org/core/2026/07/22/client-side-media-processing-in-wordpress-7-1/
- Core sources:
wp-includes/media.php,wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php,wp-includes/js/dist/upload-media.js,wp-includes/js/dist/media-utils.js.
Files (wp-agent-skills)
-
agents
-
openai.yaml 276 B
interface: display_name: "WP Client-side Media Processing" short_description: "Handle the WordPress 7.1 browser media pipeline safely" default_prompt: "Use $wp-client-side-media-processing to implement or audit WordPress 7.1 client-side media processing compatibility."
-
-
references
-
pipeline.md 3.8 KB
# WP 7.1 client-side media pipeline reference Read this when implementing or debugging an upload integration rather than only auditing one. ## End-to-end phases 1. The client creates an attachment with `POST /wp/v2/media` and may send `generate_sub_sizes=false` and `convert_format=false`. 2. Core preserves the original metadata while avoiding conflicting server derivatives, EXIF rotation, and big-image scaling for that request. 3. The browser processes the source with the WordPress upload/media packages and libvips/WASM where supported. 4. The client sends each produced file to `POST /wp/v2/media/{id}/sideload` with an allowed `image_size`. 5. Core records server-generated filenames as attachment-scoped provenance. 6. The client submits collected metadata to `POST /wp/v2/media/{id}/finalize`. 7. Core validates size names, dimensions, file ownership/provenance, and metadata shape before updating attachment metadata. 8. Core applies `wp_generate_attachment_metadata` with context `update`, persists metadata, and removes consumed provenance rows. The flow is retryable. Plugin callbacks must not assume each phase occurs exactly once. ## Hook portability | Hook/API | Client path | Server path | Guidance | |---|---:|---:|---| | `wp_generate_attachment_metadata` | Yes, including finalize update | Yes | Keep idempotent; inspect `$context`. | | `wp_update_attachment_metadata` | Yes | Yes | Suitable for observing persisted metadata; still avoid duplicate side effects. | | `big_image_size_threshold` | Reflected in client behavior | Yes | Use for policy, not side effects. | | `image_editor_output_format` | Reflected in client behavior and REST response | Yes | Return deterministic MIME mappings. | | `wp_editor_set_quality`, `jpeg_quality` | Reflected in client behavior | Yes | Keep values bounded and deterministic. | | `wp_image_maybe_exif_rotate` | Reflected in client behavior | Yes | Do not assume the PHP rotate callback runs. | | `wp_image_editors` | No | Yes | Server implementation detail; never the only enforcement point. | | `image_memory_limit` | No | Yes | Not a universal upload limit. | | `image_make_intermediate_size` | No | Yes | Not a completion signal. | ## REST additions Create parameters: - `generate_sub_sizes: boolean`, default `true`; - `convert_format: boolean`, default `true`; - `url: uri` for server-side external-image sideloading. Attachment response additions: - `missing_image_sizes: string[]`, edit context; - `filename: string`; - `filesize: integer|null`; - `exif_orientation: integer`, edit context. Do not depend on private metadata keys used for sideload provenance. They are core implementation details. ## Isolation checklist - Confirm every external script, stylesheet, audio/video source, and poster used in the block editor sends usable CORS headers. - Confirm anonymous cross-origin requests do not require cookies or HTTP authentication. - Permit required workers in CSP. Test `worker-src 'self' blob:` only as broadly as the application needs. - Avoid page-builder/editor code that reaches across iframe/document boundaries by global `document` assumptions. - Test on a current Chromium browser because Document-Isolation-Policy activation is user-agent dependent. ## Source verification paths - `wp-includes/media.php`: enablement, Chromium detection, DIP response transformation, media settings. - `wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php`: route schemas, create, sideload, provenance, finalize. - `wp-includes/js/dist/upload-media.js`: staged browser upload orchestration. - `wp-includes/js/dist/media-utils.js`: media helpers and processing decisions. - `wp-includes/js/dist/vips.js`: browser image processing implementation. Verify these against the exact target release when accepting client-produced files or depending on route schemas.
-
-
SKILL.md 8.5 KB
--- name: wp-client-side-media-processing description: Implement or audit compatibility with the WordPress 7.1 browser-side media pipeline. Covers wp_is_client_side_media_processing_enabled, wp_client_side_media_processing_enabled, REST media create/sideload/finalize, generate_sub_sizes, convert_format, client-side image resizing and conversion, metadata finalization, duplicate wp_generate_attachment_metadata calls, server-only image hooks, Document-Isolation-Policy, crossorigin behavior, WASM/CSP requirements, external-image sideloading, fallback behavior, and safe upload extension points. Use when a plugin processes uploads, generates image sizes, filters attachment metadata, embeds editor scripts, reads media REST responses, or breaks only in the block editor on WordPress 7.1. license: GPLv2-or-later metadata: wp-skills-author: "Soczó Kristóf" wp-skills-contact: "mailto:lonsdale201@hotmail.com" wp-skills-plugin: "wordpress" wp-skills-plugin-version-tested: "7.1" wp-skills-wp-version-tested: "7.1" wp-skills-php-min: "7.4" wp-skills-last-updated: "2026-08-19" --- # WordPress Client-side Media Processing WordPress 7.1 can resize, compress, rotate, convert, and create image derivatives in the browser before files are finalized on the server. Treat this as a second execution path, not as a transparent optimization: server image-editor hooks may not run, attachment metadata can be generated in stages, and the block editor may receive a Document-Isolation-Policy header. ## When to use this skill - A plugin filters image quality, formats, EXIF rotation, sub-size generation, or attachment metadata. - Code hooks `wp_generate_attachment_metadata`, `intermediate_image_sizes_advanced`, `image_editor_output_format`, `wp_image_editors`, or `image_make_intermediate_size`. - An editor asset, CDN, analytics script, page builder, or media workflow fails only on WP 7.1. - Code calls `/wp/v2/media`, reads attachment REST fields, uploads external images, or replaces attachment files. - A CSP blocks `blob:` workers or a cross-origin editor dependency stops loading. ## Model both processing paths | Path | Processing location | Important consequence | |---|---|---| | Server fallback | GD/Imagick on the server | Traditional server image hooks run. | | WP 7.1 client path | Browser, then REST sideload/finalize | Some server-only hooks never run; metadata arrives in stages. | Do not feature-detect by version alone: ```php $client_media_available = function_exists( 'wp_is_client_side_media_processing_enabled' ) && wp_is_client_side_media_processing_enabled(); ``` The core function defaults to enabled only in a secure context: HTTPS, `localhost`, or a `.localhost` host. The `wp_client_side_media_processing_enabled` filter can override it. A browser can still fall back because of unsupported capabilities or a failed client operation. ## Keep upload integrations path-independent Prefer hooks that describe the completed attachment contract rather than a particular editor implementation. Make metadata filters deterministic and idempotent: ```php add_filter( 'wp_generate_attachment_metadata', static function ( array $metadata, int $attachment_id, string $context ): array { $metadata['myplugin'] = array( 'version' => 1, 'source' => wp_get_attachment_url( $attachment_id ), ); return $metadata; }, 10, 3 ); ``` On the client path, the filter can run once for the initial create and again with `$context === 'update'` when `/finalize` applies the browser-produced files. Never bill, send a webhook, enqueue a unique job, or append duplicate data merely because this filter fired. Guard side effects with durable state and make retries safe. Hooks tied to server image editing, including `wp_image_editors`, `image_memory_limit`, and `image_make_intermediate_size`, do not describe the client path and may not fire. Do not use them as the only place to enforce a security policy or business invariant. ## Respect the REST workflow The standard create route accepts these WP 7.1 parameters: - `generate_sub_sizes`: whether the server creates derivatives; - `convert_format`: whether server-side format conversion runs; - `url`: server-side sideload of an external image. When client processing is enabled, core also registers: - `POST /wp/v2/media/{id}/sideload` for a generated file; - `POST /wp/v2/media/{id}/finalize` for the collected sub-size metadata. These are authenticated core endpoints with attachment edit/upload checks, bounded schemas, size-name validation, upload-directory pinning, and file provenance validation. Do not reproduce the flow with a permissive custom endpoint. Do not trust client-supplied filenames, dimensions, MIME types, or metadata in your own routes. New response fields include `missing_image_sizes`, `filename`, `filesize`, and edit-context `exif_orientation`. Request only the fields needed and handle `filesize: null`. ## Preserve core image filters The browser pipeline maps core policy into client behavior. Continue to use the established filters when they express the desired rule: - `big_image_size_threshold`; - `image_editor_output_format`; - `image_save_progressive`; - `wp_image_maybe_exif_rotate`; - `wp_editor_set_quality` and `jpeg_quality`. Test both paths. A PHP filter that accepts a filename or MIME type may be evaluated at a different stage, and a filter with hidden side effects is unsafe. ## Handle editor isolation deliberately For supported Chromium versions, the block editor can send: ```text Document-Isolation-Policy: isolate-and-credentialless ``` Core adds `crossorigin="anonymous"` to relevant cross-origin media/script/style tags in the isolated editor response. Consequences: - third-party resources must send compatible CORS headers; - credentials are not available to anonymous cross-origin requests; - a strict CSP must permit the required WebAssembly worker path, including `worker-src blob:` when applicable; - code must not assume the editor canvas and all third-party frames can share DOM state. Do not globally disable client processing to hide one incompatible asset. First make the asset CORS/CSP-safe or load it only where needed. Use the enablement filter only as a deliberate site compatibility escape hatch. ## External media and replacement Do not fetch arbitrary remote images in the isolated browser. Use the `url` parameter on the core media create endpoint so WordPress performs a server-side, SSRF-checked HTTP request. For plugin-owned remote imports, apply the same URL validation, capability, MIME, size, timeout, and provenance controls described in `wp-file-upload-security`. When an API exposes file replacement, treat it as an update of the attachment's complete file/metadata graph. Do not overwrite a path directly and leave stale derivatives or cache entries. ## Test matrix Run at least: 1. HTTPS client path and forced server fallback; 2. JPEG/PNG plus HEIC/HEIF where the browser supports it; 3. EXIF-oriented image and big-image downscaling; 4. custom registered image sizes and output-format filters; 5. failed/interrupted sideload followed by retry/finalize; 6. metadata filters firing more than once without duplicate side effects; 7. external image URL, invalid/private URL, and CORS-restricted asset; 8. block editor with the production CSP and third-party scripts. ## Critical rules - Support client and server paths; never make correctness depend on one image editor hook. - Make metadata callbacks idempotent and retry-safe. - Keep authorization, schema validation, MIME checks, and filename provenance server-side. - Use core REST media routes instead of cloning the sideload/finalize protocol. - Audit CORS and CSP before disabling the feature. - Do not treat browser-produced dimensions, files, or MIME data as trusted. ## Cross-references - Use **`wp-file-upload-security`** for the upload trust boundary. - Use **`wp-rest-api`** for custom endpoint authorization and schemas. - Use **`wp-admin-media-frame`** for media modal selection behavior. - Use **`wp-block-editor-iframe-compatibility`** for canvas DOM and asset placement. ## References - Read `references/pipeline.md` for the exact REST phases, hook portability matrix, isolation effects, and source paths. - Client-side media processing dev note: <https://make.wordpress.org/core/2026/07/22/client-side-media-processing-in-wordpress-7-1/> - Core sources: `wp-includes/media.php`, `wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php`, `wp-includes/js/dist/upload-media.js`, `wp-includes/js/dist/media-utils.js`.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.