OXPulse Imager releases
Optional bring-your-own imgproxy image delivery for WordPress. Disabled by default. No SaaS, no telemetry, no FFI.
by Anatoly Koptev · github.com/anatolykoptev/oxpulse-imager · website
Install
The author publishes release zips, so WP-CLI can install straight from GitHub:
wp plugin install https://github.com/anatolykoptev/oxpulse-imager/releases/download/v0.1.10/oxpulse-imager.zipReadme
OXPulse Imager
Optional bring-your-own imgproxy image delivery for WordPress.
Generates signed, deterministic imgproxy URLs for approved local origins while preserving the original URL whenever configuration, source policy, signing, or delivery cannot safely proceed. Disabled by default. No SaaS, no telemetry, no FFI.
How it works
The plugin rewrites image URLs on the frontend (never in admin) to signed imgproxy URLs. imgproxy fetches the original image, transforms it (resize, format conversion, quality), and serves the result. The original image is never modified.
Format negotiation (2026 industry standard)
The default output format is auto, which uses Accept header content negotiation — the same approach used by Cloudflare Images, Cloudinary, Imgix, and Vercel:
- Browser sends
Accept: image/avif,image/webp,...on every image request - imgproxy inspects the Accept header and serves the best format the browser supports:
image/avif→ AVIF (~50% smaller than JPEG)image/webp→ WebP (~30% smaller)- neither → original format (JPEG fallback)
- Response includes
Vary: Acceptso caches store one variant per format
No <picture> / <source> tags needed. One URL serves the optimal format per browser.
Content-Disposition
The plugin adds a fn: (filename) option to every imgproxy URL so that "Save As" in browsers produces a meaningful filename. In auto mode, the original filename is preserved; in explicit format mode (avif/webp), the extension is replaced to match.
Requirements
- PHP 8.3+
- WordPress 6.2+
- imgproxy v4+ endpoint (bring your own)
imgproxy server configuration
For auto format mode (Accept header negotiation), configure your imgproxy server with:
IMGPROXY_AUTO_AVIF=true
IMGPROXY_AUTO_WEBP=true
Without these, auto mode will serve the original format (no conversion). Use the "Test Connection" button in the plugin settings to verify that AVIF negotiation is working.
CDN / caching proxy configuration
Critical: When using auto format mode, your CDN or caching proxy MUST include the Accept header in the cache key. Otherwise, the CDN will serve an AVIF response to a browser that requested WebP (or vice versa).
Nginx cache key
proxy_cache_key "$scheme$proxy_host$request_uri$http_accept";
CDN settings
- Include
Acceptheader in cache key - Do not strip or normalize the
Acceptheader - Honor the
Vary: Acceptresponse header from imgproxy
Security note
Headers cannot be signed. An attacker can bypass your CDN cache by varying the Accept header. This is a cache-poisoning vector, not a security vulnerability in the plugin — imgproxy will still only serve allowed formats. Configure your CDN to limit the number of distinct Accept header variants it will cache.
Local delivery (Phase 6 — standard/shared hosting)
When no imgproxy endpoint is configured, the plugin switches to
LocalBackend mode: on-disk WebP delivery using PHP (Imagick→GD)
with no daemon required. Cache files are served as static files;
misses are handled by a self-contained oxpulse-img.php endpoint.
Apache (.htaccess)
The plugin generates .htaccess rules that rewrite missing cache
files to the miss-endpoint. Requires mod_rewrite + AllowOverride.
A capability-test auto-detects whether rewrite is available and falls
back to the output-buffer mode if not.
nginx
nginx does not support .htaccess. Use the output-buffer fallback
(automatic) or add this try_files snippet to your server block. Also
add a deny-PHP location for the cache dir (belt-and-braces) BEFORE it:
# Belt-and-braces: never execute scripts inside the cache dir.
location ~* ^/wp-content/cache/oxpulse/.*\.(php|phtml)$ { deny all; }
# Serve existing cache files directly; on miss, route to the endpoint
# with the key extracted from the filename.
location ~* ^/wp-content/cache/oxpulse/([0-9a-f]+)/(.+)\.(webp|avif)$ {
add_header Vary Accept;
try_files $uri /wp-content/oxpulse-img.php?k=$2;
}
Note: the location regex uses [0-9a-f]+ (not a {16} quantifier) —
an unquoted regex containing { / } makes nginx parse the braces as
a block and fail to load (pcre2_compile() failed). Battle-tested
plugins (WebP Express, Converter for Media) avoid the quantifier for
this reason.
Security note (cache-dir hardening)
The .htaccess generated by the plugin includes php_flag engine off
as defense-in-depth for Apache + mod_php hosts, guarded by
<IfModule mod_php*.c> so it only applies when mod_php is actually
loaded. A bare php_flag is a mod_php-only directive; under Apache +
php-fpm (mod_proxy_fcgi / SetHandler — the modern default) with
AllowOverride All it is an unknown directive and Apache returns 500
on every request served from the cache dir, so the guard is required.
RemoveHandler/RemoveType are emitted unguarded (valid regardless of
the PHP SAPI), and nginx does not read .htaccess at all. It is not
the security boundary. The real guard is the signed-key +
format-allowlist invariant in the miss-endpoint: the cache directory
only ever contains <key>.webp files where key is HMAC-signed and
the format extension is allowlisted to webp. An attacker cannot
plant an executable-named file (e.g. <key>.php) — the signature check
rejects unknown keys and the format allowlist rejects non-webp
extensions with a 400 before any disk write. nginx hosts should
additionally deny PHP execution inside the cache location block as
belt-and-braces.
License
GPL-2.0-or-later
Copyright (c) 2026 Anatoly Koptev
Read the full README on GitHub →
Releases
| Tag | Published | Asset | Downloads |
|---|---|---|---|
| v0.1.10 | Aug 18, 2026 | oxpulse-imager.zip | 1 |
| v0.1.9 | Aug 18, 2026 | oxpulse-imager.zip | 0 |
| v0.1.8 | Aug 18, 2026 | oxpulse-imager.zip | 0 |
| v0.1.7 | Aug 18, 2026 | oxpulse-imager.zip | 0 |
| v0.1.6 | Aug 18, 2026 | oxpulse-imager.zip | 0 |
| v0.1.5 | Jul 23, 2026 | oxpulse-imager.zip | 0 |
| v0.1.4 | Jul 23, 2026 | oxpulse-imager.zip | 0 |
| v0.1.3 | Jul 23, 2026 | oxpulse-imager.zip | 1 |
| v0.1.2 | Jul 22, 2026 | oxpulse-imager-0.1.2.zip | 0 |
| v0.1.1 | Jul 22, 2026 | oxpulse-imager-0.1.1.zip | 0 |