# Replace a Media File and Keep Its URL

> Adds a "Replace file" box to the media edit screen that uploads a new version of an image, PDF or other file over the old one, keeping the same address so every link and embed updates at once.

- Type: PHP
- Runs: Admin only
- Category: [Media](https://wpide.com/library/category/media/)
- Tags: media-library, replace, uploads
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/media-replace-file/

## Code

```php
add_action( 'add_meta_boxes_attachment', function ( $post ) {
    if ( ! current_user_can( 'edit_post', $post->ID ) ) {
        return;
    }
    add_meta_box( 'wpide-replace-file', 'Replace file', function ( $post ) {
        wp_nonce_field( 'wpide_replace_' . $post->ID, 'wpide_replace_nonce' );
        printf(
            '<p><label for="wpide-replace-input">Upload a new %s file to replace this one:</label></p><p><input type="file" id="wpide-replace-input" name="wpide_replace_file" accept="%s"></p><p class="description">Then click Update. The URL stays the same.</p>',
            esc_html( strtoupper( pathinfo( (string) get_attached_file( $post->ID ), PATHINFO_EXTENSION ) ) ),
            esc_attr( $post->post_mime_type )
        );
    }, 'attachment', 'side' );
} );

// The attachment form needs multipart encoding for file uploads.
add_action( 'post_edit_form_tag', function ( $post ) {
    if ( 'attachment' === $post->post_type ) {
        echo ' enctype="multipart/form-data"';
    }
} );

add_action( 'edit_attachment', function ( $attachment_id ) {
    if ( empty( $_FILES['wpide_replace_file']['tmp_name'] ) || ! isset( $_POST['wpide_replace_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['wpide_replace_nonce'] ), 'wpide_replace_' . $attachment_id ) || ! current_user_can( 'edit_post', $attachment_id ) ) {
        return;
    }

    $upload = $_FILES['wpide_replace_file']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
    $old    = get_attached_file( $attachment_id );
    $check  = wp_check_filetype_and_ext( $upload['tmp_name'], $upload['name'] );

    if ( UPLOAD_ERR_OK !== (int) $upload['error'] || ! $check['type'] || $check['type'] !== get_post_mime_type( $attachment_id ) || ! is_uploaded_file( $upload['tmp_name'] ) ) {
        set_transient( 'wpide_replace_error_' . get_current_user_id(), 'The new file must be a valid ' . get_post_mime_type( $attachment_id ) . ' file.', 60 );
        return;
    }

    // Remove the old intermediate sizes before writing the new original.
    $meta = wp_get_attachment_metadata( $attachment_id );
    if ( ! empty( $meta['sizes'] ) ) {
        foreach ( $meta['sizes'] as $size ) {
            wp_delete_file( path_join( dirname( $old ), $size['file'] ) );
        }
    }
    if ( ! empty( $meta['original_image'] ) ) {
        $old = path_join( dirname( $old ), $meta['original_image'] );
        update_attached_file( $attachment_id, $old );
    }

    if ( move_uploaded_file( $upload['tmp_name'], $old ) ) {
        require_once ABSPATH . 'wp-admin/includes/image.php';
        wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $old ) );
        clean_post_cache( $attachment_id );
    }
} );

add_action( 'admin_notices', function () {
    $key = 'wpide_replace_error_' . get_current_user_id();
    if ( $message = get_transient( $key ) ) {
        delete_transient( $key );
        printf( '<div class="notice notice-error"><p>%s</p></div>', esc_html( $message ) );
    }
} );
```

## Notes

The new file must be the same type (for example JPEG for JPEG); image sizes are regenerated automatically. Browsers and CDNs may show the old file until their cache expires.

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Replace a Media File and Keep Its URL" in its snippet library and add it in one click; or create a new PHP snippet in WPIDE (or any code snippets plugin) and paste the code above. More snippets: https://wpide.com/library/
