Title: External Image Importer &#8211; Import Remote Images to the Media Library
Author: NerdCow
Published: <strong>July 11, 2026</strong>
Last modified: July 11, 2026

---

Search plugins

![](https://ps.w.org/nerdcow-external-image-importer/assets/banner-772x250.png?rev
=3603890)

![](https://ps.w.org/nerdcow-external-image-importer/assets/icon-256x256.png?rev
=3603890)

# External Image Importer – Import Remote Images to the Media Library

 By [NerdCow](https://profiles.wordpress.org/nerdcow/)

[Download](https://downloads.wordpress.org/plugin/nerdcow-external-image-importer.1.0.1.zip)

 * [Details](https://wordpress.org/plugins/nerdcow-external-image-importer/#description)
 * [Reviews](https://wordpress.org/plugins/nerdcow-external-image-importer/#reviews)
 *  [Installation](https://wordpress.org/plugins/nerdcow-external-image-importer/#installation)
 * [Development](https://wordpress.org/plugins/nerdcow-external-image-importer/#developers)

 [Support](https://wordpress.org/support/plugin/nerdcow-external-image-importer/)

## Description

**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 `http` and `https`, only ports `
   80` and `443`, 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 localise` to sweep content that
   was saved _before_ the plugin was active, with `--dry-run`, `--post_type`, `--
   post_id` and `--limit` options.

#### What it scans

 * `src`, `data-src` and `data-lazy-src` attributes
 * `srcset` (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, default `true`) – 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, default `true`) – import the full-size original(
   strip a `-WxH` scaled suffix) so the destination regenerates every size. Set 
   to false to import only the exact file referenced.
 * `auei_remap_ids` (bool, default `true`) – rewrite the source-site attachment 
   ID in Gutenberg blocks (`"id":N` / `wp-image-N`) to the new local attachment 
   ID.
 * `auei_allowed_extensions` (string[], default `jpg, jpeg, png, gif, webp, avif,
   bmp`) – image extensions eligible for import. `svg` and `ico` are excluded by
   default.
 * `auei_allowed_schemes` (string[], default `http, https`) – URL schemes permitted
   for fetching.
 * `auei_allowed_ports` (int[], default `80, 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, default `8388608`, i.e. 8 MB) – maximum download size
   in bytes; larger files are rejected.
 * `auei_download_timeout` (int, default `20`) – download timeout in seconds.
 * `auei_max_redirects` (int, default `3`) – maximum HTTP redirects to follow; every
   hop is re-validated by the SSRF guard.
 * `auei_reuse_by_filename` (bool, default `true`) – 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; return `true` to force-allow or a `WP_Error` to force-block.
 * `auei_force_logging` (bool, default `false`) – force debug logging even when `
   WP_DEBUG` is off.

## Installation

 1. Upload the `nerdcow-external-image-importer` folder to `wp-content/plugins/`, or
    install the plugin through the **Plugins > Add New** screen.
 2. Activate the plugin through the **Plugins** screen in WordPress.
 3. That is it – no configuration is required. External images are imported automatically
    the next time you save a post or page.
 4. (Optional) To fix content saved before activation, run the WP-CLI command: `wp 
    auei localise --dry-run` to preview, then `wp auei localise` to apply.

## FAQ

### Will it ever replace a working image with a broken one?

No – this is a core safety behaviour. Every download is verified to be a genuine,
decodable image of the expected MIME type before any URL is rewritten. If the downloaded
file is not a real image (for example a geo-blocked placeholder page or an error
document), it is discarded and your original external URL is left exactly as it 
was. A broken local copy is never put in place of a URL that was working.

### Does it work with the Block editor and the Classic editor?

Yes. A single save hook (`wp_insert_post_data`) covers both the Block (Gutenberg)
editor – which saves via the REST API – and the Classic editor.

### Does it work on multisite?

Yes. It runs per site, so each site’s external images are imported into that site’s
own Media Library.

### What happens to alt text and captions?

They are preserved. The plugin only rewrites image URLs and remaps the attachment
ID stored in Gutenberg blocks; it does not touch your alt text, captions or any 
other attributes.

### Which image formats are supported?

JPEG, PNG, GIF, WebP, AVIF and BMP. SVG and ICO are excluded by default for security(
SVG can embed script, and ICO is a common smuggling vector). If you fully trust 
your sources you can extend the allowed set via the `auei_allowed_extensions` filter,
but doing so is at your own risk.

### Does it send my data anywhere?

No. The plugin uses no external services and contains no tracking or analytics. 
The _only_ outbound HTTP requests it makes are to the image URLs that already exist
in your own content, purely to download those images into your local Media Library.

### Which image size does it import, and what happens to the block in the editor?

It imports the **full-size original**. WordPress content usually references a _scaled_
image (for example `photo-1024x670.webp`); the plugin strips that `-WxH` suffix,
fetches the full original, and lets WordPress regenerate every registered size locally–
so you have the thumbnail, medium, large and full versions, just like a normal upload.
The image in your content is then rewritten to the local copy at the **same size
it referenced**, so the layout is unchanged. It also remaps the attachment ID stored
in the block (`"id":N` / `wp-image-N`) to the new local attachment, so the editor’s
image controls keep working and no stale or colliding source ID is left behind. 
Both behaviours can be turned off with the `auei_import_full_size` and `auei_remap_ids`
filters.

### Will it re-download images it has already imported?

No. Before downloading, the plugin checks whether the image already exists in your
Media Library and reuses it:

 1. by the exact remote source URL it previously imported (stored in `_auei_source_url`
    post meta); and
 2. by exact filename against your _whole_ Media Library, including images you uploaded
    normally, not just ones this plugin imported.

Point 2 is the important one for the dev-to-live workflow: if the destination already
has the images (for example the page was partly built on live before), they are 
reused instead of re-uploaded as duplicates. Matching is on the exact stored-file
basename, and only when a single unambiguous match exists, so an unrelated image
is never picked. Turn filename reuse off with the `auei_reuse_by_filename` filter
if you prefer always-fresh imports.

### How do I keep images from a trusted CDN or partner as external?

Add the domain to the whitelist with the `auei_whitelisted_domains` filter. Whitelisted
domains are never imported and their URLs are left untouched.

### Is it safe to run on a server behind a private network?

Yes – that is a core design goal. The built-in SSRF guard blocks requests to private,
reserved, loopback, link-local, ULA and CGNAT addresses, plus the cloud metadata
endpoint, and requires every resolved IP of a host to be public before any fetch
happens.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“External Image Importer – Import Remote Images to the Media Library” is open source
software. The following people have contributed to this plugin.

Contributors

 *   [ NerdCow ](https://profiles.wordpress.org/nerdcow/)

[Translate “External Image Importer – Import Remote Images to the Media Library” into your language.](https://translate.wordpress.org/projects/wp-plugins/nerdcow-external-image-importer)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/nerdcow-external-image-importer/),
check out the [SVN repository](https://plugins.svn.wordpress.org/nerdcow-external-image-importer/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/nerdcow-external-image-importer/)
by [RSS](https://plugins.trac.wordpress.org/log/nerdcow-external-image-importer/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.0.1

 * Reuse existing Media Library images by exact filename – including images uploaded
   normally, not just ones this plugin imported – so copying a page whose images
   already exist on the destination no longer creates duplicate uploads. Matched
   on the exact stored-file basename, single unambiguous match only.

#### 1.0.0

 * Initial release.
 * Automatic import of externally-hosted images on post save (Block and Classic 
   editors).
 * Imports the full-size original (regenerating every thumbnail and size locally)
   even when the content references a scaled “-WxH” image, then rewrites the markup
   to the matching local size so the layout is unchanged.
 * Remaps the source-site attachment ID carried in Gutenberg image, cover and gallery
   blocks (`"id":N` / `wp-image-N`) to the new local attachment ID, so blocks never
   point at a stale or colliding ID on the destination.
 * SSRF-safe URL validation: blocks private, reserved, loopback, link-local, ULA,
   CGNAT, IPv6 6to4/NAT64 and cloud-metadata addresses; requires all resolved IPs
   to be public; pins the validated IP for the fetch and re-validates every redirect
   hop; locks scheme, port and credentials.
 * Real content-type verification after download; SVG and ICO excluded by default.
 * Download timeout and maximum file-size caps (filterable).
 * Attachment reuse by `_auei_source_url` meta and (optionally) by previously-imported
   filename to avoid re-downloading.
 * URL rewriting across `src`, `data-src`, `data-lazy-src`, `srcset`, Gutenberg 
   block JSON and CSS `background-image`.
 * `wp auei localise` WP-CLI command with `--dry-run`, `--post_type`, `--post_id`
   and `--limit` options for retroactive fixes.
 * Comprehensive `auei_*` filter set for whitelisting, allowed types, schemes and
   ports, caps and overrides.

## Meta

 *  Version **1.0.1**
 *  Last updated **8 hours ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 6.0 or higher **
 *  Tested up to **7.0.1**
 *  PHP version ** 7.4 or higher **
 * Tags
 * [external images](https://wordpress.org/plugins/tags/external-images/)[import images](https://wordpress.org/plugins/tags/import-images/)
   [media library](https://wordpress.org/plugins/tags/media-library/)[migration](https://wordpress.org/plugins/tags/migration/)
   [Remote images](https://wordpress.org/plugins/tags/remote-images/)
 *  [Advanced View](https://wordpress.org/plugins/nerdcow-external-image-importer/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/nerdcow-external-image-importer/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/nerdcow-external-image-importer/reviews/)

## Contributors

 *   [ NerdCow ](https://profiles.wordpress.org/nerdcow/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/nerdcow-external-image-importer/)