External Image Importer – Import Remote Images to the Media Library
External Image Importer automatically uploads externally-hosted images to your Media Library when you paste external content into the WordPress editor, then rewrites every URL to point at your own local copy. It is built SSRF-safe: every remote fetch is validated and hardened before the file is downloaded.
Remove the hassle of importing external images by hand when you:
- migrate to WordPress from another CMS;
- work across multiple environments, such as development, staging and production;
- paste external content drafts written elsewhere.
The dev-to-live problem this solves
When you copy or migrate WordPress content between environments – for example from a dev or staging site to your live site – images embedded in that content stay hotlinked to the source domain. The block HTML (or pasted classic-editor markup) keeps pointing at https://dev.example.com/..., so your production pages silently load images from staging. If the source environment is private, password-protected or taken down after launch, those images break.
This plugin fixes that automatically: it finds the external image URLs, pulls the files into your own Media Library, and updates the content so everything is served from your own domain.
Key features
- Safe imports. Every download is verified to be a genuine, decodable image of the expected type before any URL is rewritten. If a file fails that check – for example the ”image” was really a geo-blocked placeholder page or an error document – it is discarded and your original reference is left unchanged, so a broken file is never put in place of a working URL.
- Self-hosted images. Serving images from your own domain means search engines index them on your domain and your pages no longer depend on a third party staying online.
- Full-size originals. It imports the full-size original and lets WordPress regenerate every registered size locally, then points the markup at the matching local size so the layout is unchanged.
- Duplicate-aware. Images that already exist in your Media Library (by source URL or exact filename) are reused instead of downloaded again.
- Zero configuration. It works on activation; there is nothing to set up.
Security
Server-side remote fetching is handled with defence in depth:
- SSRF guard on every fetch. Before any download, the target URL is validated. Requests to private, reserved, loopback, link-local, ULA, CGNAT and IPv6 transition (6to4 / NAT64) ranges are blocked – including the cloud metadata endpoint
169.254.169.254. All resolved A/AAAA records must be public. - IP pinning and redirect re-validation. The validated public IP is pinned for the actual download (cURL transport), and redirects are followed manually with the SSRF guard re-run on every hop. A public, validator-passing URL therefore cannot be DNS-rebound or 30x-redirected to an internal address after the check.
- Scheme, port and credential locking. Only
httpandhttps, only ports80and443, and URLs containing embedded credentials (user:pass@) are refused. - Real content-type verification. After download the actual file type is checked, not just the extension. Anything that is not a genuine raster image is deleted and rejected.
- SVG and ICO are skipped by default. SVG can carry script and ICO is a common smuggling vector, so both are excluded.
- Download caps. A per-request timeout and a maximum file size are enforced (and filterable) so a hostile or runaway source cannot tie up or fill your server.
How it works
Paste your content as you normally would, then Publish or Save. When you do, the plugin scans the content for externally-hosted images, securely downloads each one into your Media Library, and rewrites the markup to your local copy at the same size it referenced, so your layout is unchanged.
- Automatically on save. A single canonical save hook covers both the Block (Gutenberg) editor and the Classic editor, so newly pasted external images are imported the moment you save.
- It skips duplicates. Before downloading, it checks whether the image already exists in your Media Library – by the exact remote source URL it previously imported, and by exact filename against your whole Media Library (including images you uploaded normally). If a match exists it is reused instead of downloaded again.
- It imports the full-size original. Even when your content references a scaled image (for example
photo-1024x670.webp), the plugin fetches the full original and lets WordPress regenerate every registered size locally, then points the markup at the matching local size. - WP-CLI for retroactive fixes. Run
wp auei localiseto sweep content that was saved before the plugin was active, with--dry-run,--post_type,--post_idand--limitoptions.
What it scans
src,data-srcanddata-lazy-srcattributessrcset(every candidate, with width and density descriptors)- Gutenberg block-comment JSON (
url/src/href) - CSS
background-image: url(...)in inline styles
Compatibility
- Block and Classic editors. A single save hook (
wp_insert_post_data) covers both. - Single sites and multisite. On multisite, each site’s external images are imported into that site’s own Media Library.
- Alt text and captions are preserved. The plugin only rewrites image URLs and remaps block attachment IDs; your alt text, captions and other attributes are left untouched.
- Supported formats: JPEG, PNG, GIF, WebP, AVIF and BMP. SVG and ICO are excluded by default for security.
- Actively maintained and tested against the current WordPress release.
Privacy and external connections
This plugin does not phone home and does not send your data to any third party. There are no analytics, no tracking and no external API calls. The only outbound requests it makes are to the image URLs that already appear in your own content, purely to download those images into your Media Library. Nothing about your site, content or visitors is transmitted anywhere else.
Filters and Hooks
All filters use the auei_ prefix.
auei_enabled(bool, defaulttrue) – master on/off switch for the whole plugin.auei_whitelisted_domains(string[], default empty) – domains whose images are never imported (left as external), for example a trusted CDN.auei_import_full_size(bool, defaulttrue) – import the full-size original (strip a-WxHscaled suffix) so the destination regenerates every size. Set to false to import only the exact file referenced.auei_remap_ids(bool, defaulttrue) – rewrite the source-site attachment ID in Gutenberg blocks ("id":N/wp-image-N) to the new local attachment ID.auei_allowed_extensions(string[], defaultjpg, jpeg, png, gif, webp, avif, bmp) – image extensions eligible for import.svgandicoare excluded by default.auei_allowed_schemes(string[], defaulthttp, https) – URL schemes permitted for fetching.auei_allowed_ports(int[], default80, 443) – explicit ports permitted on a target URL.auei_blocked_ip_ranges(string[], default empty) – extra CIDR ranges to block, in addition to the built-in private/reserved set.auei_max_file_size(int, default8388608, i.e. 8 MB) – maximum download size in bytes; larger files are rejected.auei_download_timeout(int, default20) – download timeout in seconds.auei_max_redirects(int, default3) – maximum HTTP redirects to follow; every hop is re-validated by the SSRF guard.auei_reuse_by_filename(bool, defaulttrue) – reuse an existing Media Library image with the same filename (any attachment, not just ones this plugin imported) instead of downloading a duplicate. Matched on the exact stored-file basename.auei_allow_url(true or WP_Error, default validator result) – final override on a per-URL basis; returntrueto force-allow or aWP_Errorto force-block.auei_force_logging(bool, defaultfalse) – force debug logging even whenWP_DEBUGis off.
