Media Library
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. This sub-section’s WordPress bibliography lists the reference material consulted while preparing these pages. |
This page was written against WordPress 7.1.3 (released 2026-10-06; 7.1.4, a security release of 2026-10-28, is the
version to run in production) and WP-CLI 2.12.0, and checked on 2026-10-28. WordPress 7.1 requires PHP 7.4 at minimum,
and PHP 8.3 or newer is recommended. WordPress 7.2 is in beta and planned for
2026-12-08; nothing described here is announced to change with it. Every command below was run against the official
wordpress:7.1.3-php8.4-apache image (PHP 8.4, GD and Imagick) with a MariaDB 11.8 container, and every output block is
real output from that site.
The Media Library holds the files that Lumen Press publishes: book covers, photos from launch events, author portraits
and EPUB sample chapters. This page describes it from the site owner’s side (screens, formats, sizes, limits, alt text)
and adds the few lines of configuration a site needs, in a must-use plugin. The developer APIs behind it
(wp_get_attachment_image(), srcset, WP_Image_Editor, sideloading from code) are covered in
Images and Media API, and front-end image delivery in general (formats,
lazy loading, responsive images) in Media Optimization.
What the Media Library is
Every uploaded file becomes an attachment: a row in the posts table with post_type set to attachment, the file’s
MIME type in post_mime_type, the title in post_title, the caption in post_excerpt and the description in
post_content. The file itself goes to wp-content/uploads/ (in YYYY/MM/ sub-folders when "Organize my uploads into
month- and year-based folders" is checked under Settings > Media, the default). Three post meta keys complete the
record: _wp_attached_file (the path relative to the uploads directory), _wp_attachment_metadata (a serialized array
with width, height, every generated size, original_image and EXIF-derived image_meta) and _wp_attachment_image_alt
(the alternative text).
The table layout is described in Database Schema. An attachment may have a
parent post (post_parent): an image uploaded while editing a post is attached to it, and one uploaded from the
Library is unattached. Attachment is only bookkeeping. The same image can appear in any number of posts, and
deleting the parent does not delete the file.
Uploading requires the upload_files capability, which Authors, Editors and Administrators have and Contributors do
not. Editing or deleting someone else’s file requires edit_others_posts / delete_others_posts, so an Author manages
only their own uploads. The roles are described in
Users, Roles and Profiles.
The Media screens
Media > Library (upload.php) lists every attachment, and Media > Add Media File (media-new.php) is a drag-and-drop
upload page that shows the maximum upload size. The Library has two views, switched with the icons above the list. The
grid view shows thumbnails, filters by type (Images, Audio, Video, Documents, Spreadsheets, Archives, Unattached) and
by month, a live search and Bulk select; clicking an item opens the Attachment details dialog, which saves changes
automatically and moves between items with the arrow keys. The list view is a sortable table (File, Author, Uploaded
to, comments, Date) with Screen Options for items per page, row actions (Edit, Delete Permanently, View, Copy URL,
Download file) and Attach for unattached files.
The Attachment details dialog and the full Edit Media screen (post.php?post=<id>&action=edit) show the upload
date and author, the post it is attached to, file name, MIME type, size and dimensions, and the editable fields: Alt
Text, Title, Caption and Description. File URL is read-only, with Copy URL to clipboard and Download file
next to it. Audio files add Artist and Album fields read from the file’s tags.
Deleting is permanent. Attachments have no trash unless MEDIA_TRASH is defined as true in wp-config.php (it
defaults to false); with it, the Library gains a Trash filter and Move to Trash replaces Delete permanently.
Deleting an attachment removes the original file, every generated size and the attachment row. Content that still
refers to the file keeps a broken image, because WordPress does not track where an attachment is used.
Infinite scroll or pagination (7.1)
The grid view can load more items as the user scrolls ("infinite scrolling") or behind a Load more button. WordPress
5.8 turned infinite scrolling off by default for accessibility and performance reasons, and only a plugin using the
media_library_infinite_scrolling filter could turn it back on. WordPress 7.1 reverses the default: infinite scrolling
is on, and each user who can upload files gets a Disable infinite scrolling in the Media Library grid view checkbox
under Users > Profile. The choice is stored in the user option infinite_scrolling (the string 'false' means
disabled). The list view always paginates.
The order of precedence is: a callback on the filter, then the user’s choice, then the default. A site that needs one behaviour for everyone, for example pagination on a large catalogue with tens of thousands of covers, hooks the filter; an individual opt-out can also be set from the command line:
<?php
// In a must-use plugin: force pagination for every user, ignoring profile choices.
add_filter( 'media_library_infinite_scrolling', '__return_false' );
wp user meta update lp_author infinite_scrolling false
Success: Updated custom field 'infinite_scrolling'.
Allowed file types
WordPress decides what may be uploaded in two steps. First, the file’s extension must be in the list returned by
get_allowed_mime_types(). Second, wp_check_filetype_and_ext() reads the file’s real content type and rejects a file
whose content does not match its extension (an HTML file renamed to .jpg, for instance). A rejected file produces
"Sorry, you are not allowed to upload this file type."
The list is not fixed. get_allowed_mime_types() starts from wp_get_mime_types() (98 entries in 7.1), always removes
swf and exe, removes htm|html and js for users without the unfiltered_html capability, and then passes the
result through the upload_mimes filter. On a single site only Administrators and Editors have unfiltered_html; on
Multisite only Super Admins do, and each site’s list is further limited by Network Admin > Settings > Upload file
types (the upload_filetypes network option). Defining ALLOW_UNFILTERED_UPLOADS as true lets Administrators (Super
Admins on Multisite) upload any type; it is a debugging switch, not a production setting.
The following command prints the effective list for two users of the Lumen Press site, the Administrator lp_admin
(user 1) and the Author lp_author (user 2):
wp eval '
$all = wp_get_mime_types();
wp_set_current_user( 1 );
echo "admin removed: ", implode( ", ", array_keys( array_diff_key( $all, get_allowed_mime_types() ) ) ), "\n";
wp_set_current_user( 2 );
$a = get_allowed_mime_types();
echo "author: ", count( $a ), " removed: ", implode( ", ", array_keys( array_diff_key( $all, $a ) ) ), "\n";
'
admin removed: swf, exe
author: 94 removed: htm|html, js, swf, exe
Without any filter, an Administrator may upload 96 types: images (jpg/jpeg/jpe, gif, png, bmp, tif/tiff,
webp, avif, ico, heic, heif, heics, heifs), video (mp4/m4v, mov, webm, ogv, mkv, avi,
mpeg, wmv, flv, 3gp, 3g2 and a few older Windows formats), audio (mp3/m4a/m4b, aac, wav,
ogg/oga, flac, mid, wma, mka, ra), text (txt, csv, tsv, ics, rtx, css, vtt, dfxp, plus
htm/html and js for unfiltered_html users), documents (pdf, rtf, the Microsoft Office, OpenDocument and Apple
iWork families, oxps, xps, OneNote, WordPerfect) and archives and other files (zip, tar, gz, rar, 7z,
psd, xcf, class).
The list printed in older books predates WebP (5.8), AVIF (6.5) and HEIC (6.7) and reads as if it were fixed. In a
current install it is longer, it depends on the user’s capabilities, and on Multisite it depends on the network
setting; get_allowed_mime_types() or a test upload is the authoritative answer. EPUB and SVG are absent by default,
which matters for a publisher.
Changing the list with upload_mimes
Lumen Press publishes EPUB sample chapters and never needs bitmaps, TIFF scans, Java classes or Windows Write files. Site-wide policy like this belongs in a must-use plugin, which the theme cannot switch off. The plugin below also holds the image-size settings used later on this page:
<?php
/**
* Plugin Name: Lumen Media Policy
* Description: Upload types, image sizes and big-image scaling for the Lumen Press site.
* Version: 1.0.0
* Requires at least: 7.1
* Requires PHP: 7.4
* License: GPL-2.0-or-later
*/
defined( 'ABSPATH' ) || exit;
// Allow EPUB sample chapters; stop accepting formats the site never publishes.
function lumen_media_upload_mimes( $mimes, $user ) {
$mimes['epub'] = 'application/epub+zip';
unset( $mimes['bmp'], $mimes['tiff|tif'], $mimes['class'], $mimes['wri'] );
return $mimes;
}
add_filter( 'upload_mimes', 'lumen_media_upload_mimes', 10, 2 );
// Keep a larger "full" copy of print covers (default threshold: 2560).
function lumen_media_big_image_threshold( $threshold ) {
return 3200;
}
add_filter( 'big_image_size_threshold', 'lumen_media_big_image_threshold' );
// A 2:3 cover size used by the book templates.
function lumen_media_image_sizes() {
add_image_size( 'lp-book-cover', 600, 900, true );
}
add_action( 'after_setup_theme', 'lumen_media_image_sizes' );
The array keys are regular-expression fragments, so a removal must use the exact key (tiff|tif, not tif). Importing
an EPUB and a BMP with WP-CLI shows the filter at work; wp media import runs the same checks as the upload screen:
wp media import /var/www/html/import/the-lighthouse-keeper-chapter-1.epub \
--title="The Lighthouse Keeper, chapter 1 (EPUB)"
wp media import /var/www/html/import/old-logo.bmp
Imported file '/var/www/html/import/the-lighthouse-keeper-chapter-1.epub' as attachment ID 5.
Success: Imported 1 of 1 items.
Warning: Unable to import file '/var/www/html/import/old-logo.bmp'. Reason: Sorry, you are not allowed to upload this file type.
Error: No items imported.
Source: WP-CLI Commands: wp media import
Because the content check still runs, adding an extension to the list does not make an arbitrary file acceptable: the
real content type detected by PHP’s fileinfo must match the declared MIME type. When a legitimate file is rejected
after its type was added, the cause is usually a different detected type (some CSV and font files are detected as
text/plain or application/octet-stream). The wp_check_filetype_and_ext filter can correct that for one extension;
widening it for all files defeats the check.
SVG
SVG files are not allowed by default, and the reason is security rather than an oversight: an SVG is an XML document
that can carry <script> elements and event handlers, so an uploaded SVG opened directly from the uploads URL can run
JavaScript in the site’s origin (stored cross-site scripting). Adding svg to upload_mimes alone is unsafe. Sites
that need SVG uploads use a plugin that sanitizes each file on upload (Safe SVG is a widely used one) and restrict SVG
uploads to trusted roles. Theme SVGs (icons, logos) belong in the theme’s files and do not need the Media Library.
Modern image formats
| Format | Since | What WordPress does with it |
|---|---|---|
WebP |
5.8 |
Upload and sub-sizes in WebP when the server’s GD or Imagick supports it. |
AVIF |
6.5 |
Same as WebP. Server support is listed under Tools > Site Health > Info > Media Handling. In 7.1, client-side processing (below) decodes AVIF in the browser, so AVIF uploads work even on hosts without server support. |
HEIC/HEIF |
6.7 |
Accepted and converted to JPEG on the server when Imagick supports HEIC; the original HEIC file is kept. Without support, the uploader tells the user to convert the image first. 7.1’s client-side processing decodes HEIC in browsers whose platform has the codec (Chromium on macOS or on Windows with HEVC support, Safari on macOS) and uploads a JPEG, keeping the HEIC as a companion file. |
HDR gain maps (UltraHDR JPEG) |
7.1 |
Detected on upload by client-side processing; the original is stored unmodified and every generated size keeps its gain map. Format conversion is skipped for these files, because converting the codec would strip the gain map. |
SVG |
not supported |
Rejected by default (see above). |
The HEIC conversion depends on the PHP process that handles the upload. In the official images, the Apache image’s
Imagick lists HEIC and HEIF among its formats, but the wordpress:cli image’s Imagick does not, so a wp media import
of a .heic file from that container creates an attachment with no sizes and no metadata. Running WP-CLI inside the web
container gives the expected result. The Apache image has no WP-CLI, so the first three lines copy the wp binary from
the CLI image into it. The attachment keeps the image/heic MIME type, its main file and every size are JPEGs, and
original_image names the HEIC file:
cid=$(docker create wordpress:cli-php8.4)
docker cp "$cid":/usr/local/bin/wp ./wp-cli.phar && docker rm "$cid"
docker compose cp ./wp-cli.phar wordpress:/usr/local/bin/wp
docker compose exec -u www-data wordpress \
wp media import /var/www/html/import/author-portrait.heic --alt="Portrait of the author" --porcelain
docker compose exec -u www-data wordpress wp eval '
$m = wp_get_attachment_metadata( 11 );
echo get_post_mime_type( 11 ), " file=", $m["file"], " original=", $m["original_image"], "\n";
foreach ( $m["sizes"] as $k => $s ) { echo " $k ", $s["file"], " ", $s["mime-type"], "\n"; }
'
11
image/heic file=2026/10/author-portrait-1.jpg original=author-portrait-1.heic
medium author-portrait-1-300x225.jpg image/jpeg
thumbnail author-portrait-1-150x150.jpg image/jpeg
medium_large author-portrait-1-768x576.jpg image/jpeg
lp-book-cover author-portrait-1-600x600.jpg image/jpeg
The conversion is driven by the image_editor_output_format filter, whose default mapping in 7.1 sends image/heic,
image/heif, image/heic-sequence and image/heif-sequence to image/jpeg. The same filter can make WordPress
generate WebP or AVIF files from JPEG uploads. The "full" file and every size are then written as AVIF, the post MIME
type stays image/jpeg, and the uploaded JPEG is kept and named in original_image:
<?php
// In a must-use plugin: generate AVIF sub-sizes from JPEG uploads.
add_filter(
'image_editor_output_format',
function ( $formats ) {
$formats['image/jpeg'] = 'image/avif';
return $formats;
}
);
Importing a 1200 × 800 JPEG with that filter active and printing the MIME type, main file and sizes gives the output
below; original_image is launch-night-1.jpg:
image/jpeg 2026/10/launch-night-1.avif
medium launch-night-1-300x200.avif
large launch-night-1-1024x683.avif
thumbnail launch-night-1-150x150.avif
medium_large launch-night-1-768x512.avif
lp-book-cover launch-night-1-600x800.avif
Core does not serve AVIF or WebP with a JPEG fallback through <picture>; all browsers in current use decode WebP and
AVIF, and a site with older clients leaves the default in place. Core also does not convert images that were uploaded
before a filter was added.
Image sizes and big-image scaling
For each uploaded image WordPress creates resized copies. Three are set under Settings > Media, and three more are registered by core without a screen:
| Size | Default box | Crop | Set by |
|---|---|---|---|
|
150 × 150 |
hard (exact) |
Settings > Media, "Crop thumbnail to exact dimensions" |
|
300 × 300 |
soft (proportional) |
Settings > Media |
|
768 wide |
soft |
core (since 4.4), used in |
|
1024 × 1024 |
soft |
Settings > Media |
|
1536 × 1536 |
soft |
core (since 5.3) |
|
2048 × 2048 |
soft |
core (since 5.3) |
Themes and plugins add their own with add_image_size(). A size is only generated when the original is larger than its
box; WordPress never upscales. wp media image-size lists what is registered on the Lumen Press site, including the
lp-book-cover size from the must-use plugin:
wp media image-size
+---------------+-------+--------+------+-------+
| name | width | height | crop | ratio |
+---------------+-------+--------+------+-------+
| full | | | N/A | N/A |
| 2048x2048 | 2048 | 2048 | soft | N/A |
| 1536x1536 | 1536 | 1536 | soft | N/A |
| large | 1024 | 1024 | soft | N/A |
| medium_large | 768 | 0 | soft | N/A |
| lp-book-cover | 600 | 900 | hard | 2:3 |
| medium | 300 | 300 | soft | N/A |
| thumbnail | 150 | 150 | hard | 1:1 |
+---------------+-------+--------+------+-------+
Every registered size costs disk space and upload time for every image, whether the site displays it or not. Themes that register many sizes, and plugins that each add a few, are a common reason for slow uploads and large backups.
Big-image scaling (2560 px)
Since WordPress 5.3, an uploaded image wider or taller than 2560 pixels is scaled down to fit 2560 × 2560, and the
scaled copy, saved with a -scaled suffix, becomes the "full" size that content uses. The untouched original stays in
the uploads folder, its file name is stored as original_image in the attachment metadata, and
wp_get_original_image_path() / wp_get_original_image_url() return it. The threshold is changed with the
big_image_size_threshold filter; returning false turns scaling off.
Lumen Press raised the threshold to 3200 pixels in the must-use plugin above. Importing a 4000 × 6000 cover shows the result:
wp media import /var/www/html/import/the-lighthouse-keeper-cover.jpg \
--title="The Lighthouse Keeper cover" \
--alt="Cover of The Lighthouse Keeper: a lighthouse on a cliff at dusk" \
--caption="Cover art for The Lighthouse Keeper" --porcelain
wp eval '
$m = wp_get_attachment_metadata( 4 );
echo $m["file"], " ", $m["width"], "x", $m["height"], "\noriginal_image: ", $m["original_image"], "\n";
foreach ( $m["sizes"] as $k => $s ) { echo str_pad( $k, 14 ), $s["file"], "\n"; }
echo "original path: ", str_replace( ABSPATH, "", wp_get_original_image_path( 4 ) ), "\n";
'
4
2026/10/the-lighthouse-keeper-cover-scaled.jpg 2133x3200
original_image: the-lighthouse-keeper-cover.jpg
medium the-lighthouse-keeper-cover-200x300.jpg
large the-lighthouse-keeper-cover-683x1024.jpg
thumbnail the-lighthouse-keeper-cover-150x150.jpg
medium_large the-lighthouse-keeper-cover-768x1152.jpg
1536x1536 the-lighthouse-keeper-cover-1024x1536.jpg
2048x2048 the-lighthouse-keeper-cover-1365x2048.jpg
lp-book-cover the-lighthouse-keeper-cover-600x900.jpg
original path: wp-content/uploads/2026/10/the-lighthouse-keeper-cover.jpg
The REST API returns the same data under media_details (width, height, file, original_image and a sizes
object with the file, dimensions, MIME type and source_url of each size), so a headless front end can build its own
srcset; see REST API: Using It.
Regenerating sizes with wp media regenerate
Changing Settings > Media, adding a size with add_image_size() or switching to a theme with other sizes affects only
future uploads. Existing images keep their old files until they are regenerated:
# Every image, every size (deletes and recreates the sub-size files).
wp media regenerate --yes
# One attachment, one size: after adding lp-book-cover.
wp media regenerate 4 --image_size=lp-book-cover --yes
# Only the sizes that are missing, leaving existing files untouched.
wp media regenerate --only-missing --yes
Found 2 images to regenerate.
1/2 Regenerated thumbnails for "launch-night" (ID 7).
2/2 Regenerated thumbnails for "The Lighthouse Keeper cover" (ID 4).
Success: Regenerated 2 of 2 images.
Found 1 image to regenerate.
1/1 Regenerated "lp-book-cover" thumbnail for "The Lighthouse Keeper cover" (ID 4).
Success: Regenerated 1 of 1 images.
Found 2 images to regenerate.
1/2 No thumbnail regeneration needed for "launch-night" (ID 7).
2/2 No thumbnail regeneration needed for "The Lighthouse Keeper cover" (ID 4).
Success: Regenerated 2 of 2 images.
--only-missing is the safe default on a production site: a full regeneration rewrites thousands of files, can take
hours on a large library, and changes file names when crop settings change, which breaks hard-coded URLs in old content.
Sizes that are no longer registered are not deleted by either form; --delete-unknown removes them.
Editing images
The Edit Image screen
From the Library, Edit Image (in the Attachment details dialog or on the Edit Media screen) opens the classic image editor: crop (drag a selection, optionally with a fixed aspect ratio or exact pixel values), rotate 90° left or right, flip vertically or horizontally, scale down proportionally, and undo or redo. Changes can be applied to all sizes, to the thumbnail only, or to all sizes except the thumbnail. Editing is non-destructive: WordPress writes new files with a suffix and keeps the previous ones, and Restore original image returns to the uploaded file. While cropping, the arrow keys move the selection by 10 px (1 px with Shift) and Ctrl+arrow resizes it.
The media editor modal in the block editor (7.1)
WordPress 7.1 replaced the inline cropping tool of the block editor with a media editor modal. It opens from the same Crop button in the Image block toolbar (and the Cover block’s background image), and combines in one window:
-
freeform cropping and cropping to an aspect ratio, with the crop magnified to fill the canvas;
-
horizontal and vertical flipping;
-
rotation with fine-grained control and snapping;
-
metadata editing (alt text and caption) in a side panel;
-
full keyboard operation: Tab through the crop area, handles, toolbar and panel, arrow keys move or resize the crop, and undo/redo shortcuts work.
The crop in the modal applies to the image as used in that block. The editor saves the result through
POST /wp/v2/media/<id>/edit, which writes a new file and creates a new attachment (with an -edited file name),
leaving the original attachment and every other post that uses it unchanged. Flip support was added to that endpoint in
6.9. On the Lumen Press site, a request to that endpoint for the 1200 × 800 photo with ID 7 (crop to the left half,
flip horizontally) answered 201 with a new attachment, ID 8, file launch-night-edited.jpg, 600 × 800, while ID 7
stayed as it was. Crop values in the request are percentages of the source image.
Manual pixel crop controls and Restore original are not part of the
modal in 7.1; they remain on the Library’s Edit Image screen. Each crop adds an attachment, so a heavily edited
post leaves several -edited copies in the Library.
Client-side image processing (7.1)
Until 7.0 every image size was produced on the server by GD or Imagick, which is where large phone photos ran into
memory_limit, max_execution_time and missing codecs. WordPress 7.1 moves that work into the browser when it can.
@wordpress/vips, a WebAssembly build of libvips running in a Web Worker, compresses, resizes, rotates according to
EXIF, converts formats and generates every registered size before upload. The server then receives finished files.
How an upload travels on that path:
-
The block editor uploads the original (or the browser-decoded JPEG for HEIC) to
POST /wp/v2/mediawithgenerate_sub_sizesset tofalse, which creates the attachment and fireswp_generate_attachment_metadatawith the context'create'. -
Each sub-size is uploaded separately to
POST /wp/v2/media/<id>/sideload, at most five uploads and two image operations at a time. -
POST /wp/v2/media/<id>/finalizerunswp_generate_attachment_metadataagain with the context'update'. If it fails, the error is logged and the upload still succeeds.
Because every size is its own request, a dropped connection loses one file, not the batch. Failed requests are retried automatically with exponential backoff, uploads pause when the browser goes offline and resume when it reconnects, and the editor shows progress per file. This is the "upload retry" of the 7.1 release.
When it runs
The feature turns itself on only when every condition below holds; otherwise the classic server-side path is used, without an error:
| Condition | Detail |
|---|---|
Secure context |
|
Browser |
Chromium 137 or newer (Chrome, Edge, Opera, Brave). On block editor screens WordPress sends the header
|
Device and network |
More than 2 GB of device memory, at least two CPU cores, a connection not reported as |
Content Security Policy |
A site that sends a CSP must allow |
Screen |
Block editor, Site Editor and block widgets screens only, for users with |
Format |
JPEG, PNG, GIF, WebP and AVIF through libvips, HEIC/HEIF decoded to JPEG. The list is fixed in code. Video, audio and documents upload directly. The Media Library screen and Add Media File keep using the server. |
On the Lumen Press container, a request to the REST index with Host: localhost lists six /wp/v2/media routes
(including /sideload and /finalize), and the same request with any other host name over plain HTTP lists four.
What still applies, and what changes for plugins
The client reads the server’s settings, so the existing filters keep working: big_image_size_threshold,
image_editor_output_format, wp_editor_set_quality and jpeg_quality, image_save_progressive, image_strip_meta,
image_max_bit_depth and every size registered with add_image_size(). Lumen Press’s 3200 px threshold and
lp-book-cover size are therefore honoured on both paths.
Three server hooks never fire on the client path, because no server image editor runs: wp_image_editors,
image_make_intermediate_size and image_memory_limit. wp_handle_upload fires once per sideloaded file, and
wp_generate_attachment_metadata fires twice. A plugin that, for example, sends every new image to a CDN or an
optimization service from wp_generate_attachment_metadata must be idempotent across the 'create' and 'update'
passes.
A site that relies on a server-side image pipeline, or whose editors work on weak machines that pass the checks, turns the feature off:
<?php
// In a must-use plugin: keep all image processing on the server.
add_filter( 'wp_client_side_media_processing_enabled', '__return_false' );
A site with a Content Security Policy adds the worker source instead:
Content-Security-Policy: worker-src 'self' blob:;
Cross-origin isolation has a side effect on the editor screens: WordPress adds crossorigin="anonymous" to external
scripts there, and a third-party resource that does not send CORS headers can stop loading in the editor. Checking
window.crossOriginIsolated in the browser console tells whether the isolation is active.
Upload limits
The maximum upload size shown on Add Media File is wp_max_upload_size(), the smaller of PHP’s
upload_max_filesize and post_max_size, passed through the upload_size_limit filter. Returning a larger value from the filter changes the number shown but
not what PHP accepts; raising the limit means changing PHP (and the web server’s request body limit, client_max_body_size on nginx). The
server side, including the nginx and .htaccess variants and what happens when each limit is exceeded, is described in
Requirements and Hosting.
The official wordpress Docker image ships PHP’s defaults, upload_max_filesize = 2M and post_max_size = 8M, so a
fresh container accepts nothing larger than 2 MB, less than many phone photos. Lumen Press mounts an extra ini file:
; Lumen Press: allow 64 MB uploads (EPUBs, print-ready covers).
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 120
The file is mounted into the official image’s PHP configuration directory, here in the wordpress service of the
Compose file from Local Development Environments, next to the
must-use plugin of this page:
services:
wordpress:
image: wordpress:7.1.3-php8.4-apache
volumes:
- wordpress:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
- ./lumen-media.php:/var/www/html/wp-content/mu-plugins/lumen-media.php:ro
docker compose up -d
docker compose exec wordpress php -r 'echo ini_get("upload_max_filesize"), " ", ini_get("post_max_size"), "\n";'
64M 64M
After the change, size_format( wp_max_upload_size() ) reports 64 MB in a request served by Apache. With client-side
processing, sub-sizes arrive as separate, smaller requests, but the original file still has to fit the limit. On
Multisite, Network Admin > Settings adds a lower Max upload file size (in KB) and a per-site upload space quota on
top of PHP’s limits.
Alt text
Alternative text is what screen readers announce and what appears when an image fails to load. WordPress stores it in two places, and the difference explains most "I fixed the alt text but the page did not change" reports:
-
The Alt Text field in the Library (Attachment details or Edit Media) is stored in
_wp_attachment_image_altand is the default copied into a block when the image is inserted. -
After insertion the Image block keeps its own
altattribute in the post content. Changing the Library field later does not update posts that already use the image, and changing it in the block does not update the Library. The 7.1 media editor modal edits the Library’s alt text and caption of the attachment it works on.
Good practice, following the W3C’s alt decision tree: describe what the image conveys in its context ("Cover of The
Lighthouse Keeper: a lighthouse on a cliff at dusk"), not "image of"; describe the destination when the image is a link;
leave alt empty for purely decorative images (in the Image block, by leaving the field blank so that WordPress outputs
alt=""); do not repeat a caption word for word. The same photo may need different alt text in different posts, which
is what the block-level attribute is for. Accessibility for theme authors is covered in
Theme Accessibility and Releasing and in general in
Accessibility.
Images imported in bulk often arrive without alt text. wp media import --alt sets it on import, and a query finds the
images still missing it so that an editor can fill them in:
wp db query "SELECT p.ID, p.post_title FROM lp_posts p
LEFT JOIN lp_postmeta m ON m.post_id = p.ID AND m.meta_key = '_wp_attachment_image_alt'
WHERE p.post_type = 'attachment' AND p.post_mime_type LIKE 'image/%'
AND (m.meta_value IS NULL OR m.meta_value = '') ORDER BY p.ID;"
wp post meta update 10 _wp_attachment_image_alt "Lumen Press autumn reading evening banner"
ID post_title
10 event-banner
Success: Updated custom field '_wp_attachment_image_alt'.
Source: WP-CLI Commands: wp db query
Source: W3C WAI: An alt Decision Tree
Errors and pitfalls
| Symptom | Cause and fix |
|---|---|
"Sorry, you are not allowed to upload this file type." |
The extension is not in |
"The uploaded file exceeds the upload_max_filesize directive in php.ini." or a generic "HTTP error" |
PHP or web-server limits. Raise |
"The server cannot process the image. This can happen if the server is busy or does not have enough resources to complete the task." |
The server ran out of memory or time while creating sizes, typically for very large photos. Raise |
HEIC upload refused with a request to convert to JPEG |
The PHP that handled the upload has no HEIC-capable Imagick. Check Site Health > Info > Media Handling; with WP-CLI, run the command inside the web container. |
An image is visibly smaller than the file uploaded |
Big-image scaling at 2560 px. The original is still on disk; raise or disable |
Client-side processing never starts |
Plain HTTP on a non-localhost host, a non-Chromium browser, a weak device, Save-Data, or a CSP without |
Security
-
The upload check covers extension and content. Do not weaken
wp_check_filetype_and_extglobally, and do not enableALLOW_UNFILTERED_UPLOADSin production. -
Uploads are public by URL. Anything placed in the Media Library, including "unattached" files and the originals kept by big-image scaling, can be fetched by anyone who knows or guesses the URL. Files that must stay private (manuscripts under review, contracts) do not belong there.
-
The uploads directory must never execute PHP; blocking it at the web server is part of Security Hardening, and the permissions (644 for files, 755 for directories) are in File Permissions and Filesystem.
-
Uploaded photos carry EXIF data, including GPS coordinates. Generated sub-sizes are written without most of it (
image_strip_metadefaults totrue), but the original file keeps it; authors should remove location data before uploading photos of private places. -
SVG requires sanitizing (see above), and HTML and JavaScript uploads are limited to
unfiltered_htmlusers for the same reason. -
Media blocks are output too: WordPress 7.1.4 (2026-10-28) fixed a stored XSS in the Playlist and Image blocks, so sites that embed media from the Library should run that security release or a later one (WordPress News: WordPress 7.1.4).
Where older books and tutorials are outdated
| Older advice | Current behaviour |
|---|---|
A fixed list of allowed upload types, as printed in older books |
The list depends on capabilities, the |
Large photos must be resized before uploading |
WordPress scales anything above 2560 px since 5.3 and keeps the original; since 7.1 a supported browser resizes and compresses before upload. |
HEIC photos from phones must be converted first |
Converted to JPEG automatically since 6.7 (server with HEIC-capable Imagick) and in the browser since 7.1. |
The Library pages with a Load more button |
Infinite scrolling is the default again since 7.1, with a per-user opt-out. |
The full list of corrections is in Where Older Books and Tutorials Are Outdated.