=== AccessNow Image Optimizer ===
Contributors: accessnow
Tags: images, webp, avif, compress, resize
Requires at least: 6.5
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.4.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Shrinks big uploads and serves WebP and AVIF copies, so pages load faster. Every original is kept, restorable with one click.

== Description ==

AccessNow Image Optimizer resizes large JPEG, PNG and GIF uploads to a maximum width you choose and recompresses them. It also makes WebP and, where the server supports it, AVIF copies. Visitors whose browsers support those formats get the smaller files; everyone else gets the original.

Nothing it does is final until you say so. Before it changes an image it keeps the untouched original, and any image can be put back with one click. When you are happy with the results, it shows how much space the backups use and deletes them for you, all at once, one by one or after a number of days.

Everything happens on your own server, with WordPress's own image editor (Imagick or GD). It needs no account, no API key and no quota, and no image is sent anywhere.

= Credits =

Includes PEL, the PHP Exif Library (https://github.com/pel/pel), by Martin Geisler, Lars Olesen, Johannes Weberhofer and contributors, licensed GPLv2 or later. It edits the EXIF details of JPEG files, and is bundled under the plugin's own namespace so it never clashes with another plugin's copy.

Copies sit next to the original with the format added to the name: `photo.jpg` gets `photo.jpg.webp` and `photo.jpg.avif`. Every thumbnail size gets its own copy, so a 300 px thumbnail is never swapped for the full-size image.

Images are served in two ways:

* **Picture tags (always on):** every `<img>` on the front end is wrapped in a `<picture>` element with WebP/AVIF sources that mirror its `srcset`. Works on any server and with any page cache.
* **.htaccess rewrite (optional, Apache/LiteSpeed):** requests for `.jpg`/`.png` files in `wp-content/uploads` are answered with the WebP/AVIF copy when the browser accepts it. This also covers CSS background images. nginx needs the manual rules in the FAQ.

= Features =

* **Every original kept, restorable with one click — and deletable when you're sure, with the space it frees shown.** A backup is taken before any image is changed (on by default). "Restore original" in the Media Library, or every original at once, puts it back and remakes the thumbnails and copies. The Backups panel says how much space the backups use ("Backups use 412 MB for 1,280 images"), and deletes them: all at once, one image at a time, or automatically once they are older than the days you choose (off by default).
* Resizes uploads wider than the maximum width (default 2560 px, the same as WordPress itself) and recompresses them. An original is never replaced by a bigger file.
* Media Library column showing what each image saved and whether its backup is kept, with "Optimise now", "Restore original" and "Delete backup" buttons (also in the image's details).
* Photo details (metadata) your way: keep them all (the default), remove the location (GPS), or remove the camera and location details, keeping the copyright and author if you like. Your choice is written into the image, every thumbnail size and the WebP copies (and the AVIF copies with Imagick), on Imagick and GD alike. The colour profile is always kept, and the Media Library still fills in each upload's title and caption from the original.
* Optional credit, off unless you switch it on: adds "Optimised with AccessNow Image Optimizer" and a link to the hidden description of images uploaded while it is on. Never shown on your pages.
* Faster first image: the first large image on each page is not lazy-loaded and is fetched first, and images get their missing width and height, which helps Largest Contentful Paint.
* GIF images: static GIFs are recompressed and get WebP/AVIF copies; with Imagick, animated GIFs get animated WebP copies.
* Optional: turns PNG photos with no transparency into JPEG when that is at most half the size, and updates the links in your posts and pages.
* Exclude list: leave chosen folders, file names or sizes alone.
* Savings report: the space saved by format and over time, as a chart and a table, plus the space backups use and the space freed by deleting them.
* WP-CLI commands: `wp image-optimizer bulk`, `restore`, `stats`, `convert-png`, `backups size` and `backups delete`.
* Makes WebP and AVIF copies of the main image and, optionally, of every thumbnail size. Thumbnail copies are made in the background straight after an upload, so uploading stays quick.
* Uses WordPress's own image editor: Imagick when the server has it (decodes big JPEGs at a fraction of their size when they are being shrunk anyway), otherwise GD. Colour profiles are kept either way, except in AVIF copies made with GD.
* Bulk optimiser for existing Media Library images. It works in batches of about 20 seconds, can be stopped and resumed, and copes with libraries of any size.
* Checks free disk space and stops before the disk gets too full. Skips images too large to open safely in the server's memory instead of crashing the upload.
* Remakes a WebP/AVIF copy when its original has changed since the copy was made.
* Server check on the settings page: asks your own web server whether the backups folder really refuses downloads and whether the .htaccess rewrite really serves WebP/AVIF.
* Removes its copies when you delete an image from the Media Library.
* Fixes phone photo orientation (EXIF) before recompressing, and marks every re-saved image as upright, so no viewer turns it twice.

== Installation ==

1. Install the plugin from Plugins > Add New, or upload the `accessnow-image-optimizer` folder to `/wp-content/plugins/`.
2. Activate it.
3. Go to Settings > Image Optimizer, check the Server Status table (click "Run server check"), and choose your settings.
4. Optional: click "Estimate disk space", then "Optimize All Existing Images" to process images uploaded before the plugin was active.

== Frequently Asked Questions ==

= Where do I get help? =

Step-by-step help articles, with screenshots, are at https://support.accessnow.com.au/help/workspace-accessnow/accessnow-image-optimizer. The plugin's own screens link to the article for each one, under the Help tab at the top right. To ask a question, use Get support on https://accessnow.com.au/projects/accessnow-image-optimizer/.

= Can I get my original images back? =

Yes, if "Back up originals" was on when the image was optimised (it is on by default). Press "Restore original" in the Media Library's list view or in the image's details, or "Restore every original" under Settings > Image Optimizer > Restore and convert. The backup is copied back, the thumbnails and WebP/AVIF copies are made again, and the bulk optimizer leaves the image alone afterwards. From the command line: `wp image-optimizer restore <id>` or `wp image-optimizer restore --all`.

= How much space do backups use, and can I delete them? =

Settings > Image Optimizer > Restore and convert says, in plain words, e.g. "Backups use 412 MB for 1,280 images". Once you are happy with the optimised images, tick the box that confirms it and press "Delete all backups permanently". Or delete one image's backup with "Delete backup" in the Media Library, or switch on "Delete old backups" in the settings to delete backups older than a number of days (at least 7) once a day. The images keep their optimised versions, but can no longer be restored, and this cannot be undone. Only files in the backups folder are ever deleted. The savings report shows the space backups use and the space deleting them has freed. From the command line: `wp image-optimizer backups size` and `wp image-optimizer backups delete --older-than=90 --dry-run`.

= Does it remove the GPS location from photos? =

If you ask it to. Under "Location and camera details", choose "Remove location (GPS)" to remove the GPS position and place names, or "Remove camera and location details" to remove all EXIF, XMP and IPTC except the copyright and author (kept unless you untick that). The default for new installs is "Keep all metadata"; sites upgrading from 1.3 that had the removal switched on get "Remove camera and location details". The colour profile is always kept, and backups keep the original details.

Your choice is written into the image, its thumbnail sizes, WordPress's "-scaled" copy and the WebP copies, on Imagick and on GD. WebP has no place for IPTC, so only EXIF and XMP go there. AVIF copies get EXIF and XMP on servers with Imagick; on servers with only GD they carry no details at all. Rebuilding the EXIF block (to remove the location or add the credit) leaves out the camera maker's private "maker note". For an image already in the library, the original is changed only when "Back up originals" is on.

= What does the optional credit add to my images? =

Nothing, unless you tick "Add 'Optimised with AccessNow Image Optimizer' and a link to the plugin's website to the description of optimised images". It is off by default, and installing or upgrading never switches it on. When it is on, images uploaded from then on get `Optimised with AccessNow Image Optimizer https://www.accessnow.com.au/projects/accessnow-image-optimizer/` in their hidden description (EXIF ImageDescription and XMP description), after any description already there and separated by " | ", or on its own. It is never added twice, never to images uploaded before you switched it on (not even by the bulk optimiser), and never to the Media Library title or caption. It is not shown on your pages.

= Does it handle GIFs? =

Static GIFs are recompressed when that makes them smaller and get WebP and AVIF copies. Animated GIFs need Imagick: they keep every frame and get an animated WebP copy. On servers with only GD, animated GIFs are left alone, because GD keeps only the first frame.

= Can I stop it touching some images? =

Yes. "Leave these images alone" takes folder and file-name patterns, one per line (`logos/`, `2024/05/`, `*-original.*`), plus file-size and pixel limits. Excluded images are never resized, recompressed, stripped, converted or copied.

= Is there a command line? =

Yes, with WP-CLI: `wp image-optimizer bulk [--limit=<n>] [--dry-run]`, `wp image-optimizer restore <id>... | --all`, `wp image-optimizer stats`, `wp image-optimizer convert-png [--dry-run]`, `wp image-optimizer backups size` and `wp image-optimizer backups delete <id>... | --all | --older-than=<days> [--dry-run]`. They share the queue with the settings page.

= Does it change my original files? =

Uploads wider than the maximum width are resized, and uploads are recompressed when that makes them smaller. The bulk optimiser does the same for existing images, and never recompresses an image it has already processed. Before a file is replaced, the new version is written to a temporary file and checked; if anything goes wrong (for example, a full disk), the original is kept. "Back up originals" is on by default, so an untouched copy of every file the plugin changes is kept in `wp-content/uploads/accessnow-image-optimizer-backups/`.

= How much disk space does it use? =

WebP and AVIF copies are usually well under the size of the originals, but there is one per format for every image and (if enabled) every thumbnail size. Backups use the full size of every original that gets changed; the Restore and convert tab shows how much, and frees it when you delete them. Click "Estimate disk space" on the settings page to see the worst case for the images still waiting for the bulk optimiser. The optimiser always leaves at least 50 MB free; developers can change this with the `accessnow_image_optimizer_min_free_space` filter.

= I use nginx. Are my backups safe? =

The backups folder is protected by an `.htaccess` file, which nginx ignores. Click "Run server check" on the settings page: if it reports that the folder can be downloaded from, add this to your `server {}` block and reload nginx:

`location ^~ /wp-content/uploads/accessnow-image-optimizer-backups/ { deny all; }`

If your uploads folder is somewhere else, the server check shows the rule with your own path.

= I use nginx. How do I serve WebP/AVIF for all image requests? =

nginx ignores `.htaccess`. The `<picture>` method works without any server changes. To serve the copies for every request (including CSS background images), add these rules and reload nginx.

In the `http {}` block:

`map $http_accept $accessnow_io_avif { default ".no-avif"; "~*image/avif" ".avif"; }`
`map $http_accept $accessnow_io_webp { default ".no-webp"; "~*image/webp" ".webp"; }`

In the `server {}` block, above any other location that matches images:

`location ~* ^/wp-content/uploads/.+\.(?:jpe?g|jpe|png|gif)$ {`
`    add_header Vary Accept;`
`    try_files $uri$accessnow_io_avif $uri$accessnow_io_webp $uri =404;`
`}`

The `.no-avif`/`.no-webp` defaults make nginx skip a format the browser does not accept. Leave out the AVIF map and the first `try_files` entry if AVIF is switched off. Make sure your `mime.types` includes `image/webp webp;` and `image/avif avif;`. If WordPress lives in a subfolder, change the path to suit.

= Does it work with a CDN or page cache? =

The `<picture>` method works with any cache. With the `.htaccess` method, one URL returns different formats, so the plugin sends `Vary: Accept`. Some CDNs ignore that header and may serve WebP/AVIF to a browser that cannot show it. If you use such a CDN, leave the `.htaccess` option off.

= Why do my photos look slightly less vivid after optimising? =

Your server is using GD. The plugin writes the colour profile back into the JPEG, PNG and WebP files GD saves, but GD can't put one into an AVIF copy, so photos taken in a wide colour space (such as recent iPhones) show in standard colours where the AVIF copy is used. The Server Status table shows which engine is in use; ask your host to enable the Imagick PHP extension, or switch off AVIF.

= Some images are wrapped in <picture> and my theme's CSS no longer matches them =

A few themes style images with selectors like `figure > img`, which stop matching once the image sits inside `<picture>`. Change the selector to `figure img`. Or, if the `.htaccess` method works on your server (the server check says so), turn the wrapper off with this line in a small plugin or your theme's `functions.php`:

`add_filter( 'accessnow_image_optimizer_use_picture', '__return_false' );`

= I used "Image Optimizer" 1.0. How do I upgrade? =

Install this plugin, deactivate the old "Image Optimizer" 1.0, then activate this one. It takes over the old settings, the list of processed images and the `.htaccess` block, and keeps serving the WebP/AVIF files 1.0 made. It waits with a notice while the old plugin is still active.

= What does uninstalling remove? =

Deactivating removes the plugin's block from `wp-content/uploads/.htaccess` and its scheduled jobs. Deleting the plugin removes its settings and savings figures only if you ticked "Delete this plugin's settings when it is deleted". Image files (originals, WebP/AVIF copies and backups) are never deleted. While AccessNow Image Optimizer Pro is installed, deleting the free plugin removes nothing at all, because Pro uses the same settings. If you no longer want the backups, delete them on the Restore and convert tab before removing the plugin.

== Screenshots ==

1. The bulk optimiser working through an existing Media Library, with the space saved by each image and its thumbnails.
2. Settings: maximum width, separate quality for JPEG/WebP and for AVIF, thumbnail copies, the .htaccess rewrite, backups, GIFs, photo details and the optional credit, the first image, PNG photos and the exclude list.
3. Server Status and the server check, which asks your own web server whether the backups folder is protected and the rewrite really works.
4. The disk space estimate, shown before a bulk run so there are no surprises.
5. The Optimisation column in the Media Library, with Optimise now and Restore original.
6. The savings report: space saved by format, day by day.
7. The Backups panel: how much space the backups of your originals use, and deleting them for good once you are sure.

== Changelog ==

= 1.4.0 =
* New: see and free the space your backups use. The Restore and convert tab says how much space the backups of your originals use, and for how many images, and what deleting them means. "Delete all backups permanently" deletes them all after you tick a box that states the space freed (resumable, like restoring). The figure is counted in short batches and kept up to date as backups are added and deleted.
* New: "Delete backup" next to "Restore original" in the Media Library and the image's details (administrators only). Afterwards the image says "No backup kept".
* New: "Delete old backups" setting: once a day, delete backups older than the days you choose (at least 7). Off by default.
* New: the savings report shows "Backups use" and "Freed by deleting backups".
* New: WP-CLI `wp image-optimizer backups size` and `wp image-optimizer backups delete [<id>...] [--all] [--older-than=<days>] [--dry-run] [--yes]`.
* Only files inside the backups folder are ever deleted; its protection files stay, and folders left empty are removed. Uninstalling still never deletes an image file.
* New: choose what photo details (metadata) optimised images keep: "Keep all metadata" (the default for new installs), "Remove location (GPS)" or "Remove camera and location details", with "Keep copyright and author". Sites that had "Remove location (GPS), camera and other hidden details" ticked get "Remove camera and location details"; the others keep everything.
* New: photo details are read from the upload before anything changes and written into the image, every thumbnail size, WordPress's "-scaled" copy and the WebP copies (EXIF, XMP, IPTC and the colour profile), and into AVIF copies with Imagick. GD-made thumbnails and copies no longer lose them, nor the colour profile. A PNG turned into JPEG keeps its details, its Author and Copyright too.
* New: optional credit in the hidden description of images uploaded while it is on. Off by default and never switched on by installing or upgrading.
* Fix: the Media Library now fills in a new upload's title, caption, camera, credit and copyright from the original, whatever the photo details setting (optimising used to remove them before WordPress read them).
* Fix: every re-saved image is marked as upright (EXIF orientation 1), so phone photos are never turned twice; the same after WordPress's Edit Image. Regenerated thumbnails follow the setting too.
* Safety: before changing an image's details the plugin checks what the file really is; anything unexpected is left alone and noted, and the upload carries on.
* Safety: deleting the plugin while AccessNow Image Optimizer Pro is installed keeps every setting and figure, even with "Delete this plugin's settings when it is deleted" ticked: Pro uses them.
* Fix: a PNG converted to JPEG on upload whose backup had gone was still offered for restore; it now says no backup is kept.
* For developers: filters `accessnow_image_optimizer_tabs`, `accessnow_image_optimizer_help_tabs`, `accessnow_image_optimizer_lcp_enabled`, `accessnow_image_optimizer_backup_roots`, `accessnow_image_optimizer_metadata_fields` and `accessnow_image_optimizer_credit_text`, actions `accessnow_image_optimizer_render_tab`, `accessnow_image_optimizer_deactivate` and `accessnow_image_optimizer_backups_deleted`, and the function `accessnow_image_optimizer_apply_metadata()`.
* Includes PEL (PHP Exif Library, GPLv2 or later) under the plugin's own namespace.

= 1.3.0 =
* New: put an original back. "Restore original" in the Media Library, or every image at once (resumable) under Settings > Image Optimizer > Restore and convert. The backup is copied back, the thumbnails and WebP/AVIF copies are made again, and the bulk optimizer leaves the image alone afterwards. Images without a backup are reported and left as they are.
* New: an Optimisation column in the Media Library's list view, and the same in each image's details, showing the savings, with Optimise now and Restore original buttons.
* New: remove location and camera details (EXIF, IPTC, XMP, including GPS) when images are optimised, keeping the colour profile and, optionally, the copyright and author. Untouched originals are cleaned without re-saving their pixels. On for new installs; sites upgrading keep their current behaviour until they switch it on.
* New: faster first image. The first large image on each page is no longer lazy-loaded and gets fetchpriority="high", and images without width and height get them, working alongside WordPress's own handling and the picture tags. On for new installs; off on upgrading sites until switched on.
* New: GIF images. Static GIFs are recompressed (never bigger) and get WebP/AVIF copies; with Imagick, animated GIFs keep every frame and get animated WebP copies. With GD, animated GIFs are left alone, and Server Status says so. Picture tags and the .htaccess rewrite now cover .gif. On for all sites: the next bulk run includes GIFs already in the library.
* New: PNG photos to JPEG (off by default). Opaque PNGs are converted when the JPEG is at most half the size; links in posts and pages follow, and the PNG is kept so Restore original undoes it. Count first, then convert, on the Restore and convert tab.
* New: exclude list by folder or file-name pattern, file size and pixel size.
* New: savings report tab, by format and over time, with a chart and a data table. Counting starts with this version.
* New: WP-CLI commands `wp image-optimizer bulk`, `restore`, `stats` and `convert-png`.
* Fix: the Help tab's article links on the settings page pointed at a broken address.
* Accessibility: the settings page now has one button with the id "submit"; new controls are labelled, keyboard-operable and announce their results.

= 1.2.1 =
* Fix: AVIF copies of photos could be bigger than the JPEG they replace, because AVIF used the same quality number as JPEG and WebP. AVIF now has its own quality setting, 15 below Quality to begin with (70), which looks the same at a much smaller size. The `accessnow_image_optimizer_avif_quality` filter can change it.
* A WebP or AVIF copy that isn't smaller than its original is never served or kept, so a visitor never downloads a bigger file.
* Fix: the bulk optimiser's results compared every thumbnail's copies against the main image alone. Both sides now include the thumbnail sizes, and the combined bar is labelled "Disk Space Used (All Formats)".
* Accessibility: the green and red status text on the settings page now meets WCAG AA contrast.
* New: Help articles and Get support links on the Plugins screen, and a Help tab on the settings page linking each part of it to its help article.
* After upgrading, every image goes back in the bulk queue: run the bulk optimiser once to replace any AVIF copy that turned out bigger than its original. Copies that are already smaller are kept.

= 1.2.0 =
* Backups of originals are now on by default for new installs, and the default maximum width is 2560 px (the same as WordPress). Existing sites keep the settings they saved.
* Uses WordPress's own image editor: Imagick where available, which keeps colour profiles, with a faster decode for big JPEGs; GD otherwise. Requires WordPress 6.5.
* Thumbnail copies are made in the background after an upload, so uploads are quicker.
* The bulk optimiser keeps its queue in post meta and works in batches of about 20 seconds instead of two images, so large libraries finish in far fewer requests. The old list converts automatically.
* New: server check on the settings page for the backups folder and the .htaccess rewrite.
* New: `accessnow_image_optimizer_use_picture` filter to switch off the `<picture>` wrapper.
* Fix: a WebP/AVIF copy older than its original (a regenerated thumbnail, or a new upload that reused the name of a deleted image) is remade instead of being served.
* Fix: resized PNGs no longer come out bigger than necessary, and an original is never replaced by a bigger file.
* Fix: images too large for the server's memory are skipped with a message instead of breaking the upload.
* Fix: the quality setting now also applies when a theme sets its own JPEG quality, and another plugin's output-format mapping can no longer write a WebP over a JPEG.
* Fix: feeds are no longer wrapped in `<picture>`; uninstalling on a large multisite network covers every site.
* The backups folder is guarded by an empty `index.html` instead of a PHP file (an old `index.php` guard is removed), and paths in messages follow the site's real uploads location.

= 1.1.0 =
* New name and folder (`accessnow-image-optimizer`), with automatic migration from 1.0.
* Each thumbnail size gets its own copy; copies are named `photo.jpg.webp`, so `photo.jpg` and `photo.png` no longer share one.
* Safer file handling: originals are only replaced after the new file is checked, deletes are limited to the uploads folder, and the `.htaccess` block is written with WordPress's own functions.
* Optional backups, disk space estimate and low-disk guard; translation ready.

= 1.0.0 =
* First release.

== Upgrade Notice ==

= 1.4.0 =
See and delete the space your backups use (nothing is deleted unless you ask). Choose which photo details images keep; sites that removed them keep doing so. Titles and captions import again. The optional credit stays off.

= 1.3.0 =
Restore originals, a Media Library column, GIF support, a savings report, WP-CLI and more. Removing location details and the faster-first-image changes stay off on upgrading sites until you switch them on in the settings. GIFs join the bulk queue.

= 1.2.1 =
Smaller AVIF copies, with their own quality setting. Run the bulk optimiser once after upgrading to replace any AVIF copy that is bigger than its original.

= 1.2.0 =
Uses WordPress's image editor (Imagick keeps colour profiles) and needs WordPress 6.5. Faster bulk optimising and uploads. Existing settings are kept.
